Merge "ASoC: wcd9xxx: Report lineout immediately"
diff --git a/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt b/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt
index 9af81da..0d3d3f7 100644
--- a/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt
+++ b/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt
@@ -140,7 +140,15 @@
1 => equal to PVS corner ceiling voltage
2 => equal to slow speed corner ceiling
3 => equal to qcom,vdd-mx-vmax
+ 4 => equal to VDD_APC corner mapped vdd-mx voltage
This is required when vdd-mx-supply is present.
+- qcom,vdd-mx-corner-map: Array of 3 elements which defines the mapping from VDD_APC
+ fuse voltage corners to vdd-mx-supply voltages.
+ The 3 elements with index[0..2] are:
+ [0] => voltage to set for vdd-mx when VDD_APC is running at SVS corner
+ [1] => voltage to set for vdd-mx when VDD_APC is running at NOM corner
+ [2] => voltage to set for vdd-mx when VDD_APC is running at TURBO corner
+ This is required when the qcom,vdd-mx-vmin-method property has a value of 4.
- qcom,cpr-fuse-redun-bp-cpr-disable: Redundant bit position of the bit to indicate if CPR should be disable
- qcom,cpr-fuse-redun-bp-scheme: Redundant bit position of the bit to indicate if it's a global/local scheme
This property is required if cpr-fuse-redun-bp-cpr-disable
@@ -213,10 +221,18 @@
the virtual corner value. For example, the first element in the list is the fuse corner
value that virtual corner 1 maps to.
This is required if qcom,cpr-quot-adjust-table is present.
-- qcom,cpr-quotient-adjustment: Present: CPR adjusts quotient value. The
- adjustment equals to the quotient adjustment
- in millivolts multiply the KV value.
- Not Present: CPR will not adjust quotient value.
+- qcom,cpr-quotient-adjustment: Array of three elements of CPR quotient adjustments for each corner.
+ The 3 quotient adjustments with index[0..2] are:
+ [0] => amount to add to the SVS quotient
+ [1] => amount to add to the NORM quotient
+ [2] => amount to add to the TURBO quotient
+ If this property is specified, then the quotient adjustment values are added to the target
+ quotient values read from fuses before writing them into the CPR GCNT target control registers.
+ This property can be used to add static margin to the voltage rail managed by the CPR controller.
+- vdd-apc-optional-prim-supply: Present: Regulator of highest priority to supply VDD APC power
+ Not Present: No such regulator.
+- vdd-apc-optional-sec-supply: Present: Regulator of second highest priority to supply VDD APC power.
+ Not Present: No such regulator.
Example:
apc_vreg_corner: regulator@f9018000 {
@@ -245,6 +261,8 @@
qcom,pvs-corner-ceiling-nom = <975000 1075000 1200000>;
qcom,pvs-corner-ceiling-fast = <900000 1000000 1140000>;
vdd-apc-supply = <&pm8226_s2>;
+ vdd-apc-optional-prim-supply = <&ncp6335d>;
+ vdd-apc-optional-sec-supply = <&fan53555>;
vdd-mx-supply = <&pm8226_l3_ao>;
qcom,vdd-mx-vmax = <1350000>;
qcom,vdd-mx-vmin-method = <1>;
diff --git a/Documentation/devicetree/bindings/arm/msm/rpm-regulator-smd.txt b/Documentation/devicetree/bindings/arm/msm/rpm-regulator-smd.txt
index 3d70a9b..2b25fcb 100644
--- a/Documentation/devicetree/bindings/arm/msm/rpm-regulator-smd.txt
+++ b/Documentation/devicetree/bindings/arm/msm/rpm-regulator-smd.txt
@@ -32,6 +32,10 @@
- qcom,enable-time: Time in us to delay after enabling the regulator
- qcom,hpm-min-load: Load current in uA which corresponds to the minimum load
which requires the regulator to be in high power mode.
+- qcom,apps-only: Flag which indicates that the regulator only has
+ consumers on the application processor. If this flag
+ is specified, then voltage and current updates are
+ only sent to the RPM if the regulator is enabled.
[Second Level Nodes]
@@ -84,6 +88,7 @@
current updates are only sent if the given
regulator has also been enabled by a Linux
consumer.
+
The following properties specify initial values for parameters to be sent to the
RPM in regulator requests.
- qcom,init-enable: 0 = regulator disabled
diff --git a/Documentation/devicetree/bindings/crypto/msm/qcrypto.txt b/Documentation/devicetree/bindings/crypto/msm/qcrypto.txt
index 2ae9821..1bb4659 100644
--- a/Documentation/devicetree/bindings/crypto/msm/qcrypto.txt
+++ b/Documentation/devicetree/bindings/crypto/msm/qcrypto.txt
@@ -22,6 +22,7 @@
- qcom,use-sw-ahash-algo : optional, indicates if use SW hash algorithm.
- qcom,use-sw-hmac-algo : optional, indicates if use SW hmac algorithm.
- qcom,use-sw-aes-ccm-algo : optional, indicates if use SW aes-ccm algorithm.
+ - qcom,clk-mgmt-sus-res : optional, indicate if the ce clocks need to be disabled/enabled in suspend/resume function.
Example:
diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
index 113ded8..ae6f8ef 100644
--- a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
@@ -126,6 +126,9 @@
implemented during suspend/resume.
"dfps_immediate_clk_mode" = FPS change request is
implemented immediately using DSI clocks.
+ "dfps_immediate_porch_mode" = FPS change request is
+ implemented immediately by changing panel porch
+ values.
- qcom,mdss-dsi-bl-pmic-control-type: A string that specifies the implementation of backlight
control for this panel.
"bl_ctrl_pwm" = Backlight controlled by PWM gpio.
@@ -156,15 +159,9 @@
1 = TE through TE gpio pin. (default)
- qcom,mdss-dsi-te-dcs-command: Inserts the dcs command.
1 = default value.
-- qcom,mdss-dsi-te-v-sync-rd-ptr-irq-line: Configures the scan line number that the dsi
- pixel transfer will start on. Rasing this number
- will result in delaying the start of the pixel
- transfer.
+- qcom,mdss-dsi-wr-mem-start: DCS command for write_memory_start.
0x2c = default value.
-- qcom,mdss-dsi-te-v-sync-continue-lines: Represents the difference in number of lines
- between estimated read pointer and write pointer
- to allow the updating of all the lines except
- the first line of the frame.
+- qcom,mdss-dsi-wr-mem-continue: DCS command for write_memory_continue.
0x3c = default value.
- qcom,mdss-dsi-h-sync-pulse: Specifies the pulse mode option for the panel.
0 = Don't send hsa/he following vs/ve packet(default)
@@ -307,8 +304,8 @@
qcom,mdss-dsi-te-check-enable;
qcom,mdss-dsi-te-using-te-pin;
qcom,mdss-dsi-te-dcs-command = <1>;
- qcom,mdss-dsi-te-v-sync-continue-lines = <0x3c>;
- qcom,mdss-dsi-te-v-sync-rd-ptr-irq-line = <0x2c>;
+ qcom,mdss-dsi-wr-mem-continue = <0x3c>;
+ qcom,mdss-dsi-wr-mem-start = <0x2c>;
qcom,mdss-dsi-te-pin-select = <1>;
qcom,mdss-dsi-h-sync-pulse = <1>;
qcom,mdss-dsi-hfp-power-mode;
diff --git a/Documentation/devicetree/bindings/fb/mdss-mdp.txt b/Documentation/devicetree/bindings/fb/mdss-mdp.txt
index f850659..c2b963f 100644
--- a/Documentation/devicetree/bindings/fb/mdss-mdp.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-mdp.txt
@@ -117,6 +117,79 @@
- "edp"
- "hdmi"
+Bus Scaling Data:
+- qcom,msm-bus,name: String property describing MDSS client.
+- qcom,msm-bus,num-cases: This is the the number of Bus Scaling use cases
+ defined in the vectors property. This must be
+ set to <3> for MDSS driver where use-case 0 is
+ used to take off MDSS BW votes from the system.
+ And use-case 1 & 2 are used in ping-pong fashion
+ to generate run-time BW requests.
+- qcom,msm-bus,active-only: A boolean flag indicating if it is active only.
+- qcom,msm-bus,num-paths: This represents the number of paths in each
+ Bus Scaling Usecase. This value depends on
+ how many number of AXI master ports are
+ dedicated to MDSS for particular chipset.
+- qcom,msm-bus,vectors-KBps: * A series of 4 cell properties, with a format
+ of (src, dst, ab, ib) which is defined at
+ Documentation/devicetree/bindings/arm/msm/msm_bus.txt
+ * Current values of src & dst are defined at
+ arch/arm/mach-msm/msm_bus_board.h
+ src values allowed for MDSS are:
+ 22 = MSM_BUS_MASTER_MDP_PORT0
+ 23 = MSM_BUS_MASTER_MDP_PORT1
+ dst values allowed for MDSS are:
+ 512 = MSM_BUS_SLAVE_EBI_CH0
+ ab: Represents aggregated bandwidth.
+ ib: Represents instantaneous bandwidth.
+ * Total number of 4 cell properties will be
+ (number of use-cases * number of paths).
+ * These values will be overridden by the driver
+ based on the run-time requirements. So initial
+ ab and ib values defined here are random and
+ bare no logic except for the use-case 0 where ab
+ and ib values needs to be 0.
+
+- qcom,mdss-prefill-outstanding-buffer-bytes: The size of mdp outstanding buffer
+ in bytes. The buffer is filled during prefill
+ time and the buffer size shall be included in
+ prefill bandwidth calculation.
+- qcom,mdss-prefill-y-buffer-bytes: The size of mdp y plane buffer in bytes. The
+ buffer is filled during prefill time when format
+ is YUV and the buffer size shall be included in
+ prefill bandwidth calculation.
+- qcom,mdss-prefill-scaler-buffer-lines-bilinear: The value indicates how many lines
+ of scaler line buffer need to be filled during
+ prefill time. If bilinear scalar is enabled, then this
+ number of lines is used to determine how many bytes
+ of scaler buffer to be included in prefill bandwidth
+ calculation.
+- qcom,mdss-prefill-scaler-buffer-lines-caf: The value indicates how many lines of
+ of scaler line buffer need to be filled during
+ prefill time. If CAF mode filter is enabled, then
+ this number of lines is used to determine how many
+ bytes of scaler buffer to be included in prefill
+ bandwidth calculation.
+- qcom,mdss-prefill-post-scaler-buffer: The size of post scaler buffer in bytes.
+ The buffer is used to smooth the output of the
+ scaler. If the buffer is present in h/w, it is
+ filled during prefill time and the number of bytes
+ shall be included in prefill bandwidth calculation.
+- qcom,mdss-prefill-pingpong-buffer-pixels: The size of pingpong buffer in pixels.
+ The buffer is used to keep pixels flowing to the
+ panel interface. If the vertical start position of a
+ layer is in the beginning of the active area, pingpong
+ buffer must be filled during prefill time to generate
+ starting lines. The number of bytes to be filled is
+ determined by the line width, starting position,
+ byte per pixel and scaling ratio, this number shall be
+ included in prefill bandwidth calculation.
+- qcom,mdss-prefill-fbc-lines: The value indicates how many lines are required to fill
+ fbc buffer during prefill time if FBC (Frame Buffer
+ Compressor) is enabled. The number of bytes to be filled
+ is determined by the line width, bytes per pixel and
+ scaling ratio, this number shall be included in prefill bandwidth
+ calculation.
Optional properties:
- vdd-cx-supply : Phandle for vdd CX regulator device node.
- batfet-supply : Phandle for battery FET regulator device node.
@@ -170,6 +243,33 @@
applied in scenarios where panel interface can
be more tolerant to memory latency such as
command mode panels.
+- qcom,mdss-rotator-ot-limit: This integer value indicates maximum number of pending
+ writes that can be allowed from rotator client. Default
+ value is 16 which is the maximum. This value can be
+ used to reduce the pending writes limit dynamically
+ and can be tuned to match performance requirements
+ depending upon system state.
+
+Fudge Factors: Fudge factors are used to boost demand for
+ resources like bus bandswidth, clk rate etc. to
+ overcome system inefficiencies and avoid any
+ glitches. These fudge factors are expressed in
+ terms of numerator and denominator. First value
+ is numerator followed by denominator. They all
+ are optional but highly recommended.
+ Ex:
+ x = value to be fudged
+ a = numerator, default value is 1
+ b = denominator, default value is 1
+ 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-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
+ linear format. The value tells the GPU highest memory
+ bank bit used.
+
Optional subnodes:
Child nodes representing the frame buffer virtual devices.
@@ -219,6 +319,20 @@
qcom,max-bandwidth-low-kbps = <2300000>;
qcom,max-bandwidth-high-kbps = <3000000>;
+ /* Bus Scale Settings */
+ qcom,msm-bus,name = "mdss_mdp";
+ qcom,msm-bus,num-cases = <3>;
+ qcom,msm-bus,num-paths = <2>;
+ qcom,msm-bus,vectors-KBps =
+ <22 512 0 0>, <23 512 0 0>,
+ <22 512 0 6400000>, <23 512 0 6400000>,
+ <22 512 0 6400000>, <23 512 0 6400000>;
+
+ /* Fudge factors */
+ qcom,mdss-ab-factor = <2 1>; /* 2 times */
+ qcom,mdss-ib-factor = <3 2>; /* 1.5 times */
+ qcom,mdss-clk-factor = <5 4>; /* 1.25 times */
+
qcom,max-clk-rate = <320000000>;
qcom,vbif-settings = <0x0004 0x00000001>,
<0x00D8 0x00000707>;
@@ -243,6 +357,7 @@
qcom,mdss-smp-data = <22 4096>;
qcom,mdss-rot-block-size = <64>;
+ qcom,mdss-rotator-ot-limit = <2>;
qcom,mdss-smp-mb-per-pipe = <2>;
qcom,mdss-pref-prim-intf = "dsi";
qcom,mdss-has-bwc;
@@ -261,6 +376,15 @@
qcom,mdss-intf-off = <0x00021100 0x00021300
0x00021500 0x00021700>;
+ /* buffer parameters to calculate prefill bandwidth */
+ qcom,mdss-prefill-outstanding-buffer-bytes = <1024>;
+ qcom,mdss-prefill-y-buffer-bytes = <4096>;
+ qcom,mdss-prefill-scaler-buffer-lines-bilinear = <2>;
+ qcom,mdss-prefill-scaler-buffer-lines-caf = <4>;
+ qcom,mdss-prefill-post-scaler-buffer-pixels = <2048>;
+ qcom,mdss-prefill-pingpong-buffer-pixels = <5120>;
+ qcom,mdss-prefill-fbc-lines = <2>;
+
mdss_fb0: qcom,mdss_fb_primary {
cell-index = <0>;
compatible = "qcom,mdss-fb";
diff --git a/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt b/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt
index 31bf540..d0cad52 100644
--- a/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt
+++ b/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt
@@ -9,7 +9,12 @@
Required properties:
- compatible : should be "qcom,qpnp-iadc" for Current ADC driver.
-- reg : offset and length of the PMIC Aribter register map.
+- reg : offset and length of the PMIC Arbiter register map.
+- reg-names : resource names used for the physical base address of the PMIC IADC
+ peripheral, the SMBB_BAT_IF_TRIM_CNST_RDS register.
+ Should be "iadc-base" for the PMIC IADC peripheral base register.
+ Should be "batt-id-trim-cnst-rds" for reading the
+ SMBB_BAT_IF_TRIM_CNST_RDS register.
- address-cells : Must be one.
- size-cells : Must be zero.
- interrupts : The USR bank peripheral IADC interrupt.
@@ -21,6 +26,13 @@
- qcom,rsense : Use this property when external rsense should be used
for current calculation and specify the units in nano-ohms.
- qcom,iadc-poll-eoc: Use polling instead of interrupts for End of Conversion completion.
+- 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
+ apply the default RSENSE if conditions are met.
+ 1 : Select this type to read the IADC, SMBB trim register and
+ manufacturer type and apply the default RSENSE if conditions are met.
Channel node
NOTE: Atleast one Channel node is required.
diff --git a/Documentation/devicetree/bindings/input/misc/hbtp_input.txt b/Documentation/devicetree/bindings/input/misc/hbtp_input.txt
new file mode 100644
index 0000000..54643c7
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/misc/hbtp_input.txt
@@ -0,0 +1,14 @@
+Platform device for Host Based Touch Processing (hbtp)
+
+Required properties:
+
+ - compatible : should be "qcom,hbtp"
+ - vcc_ana-supply : Analog power supply needed to power device
+
+Example:
+ &soc {
+ hbtp {
+ compatible = "qcom,hbtp";
+ vcc_ana-supply = <&pm8941_l18>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt b/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt
index f2ca95b..d0c2b7d 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt
@@ -29,7 +29,7 @@
It is a four tuple consisting of min x,
min y, max x and max y values.
- goodix,i2c-pull-up : To specify pull up is required.
- - goodix,no-force-update : To specify force update is allowed.
+ - goodix,force-update : To specify force update is allowed.
- goodix,enable-power-off : Power off touchscreen during suspend.
- goodix,button-map : Button map of key codes. The number of key codes
depend on panel.
diff --git a/Documentation/devicetree/bindings/nfc/nfc-nci.txt b/Documentation/devicetree/bindings/nfc/nfc-nci.txt
index dca7d5c..2c06599 100644
--- a/Documentation/devicetree/bindings/nfc/nfc-nci.txt
+++ b/Documentation/devicetree/bindings/nfc/nfc-nci.txt
@@ -8,13 +8,15 @@
- reg: NCI i2c slave address.
- qcom,dis-gpio: specific gpio for hardware reset.
- qcom,irq-gpio: specific gpio for read interrupt.
-- qcom,clk-src: nfc clock source ("BBCLK2", "RFCLK3", "GPCLK","GPCLK2" ...)
-- qcom,clk-en-gpio: msm gpio clock,used ony if clock source is msm gpio
+- qcom,clk-src: nfc clock source ("BBCLK2", "RFCLK3", "GPCLK", "GPCLK2", ...)
+- qcom,clk-src-gpio: msm gpio clock,used ony if clock source is msm gpio
+- qcom,clk-req-gpio: clk-req input gpio for MSM based clocks.
+ not used for pmic implementation
- vlogic-supply: LDO for power supply
- interrupt-parent: Should be phandle for the interrupt controller
that services interrupts for this device.
-- interrupts: should contain the NFC interrupt. NFC has one read interrupt.
-- qcom,clk-gpio: pmic gpio on which bbclk2 signal is coming.
+- interrupts: Nfc read interrupt,gpio-clk-req interrupt
+- qcom,clk-gpio: pmic or msm gpio on which bbclk2 signal is coming.
LDO example:
@@ -24,8 +26,8 @@
reg = <0x0e>;
qcom,irq-gpio = <&msmgpio 77 0x00>;
qcom,dis-gpio = <&msmgpio 93 0x00>;
- qcom,clk-en-gpio = <&msmgpio 78 0x00>;
- qcom,clk-src = "GPCLK";
+ qcom,clk-src-gpio = <&msmgpio 78 0x00>;
+ qcom,clk-src = "GPCLK2";
interrupt-parent = <&msmgpio>;
interrupts = <77 0>;
qcom,clk-gpio = <&msmgpio 75 0x00>;
diff --git a/Documentation/devicetree/bindings/regulator/fan53555.txt b/Documentation/devicetree/bindings/regulator/fan53555.txt
new file mode 100644
index 0000000..57b54d6
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/fan53555.txt
@@ -0,0 +1,50 @@
+Fairchild Semiconductor FAN53555 regulator
+
+The FAN53555 is a step-down switching voltage regulator that delivers a
+digitally programmable output from an input voltage supply of 2.5 V to 5.5 V.
+The output voltage is programmed through an I2C interface capable of operating
+up to 3.4 MHz.
+
+The fan53555 interface is via I2C bus.
+
+Required Properties:
+- compatible: Must be "fairchild,fan53555-regulator".
+- reg: The device 8-bit I2C address.
+- fairchild,backup-vsel: Register ID of backup register.
+ Supported values are 0 or 1.
+ The voltage selection ID used while the system
+ is active will be the other option not used
+ during running.
+- regulator-min-microvolt: Minimum voltage in microvolts supported by this
+ regulator.
+- regulator-max-microvolt: Maximum voltage in microvolts supported by this
+ regulator.
+- regulator-ramp-delay: The slew rate of the regulator, in uV/us.
+ Supported values are 64000, 32000, 16000,
+ 8000, 4000, 2000, 1000 and 500.
+
+Optional Properties:
+- fairchild,vsel-gpio: Present: GPIO connects to the VSEL pin and set
+ the output.
+ Not Present: No GPIO is connected to vsel pin.
+- fairchild,restore-reg: Present: Restore vsel register from backup
+ register.
+ Not Present: No restore.
+- fairchild,disable-suspend: Present: Disable regulator suspend method.
+ Not Present: Do not disable regulator suspend
+ method.
+
+Example:
+ i2c_0 {
+ fan53555-regulator@60 {
+ compatible = "fairchild,fan53555-regulator";
+ reg = <0x60>;
+ fairchild,backup-vsel = <1>;
+ regulator-min-microvolt = <1050000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-ramp-delay = <8000>;
+ fairchild,vsel-gpio = <&msmgpio 2 1>;
+ fairchild,restore-reg;
+ fairchild,disable-suspend;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/regulator/onsemi-ncp6335d.txt b/Documentation/devicetree/bindings/regulator/onsemi-ncp6335d.txt
new file mode 100644
index 0000000..648cb11
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/onsemi-ncp6335d.txt
@@ -0,0 +1,71 @@
+ON Semiconductor NCP6335d regulator
+
+NCP6335d is able to deliver up to 5.0 A, with programmable output voltage from
+0.6 V to 1.87 V in 10mV steps, with synchronous rectification and automatic PWM/
+PFM transitions, enable pins and power good/fail signaling.
+
+The NCP6335d interface is via I2C bus.
+
+Required Properties:
+- compatible: Must be "onnn,ncp6335d-regulator".
+- reg: The device 8-bit I2C address.
+- regulator-min-microvolt: Minimum voltage in microvolts supported by this
+ regulator.
+- regulator-max-microvolt: Maximum voltage in microvolts supported by this
+ regulator.
+- onnn,min-setpoint: Minimum setpoint voltage in microvolts supported
+ by this regulator.
+- onnn,step-size: The step size of the regulator, in uV.
+- onnn,min-slew-ns: Minimum time in ns needed to change voltage by
+ one step size. This value corresponds to DVS
+ mode bit of 00b in command register.
+- onnn,max-slew-ns: Maximum time in ns needed to change voltage by
+ one step size. This value corresponds to DVS
+ mode bit of 11b in command register.
+- onnn,vsel: Working vsel register. Supported value are 0
+ or 1.
+- onnn,slew-ns: Time in ns needed to change voltage by one step
+ size. Supported value are 333, 666, 1333, 2666.
+
+Optional Properties:
+- onnn,discharge-enable: Present: discharge enabled.
+ Not Present: discharge disabled.
+- onnn,restore-reg: Present: Restore vsel register from backup register.
+ Not Present: No restore.
+- onnn,vsel-gpio: Present: GPIO connects to the VSEL pin and set the
+ VSEL pin according to device tree flag.
+ Not Present: No GPIO is connected to vsel pin.
+- onnn,sleep-enable: Present: Forced in sleep mode when EN and VSEL
+ pins are low.
+ Not Present: Low quiescent current mode when EN and VSEL
+ pins are low.
+- onnn,tlmm-config: Array of 2 elements to indicate the mask value and the value
+ to write in the masked bits.
+ This property is used to allow the VSEL GPIO to toggle on certain
+ Qualcomm processors. If it is not specified, then no register write is required.
+ The 2 elements with index[0..1] are:
+ [0] => the mask value;
+ [1] => the value to write into the masked bits.
+- onnn,mode: A string which specifies the initial mode to use for the regulator.
+ Supported values are "pwm" and "auto". PWM mode is more
+ robust, but draws more current than auto mode. If this propery
+ is not specified, then the regulator will be in the hardware default mode.
+
+Example:
+ i2c_0 {
+ ncp6335d-regulator@1c {
+ compatible = "onnn,ncp6335d-regulator";
+ reg = <0x1c>;
+ onnn,vsel = <0>;
+ onnn,slew-rate-ns = <2666>;
+ onnn,discharge-enable;
+ onnn,step-size = <10000>;
+ onnn,min-slew-ns = <333>;
+ onnn,max-slew-ns = <2666>;
+
+ regulator-min-microvolt = <1050000>;
+ regulator-max-microvolt = <1350000>;
+ onnn,min-setpoint = <600000>;
+ onnn,vsel-gpio = <&msmgpio 2 1>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 35e7324..fd826d9 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -17,6 +17,7 @@
denx Denx Software Engineering
epson Seiko Epson Corp.
est ESTeem Wireless Modems
+fairchild Fairchild Semiconductor International, Inc.
focaltech Focaltech systems
fsl Freescale Semiconductor
GEFanuc GE Fanuc Intelligent Platforms Embedded Systems, Inc.
diff --git a/arch/arm/boot/dts/apq8084-mdss.dtsi b/arch/arm/boot/dts/apq8084-mdss.dtsi
index 0e771ce..329ab32 100644
--- a/arch/arm/boot/dts/apq8084-mdss.dtsi
+++ b/arch/arm/boot/dts/apq8084-mdss.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
@@ -24,6 +24,20 @@
qcom,max-bandwidth-low-kbps = <6000000>;
qcom,max-bandwidth-high-kbps = <6000000>;
+ /* Bus Scale Settings */
+ qcom,msm-bus,name = "mdss_mdp";
+ qcom,msm-bus,num-cases = <3>;
+ qcom,msm-bus,num-paths = <2>;
+ qcom,msm-bus,vectors-KBps =
+ <22 512 0 0>, <23 512 0 0>,
+ <22 512 0 6400000>, <23 512 0 6400000>,
+ <22 512 0 6400000>, <23 512 0 6400000>;
+
+ /* Fudge factors */
+ qcom,mdss-ab-factor = <2 1>; /* 2 times */
+ qcom,mdss-ib-factor = <6 5>; /* 1.2 times */
+ qcom,mdss-clk-factor = <105 100>; /* 1.05 times */
+
qcom,max-clk-rate = <320000000>;
qcom,mdss-pipe-vig-off = <0x00001200 0x00001600
@@ -60,6 +74,15 @@
qcom,mdss-has-bwc;
qcom,mdss-has-decimation;
+ /* buffer parameters to calculate prefill bandwidth */
+ qcom,mdss-prefill-outstanding-buffer-bytes = <1024>;
+ qcom,mdss-prefill-y-buffer-bytes = <4096>;
+ qcom,mdss-prefill-scaler-buffer-lines-bilinear = <2>;
+ qcom,mdss-prefill-scaler-buffer-lines-caf = <4>;
+ qcom,mdss-prefill-post-scaler-buffer-pixels = <2048>;
+ qcom,mdss-prefill-pingpong-buffer-pixels = <5120>;
+ qcom,mdss-prefill-fbc-lines = <2>;
+
mdss_fb0: qcom,mdss_fb_primary {
cell-index = <0>;
compatible = "qcom,mdss-fb";
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 17e6e94..14d4c35 100644
--- a/arch/arm/boot/dts/dsi-panel-generic-720p-cmd.dtsi
+++ b/arch/arm/boot/dts/dsi-panel-generic-720p-cmd.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
@@ -59,8 +59,8 @@
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-v-sync-continues-lines = <0x3c>;
+ qcom,mdss-dsi-wr-mem-start = <0x2c>;
+ qcom,mdss-dsi-wr-mem-continue = <0x3c>;
qcom,mdss-dsi-te-dcs-command = <1>;
qcom,mdss-dsi-te-check-enable;
qcom,mdss-dsi-te-using-te-pin;
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 be42509..917c637 100644
--- a/arch/arm/boot/dts/dsi-panel-jdi-1080p-video.dtsi
+++ b/arch/arm/boot/dts/dsi-panel-jdi-1080p-video.dtsi
@@ -66,5 +66,7 @@
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>;
+ qcom,mdss-pan-physical-width-dimension = <61>;
+ qcom,mdss-pan-physical-height-dimension = <110>;
};
};
diff --git a/arch/arm/boot/dts/dsi-panel-nt35590-720p-cmd.dtsi b/arch/arm/boot/dts/dsi-panel-nt35590-720p-cmd.dtsi
index d3547d8..cbc38f2 100644
--- a/arch/arm/boot/dts/dsi-panel-nt35590-720p-cmd.dtsi
+++ b/arch/arm/boot/dts/dsi-panel-nt35590-720p-cmd.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
@@ -517,7 +517,7 @@
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-wr-mem-start = <0x2c>;
qcom,mdss-dsi-te-dcs-command = <1>;
qcom,mdss-dsi-te-check-enable;
qcom,mdss-dsi-te-using-te-pin;
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 4cd1563..3106cd4 100644
--- a/arch/arm/boot/dts/dsi-panel-truly-wvga-cmd.dtsi
+++ b/arch/arm/boot/dts/dsi-panel-truly-wvga-cmd.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
@@ -151,8 +151,8 @@
qcom,mdss-dsi-lane-0-state;
qcom,mdss-dsi-lane-1-state;
qcom,mdss-dsi-te-pin-select = <1>;
- qcom,mdss-dsi-te-v-sync-rd-ptr-irq-line = <0x2c>;
- qcom,mdss-dsi-te-v-sync-continues-lines = <0x3c>;
+ qcom,mdss-dsi-wr-mem-start = <0x2c>;
+ qcom,mdss-dsi-wr-mem-continue = <0x3c>;
qcom,mdss-dsi-te-dcs-command = <1>;
qcom,mdss-dsi-te-check-enable;
qcom,mdss-dsi-te-using-te-pin;
diff --git a/arch/arm/boot/dts/msm-pm8110.dtsi b/arch/arm/boot/dts/msm-pm8110.dtsi
index 20e8a96..9adbf81 100644
--- a/arch/arm/boot/dts/msm-pm8110.dtsi
+++ b/arch/arm/boot/dts/msm-pm8110.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
@@ -256,7 +256,9 @@
pm8110_iadc: iadc@3600 {
compatible = "qcom,qpnp-iadc";
- reg = <0x3600 0x100>;
+ reg = <0x3600 0x100>,
+ <0x12f1 0x1>;
+ reg-names = "iadc-base", "batt-id-trim-cnst-rds";
#address-cells = <1>;
#size-cells = <0>;
interrupts = <0x0 0x36 0x0>;
@@ -265,6 +267,7 @@
qcom,adc-vdd-reference = <1800>;
qcom,iadc-vadc = <&pm8110_vadc>;
qcom,iadc-poll-eoc;
+ qcom,use-default-rds-trim = <1>;
chan@0 {
label = "internal_rsense";
diff --git a/arch/arm/boot/dts/msm-pm8226.dtsi b/arch/arm/boot/dts/msm-pm8226.dtsi
index a13e66a..0558ee5 100644
--- a/arch/arm/boot/dts/msm-pm8226.dtsi
+++ b/arch/arm/boot/dts/msm-pm8226.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
@@ -414,7 +414,9 @@
pm8226_iadc: iadc@3600 {
compatible = "qcom,qpnp-iadc";
- reg = <0x3600 0x100>;
+ reg = <0x3600 0x100>,
+ <0x12f1 0x1>;
+ reg-names = "iadc-base", "batt-id-trim-cnst-rds";
#address-cells = <1>;
#size-cells = <0>;
interrupts = <0x0 0x36 0x0>;
@@ -422,6 +424,7 @@
qcom,adc-bit-resolution = <16>;
qcom,adc-vdd-reference = <1800>;
qcom,iadc-vadc = <&pm8226_vadc>;
+ qcom,use-default-rds-trim = <0>;
chan@0 {
label = "internal_rsense";
diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index 520decd..e678388 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.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
@@ -812,7 +812,9 @@
pm8941_iadc: iadc@3600 {
compatible = "qcom,qpnp-iadc";
- reg = <0x3600 0x100>;
+ reg = <0x3600 0x100>,
+ <0x12f1 0x1>;
+ reg-names = "iadc-base", "batt-id-trim-cnst-rds";
#address-cells = <1>;
#size-cells = <0>;
interrupts = <0x0 0x36 0x0>;
@@ -821,6 +823,7 @@
qcom,adc-vdd-reference = <1800>;
qcom,iadc-vadc = <&pm8941_vadc>;
qcom,iadc-poll-eoc;
+ qcom,use-default-rds-trim = <0>;
chan@0 {
label = "internal_rsense";
diff --git a/arch/arm/boot/dts/msm8226-mdss.dtsi b/arch/arm/boot/dts/msm8226-mdss.dtsi
index 84bb234..8a98d5c 100644
--- a/arch/arm/boot/dts/msm8226-mdss.dtsi
+++ b/arch/arm/boot/dts/msm8226-mdss.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
@@ -21,6 +21,21 @@
qcom,max-bandwidth-low-kbps = <1660000>;
qcom,max-bandwidth-high-kbps = <1660000>;
+
+ /* Bus Scale Settings */
+ qcom,msm-bus,name = "mdss_mdp";
+ qcom,msm-bus,num-cases = <3>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <22 512 0 0>,
+ <22 512 0 6400000>,
+ <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-clk-factor = <5 4>; /* 1.25 times */
+
qcom,max-clk-rate = <200000000>;
qcom,mdss-pipe-vig-off = <0x00001200>;
qcom,mdss-pipe-rgb-off = <0x00001E00>;
@@ -43,6 +58,7 @@
qcom,mdss-wb-off = <0x00011100 0x00013100>;
qcom,mdss-intf-off = <0x00000000 0x00021300>;
qcom,mdss-rot-block-size = <64>;
+ qcom,mdss-rotator-ot-limit = <2>;
qcom,mdss-smp-mb-per-pipe = <4>;
vdd-cx-supply = <&pm8226_s1_corner>;
@@ -52,6 +68,15 @@
qcom,mdp-settings = <0x02E0 0x000000A5>,
<0x02E4 0x00000055>;
+ /* buffer parameters to calculate prefill bandwidth */
+ qcom,mdss-prefill-outstanding-buffer-bytes = <1024>;
+ qcom,mdss-prefill-y-buffer-bytes = <0>;
+ qcom,mdss-prefill-scaler-buffer-lines-bilinear = <2>;
+ qcom,mdss-prefill-scaler-buffer-lines-caf = <4>;
+ qcom,mdss-prefill-post-scaler-buffer-pixels = <0>;
+ qcom,mdss-prefill-pingpong-buffer-pixels = <4096>;
+ qcom,mdss-prefill-fbc-lines = <0>;
+
mdss_fb0: qcom,mdss_fb_primary {
cell-index = <0>;
compatible = "qcom,mdss-fb";
diff --git a/arch/arm/boot/dts/msm8226-v2.dtsi b/arch/arm/boot/dts/msm8226-v2.dtsi
index 089d415..a285ec9 100644
--- a/arch/arm/boot/dts/msm8226-v2.dtsi
+++ b/arch/arm/boot/dts/msm8226-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
@@ -83,31 +83,11 @@
< 384000000 2>,
< 787200000 4>,
<1190400000 7>;
- qcom,speed6-bin-v2 =
- < 0 0>,
- < 384000000 2>,
- < 787200000 4>,
- <1190400000 7>;
qcom,speed2-bin-v2 =
< 0 0>,
< 384000000 2>,
< 787200000 4>,
<1401600000 10>;
- qcom,speed5-bin-v2 =
- < 0 0>,
- < 384000000 2>,
- < 787200000 4>,
- <1401600000 10>;
- qcom,speed4-bin-v2 =
- < 0 0>,
- < 384000000 2>,
- < 787200000 4>,
- <1401600000 10>;
- qcom,speed7-bin-v2 =
- < 0 0>,
- < 384000000 2>,
- < 787200000 4>,
- <1401600000 10>;
qcom,speed1-bin-v2 =
< 0 0>,
< 384000000 2>,
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
index e0304f5..148d99c 100644
--- a/arch/arm/boot/dts/msm8226.dtsi
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -977,7 +977,7 @@
qcom,scl-gpio = <&msmgpio 11 0>;
qcom,master-id = <86>;
};
- i2c@f9926000 { /* BLSP-1 QUP-4 */
+ i2c_0: i2c@f9926000 { /* BLSP-1 QUP-4 */
cell-index = <0>;
compatible = "qcom,i2c-qup";
reg = <0xf9926000 0x1000>;
@@ -987,6 +987,8 @@
interrupts = <0 98 0>;
interrupt-names = "qup_err_intr";
qcom,i2c-bus-freq = <100000>;
+ qcom,i2c-src-freq = <19200000>;
+ qcom,master-id = <86>;
};
i2c@f9927000 { /* BLSP1 QUP5 */
@@ -1303,15 +1305,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,qcrypto@fd404000 {
diff --git a/arch/arm/boot/dts/msm8610-gpu.dtsi b/arch/arm/boot/dts/msm8610-gpu.dtsi
index 7e3ee0d..de480df 100644
--- a/arch/arm/boot/dts/msm8610-gpu.dtsi
+++ b/arch/arm/boot/dts/msm8610-gpu.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
@@ -38,7 +38,7 @@
<26 512 0 0>,
<26 512 0 800000>,
<26 512 0 1600000>,
- <26 512 0 2128000>;
+ <26 512 0 2664000>;
/* GDSC oxili regulators */
vdd-supply = <&gdsc_oxili_cx>;
diff --git a/arch/arm/boot/dts/msm8610-mtp.dtsi b/arch/arm/boot/dts/msm8610-mtp.dtsi
index 7f48db0..aad838e 100644
--- a/arch/arm/boot/dts/msm8610-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8610-mtp.dtsi
@@ -151,10 +151,11 @@
reg = <0x0e>;
qcom,irq-gpio = <&msmgpio 77 0x00>;
qcom,dis-gpio = <&msmgpio 93 0x00>;
- qcom,clk-en-gpio = <&msmgpio 78 0x00>;
+ qcom,clk-req-gpio = <&msmgpio 75 0x00>;
+ qcom,clk-src-gpio = <&msmgpio 78 0x00>;
qcom,clk-src = "GPCLK";
interrupt-parent = <&msmgpio>;
- interrupts = <77 0>;
+ interrupts = <77 75 0>;
qcom,clk-gpio = <&pm8110_gpios 1 0>;
};
};
diff --git a/arch/arm/boot/dts/msm8610-qrd-skuaa.dtsi b/arch/arm/boot/dts/msm8610-qrd-skuaa.dtsi
index 719830e..53abb95 100644
--- a/arch/arm/boot/dts/msm8610-qrd-skuaa.dtsi
+++ b/arch/arm/boot/dts/msm8610-qrd-skuaa.dtsi
@@ -38,11 +38,12 @@
compatible = "qcom,nfc-nci";
reg = <0x0e>;
qcom,irq-gpio = <&msmgpio 77 0x00>;
+ qcom,clk-req-gpio = <&msmgpio 75 0x00>;
qcom,dis-gpio = <&msmgpio 93 0x00>;
- qcom,clk-en-gpio = <&msmgpio 78 0x00>;
+ qcom,clk-src-gpio = <&msmgpio 78 0x00>;
qcom,clk-src = "GPCLK2";
interrupt-parent = <&msmgpio>;
- interrupts = <77 0>;
+ interrupts = <77 75 0>;
qcom,clk-gpio = <&msmgpio 75 0x00>;
};
};
diff --git a/arch/arm/boot/dts/msm8610.dtsi b/arch/arm/boot/dts/msm8610.dtsi
index 6e10deb..61fa6bc 100644
--- a/arch/arm/boot/dts/msm8610.dtsi
+++ b/arch/arm/boot/dts/msm8610.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
@@ -1071,7 +1071,7 @@
label = "vchg_sns";
reg = <2>;
qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <2>;
+ qcom,pre-div-channel-scaling = <5>;
qcom,calibration-type = "absolute";
qcom,scale-function = <0>;
qcom,hw-settle-time = <0>;
diff --git a/arch/arm/boot/dts/msm8926-qrd-skug.dtsi b/arch/arm/boot/dts/msm8926-qrd-skug.dtsi
index fea5a9e..c93ea12 100644
--- a/arch/arm/boot/dts/msm8926-qrd-skug.dtsi
+++ b/arch/arm/boot/dts/msm8926-qrd-skug.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
@@ -10,7 +10,6 @@
* GNU General Public License for more details.
*/
-/include/ "msm8926.dtsi"
/include/ "msm8226-qrd.dtsi"
/include/ "msm8926-camera-sensor-qrd.dtsi"
diff --git a/arch/arm/boot/dts/msm8926-1080p-cdp.dts b/arch/arm/boot/dts/msm8926-v1-1080p-cdp.dts
similarity index 89%
copy from arch/arm/boot/dts/msm8926-1080p-cdp.dts
copy to arch/arm/boot/dts/msm8926-v1-1080p-cdp.dts
index 33e484a..1b6f971 100644
--- a/arch/arm/boot/dts/msm8926-1080p-cdp.dts
+++ b/arch/arm/boot/dts/msm8926-v1-1080p-cdp.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
@@ -12,7 +12,7 @@
/dts-v1/;
-/include/ "msm8926.dtsi"
+/include/ "msm8926-v1.dtsi"
/include/ "msm8226-1080p-cdp.dtsi"
/include/ "msm8226-camera-sensor-cdp.dtsi"
diff --git a/arch/arm/boot/dts/msm8926-1080p-mtp.dts b/arch/arm/boot/dts/msm8926-v1-1080p-mtp.dts
similarity index 87%
rename from arch/arm/boot/dts/msm8926-1080p-mtp.dts
rename to arch/arm/boot/dts/msm8926-v1-1080p-mtp.dts
index c1217a2..0812c54 100644
--- a/arch/arm/boot/dts/msm8926-1080p-mtp.dts
+++ b/arch/arm/boot/dts/msm8926-v1-1080p-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
@@ -12,7 +12,7 @@
/dts-v1/;
-/include/ "msm8926.dtsi"
+/include/ "msm8926-v1.dtsi"
/include/ "msm8226-1080p-mtp.dtsi"
/include/ "msm8226-camera-sensor-mtp.dtsi"
diff --git a/arch/arm/boot/dts/msm8926-720p-cdp.dts b/arch/arm/boot/dts/msm8926-v1-720p-cdp.dts
similarity index 89%
copy from arch/arm/boot/dts/msm8926-720p-cdp.dts
copy to arch/arm/boot/dts/msm8926-v1-720p-cdp.dts
index 80bb5e6..37da01c 100644
--- a/arch/arm/boot/dts/msm8926-720p-cdp.dts
+++ b/arch/arm/boot/dts/msm8926-v1-720p-cdp.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
@@ -12,7 +12,7 @@
/dts-v1/;
-/include/ "msm8926.dtsi"
+/include/ "msm8926-v1.dtsi"
/include/ "msm8226-720p-cdp.dtsi"
/include/ "msm8226-camera-sensor-cdp.dtsi"
diff --git a/arch/arm/boot/dts/msm8926-720p-mtp.dts b/arch/arm/boot/dts/msm8926-v1-720p-mtp.dts
similarity index 87%
copy from arch/arm/boot/dts/msm8926-720p-mtp.dts
copy to arch/arm/boot/dts/msm8926-v1-720p-mtp.dts
index 32301fd..7b49930 100644
--- a/arch/arm/boot/dts/msm8926-720p-mtp.dts
+++ b/arch/arm/boot/dts/msm8926-v1-720p-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
@@ -12,7 +12,7 @@
/dts-v1/;
-/include/ "msm8926.dtsi"
+/include/ "msm8926-v1.dtsi"
/include/ "msm8226-720p-mtp.dtsi"
/include/ "msm8226-camera-sensor-mtp.dtsi"
diff --git a/arch/arm/boot/dts/msm8926-qrd-skug-pvt.dts b/arch/arm/boot/dts/msm8926-v1-qrd-skug-pvt.dts
similarity index 88%
rename from arch/arm/boot/dts/msm8926-qrd-skug-pvt.dts
rename to arch/arm/boot/dts/msm8926-v1-qrd-skug-pvt.dts
index 2a45592..fb5bc01 100644
--- a/arch/arm/boot/dts/msm8926-qrd-skug-pvt.dts
+++ b/arch/arm/boot/dts/msm8926-v1-qrd-skug-pvt.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
@@ -11,6 +11,7 @@
*/
/dts-v1/;
+/include/ "msm8926-v1.dtsi"
/include/ "msm8926-qrd-skug.dtsi"
/ {
diff --git a/arch/arm/boot/dts/msm8926-qrd-skug.dts b/arch/arm/boot/dts/msm8926-v1-qrd-skug.dts
similarity index 86%
rename from arch/arm/boot/dts/msm8926-qrd-skug.dts
rename to arch/arm/boot/dts/msm8926-v1-qrd-skug.dts
index 4aab4f9..0cb6119 100644
--- a/arch/arm/boot/dts/msm8926-qrd-skug.dts
+++ b/arch/arm/boot/dts/msm8926-v1-qrd-skug.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
@@ -11,6 +11,7 @@
*/
/dts-v1/;
+/include/ "msm8926-v1.dtsi"
/include/ "msm8926-qrd-skug.dtsi"
/ {
diff --git a/arch/arm/boot/dts/msm8926-qrd.dts b/arch/arm/boot/dts/msm8926-v1-qrd.dts
similarity index 86%
rename from arch/arm/boot/dts/msm8926-qrd.dts
rename to arch/arm/boot/dts/msm8926-v1-qrd.dts
index 8ee8828..fa6ff64 100644
--- a/arch/arm/boot/dts/msm8926-qrd.dts
+++ b/arch/arm/boot/dts/msm8926-v1-qrd.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
@@ -11,7 +11,7 @@
*/
/dts-v1/;
-/include/ "msm8926.dtsi"
+/include/ "msm8926-v1.dtsi"
/include/ "msm8226-qrd.dtsi"
/ {
diff --git a/arch/arm/boot/dts/msm8926-qrd.dts b/arch/arm/boot/dts/msm8926-v1.dtsi
similarity index 68%
copy from arch/arm/boot/dts/msm8926-qrd.dts
copy to arch/arm/boot/dts/msm8926-v1.dtsi
index 8ee8828..c70cf71 100644
--- a/arch/arm/boot/dts/msm8926-qrd.dts
+++ b/arch/arm/boot/dts/msm8926-v1.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
@@ -10,12 +10,11 @@
* GNU General Public License for more details.
*/
-/dts-v1/;
/include/ "msm8926.dtsi"
-/include/ "msm8226-qrd.dtsi"
/ {
- model = "Qualcomm MSM 8926 QRD";
- compatible = "qcom,msm8926-qrd", "qcom,msm8926", "qcom,qrd";
- qcom,board-id = <11 0>;
+ qcom,msm-id = <200 0>,
+ <224 0>,
+ <200 0x10001>,
+ <224 0x10001>;
};
diff --git a/arch/arm/boot/dts/msm8926-1080p-cdp.dts b/arch/arm/boot/dts/msm8926-v2-1080p-cdp.dts
similarity index 89%
rename from arch/arm/boot/dts/msm8926-1080p-cdp.dts
rename to arch/arm/boot/dts/msm8926-v2-1080p-cdp.dts
index 33e484a..a4ebbe1 100644
--- a/arch/arm/boot/dts/msm8926-1080p-cdp.dts
+++ b/arch/arm/boot/dts/msm8926-v2-1080p-cdp.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
@@ -12,7 +12,7 @@
/dts-v1/;
-/include/ "msm8926.dtsi"
+/include/ "msm8926-v2.dtsi"
/include/ "msm8226-1080p-cdp.dtsi"
/include/ "msm8226-camera-sensor-cdp.dtsi"
diff --git a/arch/arm/boot/dts/msm8926-1080p-cdp.dts b/arch/arm/boot/dts/msm8926-v2-1080p-ext-buck-cdp.dts
similarity index 86%
copy from arch/arm/boot/dts/msm8926-1080p-cdp.dts
copy to arch/arm/boot/dts/msm8926-v2-1080p-ext-buck-cdp.dts
index 33e484a..64e872b 100644
--- a/arch/arm/boot/dts/msm8926-1080p-cdp.dts
+++ b/arch/arm/boot/dts/msm8926-v2-1080p-ext-buck-cdp.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
@@ -12,14 +12,14 @@
/dts-v1/;
-/include/ "msm8926.dtsi"
+/include/ "msm8926-v2-ext-buck.dtsi"
/include/ "msm8226-1080p-cdp.dtsi"
/include/ "msm8226-camera-sensor-cdp.dtsi"
/ {
model = "Qualcomm MSM 8926 CDP";
compatible = "qcom,msm8926-cdp", "qcom,msm8926", "qcom,cdp";
- qcom,board-id = <1 2>;
+ qcom,board-id = <1 3>;
};
&pm8226_chg {
diff --git a/arch/arm/boot/dts/msm8926-1080p-mtp.dts b/arch/arm/boot/dts/msm8926-v2-1080p-ext-buck-mtp.dts
similarity index 83%
copy from arch/arm/boot/dts/msm8926-1080p-mtp.dts
copy to arch/arm/boot/dts/msm8926-v2-1080p-ext-buck-mtp.dts
index c1217a2..f9a3cd8 100644
--- a/arch/arm/boot/dts/msm8926-1080p-mtp.dts
+++ b/arch/arm/boot/dts/msm8926-v2-1080p-ext-buck-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
@@ -12,12 +12,12 @@
/dts-v1/;
-/include/ "msm8926.dtsi"
+/include/ "msm8926-v2-ext-buck.dtsi"
/include/ "msm8226-1080p-mtp.dtsi"
/include/ "msm8226-camera-sensor-mtp.dtsi"
/ {
model = "Qualcomm MSM 8926 MTP";
compatible = "qcom,msm8926-mtp", "qcom,msm8926", "qcom,mtp";
- qcom,board-id = <8 2>;
+ qcom,board-id = <8 3>;
};
diff --git a/arch/arm/boot/dts/msm8926-1080p-mtp.dts b/arch/arm/boot/dts/msm8926-v2-1080p-mtp.dts
similarity index 87%
copy from arch/arm/boot/dts/msm8926-1080p-mtp.dts
copy to arch/arm/boot/dts/msm8926-v2-1080p-mtp.dts
index c1217a2..1f0bab1 100644
--- a/arch/arm/boot/dts/msm8926-1080p-mtp.dts
+++ b/arch/arm/boot/dts/msm8926-v2-1080p-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
@@ -12,7 +12,7 @@
/dts-v1/;
-/include/ "msm8926.dtsi"
+/include/ "msm8926-v2.dtsi"
/include/ "msm8226-1080p-mtp.dtsi"
/include/ "msm8226-camera-sensor-mtp.dtsi"
diff --git a/arch/arm/boot/dts/msm8926-720p-cdp.dts b/arch/arm/boot/dts/msm8926-v2-720p-cdp.dts
similarity index 89%
rename from arch/arm/boot/dts/msm8926-720p-cdp.dts
rename to arch/arm/boot/dts/msm8926-v2-720p-cdp.dts
index 80bb5e6..1e6e197 100644
--- a/arch/arm/boot/dts/msm8926-720p-cdp.dts
+++ b/arch/arm/boot/dts/msm8926-v2-720p-cdp.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
@@ -12,7 +12,7 @@
/dts-v1/;
-/include/ "msm8926.dtsi"
+/include/ "msm8926-v2.dtsi"
/include/ "msm8226-720p-cdp.dtsi"
/include/ "msm8226-camera-sensor-cdp.dtsi"
diff --git a/arch/arm/boot/dts/msm8926-720p-mtp.dts b/arch/arm/boot/dts/msm8926-v2-720p-mtp.dts
similarity index 87%
rename from arch/arm/boot/dts/msm8926-720p-mtp.dts
rename to arch/arm/boot/dts/msm8926-v2-720p-mtp.dts
index 32301fd..59ad506 100644
--- a/arch/arm/boot/dts/msm8926-720p-mtp.dts
+++ b/arch/arm/boot/dts/msm8926-v2-720p-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
@@ -12,7 +12,7 @@
/dts-v1/;
-/include/ "msm8926.dtsi"
+/include/ "msm8926-v2.dtsi"
/include/ "msm8226-720p-mtp.dtsi"
/include/ "msm8226-camera-sensor-mtp.dtsi"
diff --git a/arch/arm/boot/dts/msm8926-v2-ext-buck.dtsi b/arch/arm/boot/dts/msm8926-v2-ext-buck.dtsi
new file mode 100644
index 0000000..e5e496e
--- /dev/null
+++ b/arch/arm/boot/dts/msm8926-v2-ext-buck.dtsi
@@ -0,0 +1,56 @@
+/* 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/ "msm8926-v2.dtsi"
+
+&i2c_0 {
+ qcom,sda-gpio = <&msmgpio 14 0>;
+ qcom,scl-gpio = <&msmgpio 15 0>;
+
+ ncp6335d: ncp6335d-regulator@1c {
+ compatible = "onnn,ncp6335d-regulator";
+ reg = <0x1c>;
+ onnn,vsel = <0>;
+ onnn,slew-ns = <2666>;
+ onnn,step-size = <10000>;
+ onnn,min-slew-ns = <333>;
+ onnn,max-slew-ns = <2666>;
+ regulator-min-microvolt = <1050000>;
+ regulator-max-microvolt = <1350000>;
+ onnn,min-setpoint = <600000>;
+ onnn,vsel-gpio = <&msmgpio 2 1>;
+ onnn,discharge-enable;
+ onnn,restore-reg;
+ onnn,tlmm-config = <0x80 0x0>;
+ };
+
+ fan53555: fan53555-regulator@60 {
+ compatible = "fairchild,fan53555-regulator";
+ reg = <0x60>;
+ fairchild,backup-vsel = <1>;
+ regulator-min-microvolt = <1050000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-ramp-delay = <8000>;
+ fairchild,vsel-gpio = <&msmgpio 2 1>;
+ fairchild,restore-reg;
+ fairchild,disable-suspend;
+ };
+};
+
+&apc_vreg_corner {
+ vdd-apc-optional-prim-supply = <&ncp6335d>;
+ vdd-apc-supply = <&fan53555>;
+};
+
+&soc {
+
+};
diff --git a/arch/arm/boot/dts/msm8926-qrd-skug-pvt.dts b/arch/arm/boot/dts/msm8926-v2-qrd-skug-pvt.dts
similarity index 88%
copy from arch/arm/boot/dts/msm8926-qrd-skug-pvt.dts
copy to arch/arm/boot/dts/msm8926-v2-qrd-skug-pvt.dts
index 2a45592..f8cd88d 100644
--- a/arch/arm/boot/dts/msm8926-qrd-skug-pvt.dts
+++ b/arch/arm/boot/dts/msm8926-v2-qrd-skug-pvt.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
@@ -11,6 +11,7 @@
*/
/dts-v1/;
+/include/ "msm8926-v2.dtsi"
/include/ "msm8926-qrd-skug.dtsi"
/ {
diff --git a/arch/arm/boot/dts/msm8926-qrd-skug.dts b/arch/arm/boot/dts/msm8926-v2-qrd-skug.dts
similarity index 86%
copy from arch/arm/boot/dts/msm8926-qrd-skug.dts
copy to arch/arm/boot/dts/msm8926-v2-qrd-skug.dts
index 4aab4f9..0d69e51 100644
--- a/arch/arm/boot/dts/msm8926-qrd-skug.dts
+++ b/arch/arm/boot/dts/msm8926-v2-qrd-skug.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
@@ -11,6 +11,7 @@
*/
/dts-v1/;
+/include/ "msm8926-v2.dtsi"
/include/ "msm8926-qrd-skug.dtsi"
/ {
diff --git a/arch/arm/boot/dts/msm8926-qrd.dts b/arch/arm/boot/dts/msm8926-v2-qrd.dts
similarity index 86%
copy from arch/arm/boot/dts/msm8926-qrd.dts
copy to arch/arm/boot/dts/msm8926-v2-qrd.dts
index 8ee8828..6223055 100644
--- a/arch/arm/boot/dts/msm8926-qrd.dts
+++ b/arch/arm/boot/dts/msm8926-v2-qrd.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
@@ -11,7 +11,7 @@
*/
/dts-v1/;
-/include/ "msm8926.dtsi"
+/include/ "msm8926-v2.dtsi"
/include/ "msm8226-qrd.dtsi"
/ {
diff --git a/arch/arm/boot/dts/msm8926-v2.dtsi b/arch/arm/boot/dts/msm8926-v2.dtsi
new file mode 100644
index 0000000..9d0c6db
--- /dev/null
+++ b/arch/arm/boot/dts/msm8926-v2.dtsi
@@ -0,0 +1,86 @@
+/* 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/ "msm8926.dtsi"
+
+/ {
+ qcom,msm-id = <200 0x20000>,
+ <224 0x20000>;
+};
+
+&msm_gpu {
+ /* Updated chip ID */
+ qcom,chipid = <0x03000512>;
+
+ /* Bus Scale Settings */
+ qcom,msm-bus,num-cases = <4>;
+ qcom,msm-bus,vectors-KBps =
+ <26 512 0 0>, <89 604 0 0>,
+ <26 512 0 1800000>, <89 604 0 3200000>,
+ <26 512 0 3600000>, <89 604 0 5120000>,
+ <26 512 0 4800000>, <89 604 0 6400000>;
+
+ /* Power levels */
+ qcom,gpu-pwrlevels {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ compatible = "qcom,gpu-pwrlevels";
+
+ qcom,gpu-pwrlevel@0 {
+ reg = <0>;
+ qcom,gpu-freq = <500000000>;
+ qcom,bus-freq = <3>;
+ qcom,io-fraction = <0>;
+ };
+
+ qcom,gpu-pwrlevel@1 {
+ reg = <1>;
+ qcom,gpu-freq = <320000000>;
+ qcom,bus-freq = <2>;
+ qcom,io-fraction = <33>;
+ };
+
+ qcom,gpu-pwrlevel@2 {
+ reg = <2>;
+ qcom,gpu-freq = <200000000>;
+ qcom,bus-freq = <1>;
+ qcom,io-fraction = <100>;
+ };
+
+ qcom,gpu-pwrlevel@3 {
+ reg = <3>;
+ qcom,gpu-freq = <19000000>;
+ qcom,bus-freq = <0>;
+ qcom,io-fraction = <0>;
+ };
+ };
+};
+
+&pm8226_l3 {
+ regulator-max-microvolt = <1287500>;
+};
+
+&pm8226_l3_ao {
+ regulator-max-microvolt = <1287500>;
+};
+
+&pm8226_l3_so {
+ regulator-max-microvolt = <1287500>;
+};
+
+&apc_vreg_corner {
+ /delete-property/ qcom,cpr-enable;
+ qcom,vdd-mx-vmax = <1287500>;
+ qcom,vdd-mx-vmin-method = <4>;
+ qcom,vdd-mx-corner-map = <1050000 1150000 1280000>;
+};
diff --git a/arch/arm/boot/dts/msm8926.dtsi b/arch/arm/boot/dts/msm8926.dtsi
index 1c8fe4a..54fcfeb 100644
--- a/arch/arm/boot/dts/msm8926.dtsi
+++ b/arch/arm/boot/dts/msm8926.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,10 +22,6 @@
/ {
model = "Qualcomm MSM 8926";
compatible = "qcom,msm8926";
- qcom,msm-id = <200 0>,
- <224 0>,
- <200 0x10001>,
- <224 0x10001>;
};
&soc {
@@ -48,31 +44,11 @@
< 384000000 2>,
< 787200000 4>,
<1190400000 7>;
- qcom,speed6-bin-v1 =
- < 0 0>,
- < 384000000 2>,
- < 787200000 4>,
- <1190400000 7>;
qcom,speed2-bin-v1 =
< 0 0>,
< 384000000 2>,
< 787200000 4>,
<1401600000 10>;
- qcom,speed5-bin-v1 =
- < 0 0>,
- < 384000000 2>,
- < 787200000 4>,
- <1401600000 10>;
- qcom,speed4-bin-v1 =
- < 0 0>,
- < 384000000 2>,
- < 787200000 4>,
- <1401600000 10>;
- qcom,speed7-bin-v1 =
- < 0 0>,
- < 384000000 2>,
- < 787200000 4>,
- <1401600000 10>;
qcom,speed1-bin-v1 =
< 0 0>,
< 384000000 2>,
@@ -170,9 +146,9 @@
qcom,pvs-corner-ceiling-fast = <1050000 1050000 1100000>;
qcom,cpr-step-quotient = <30>;
qcom,cpr-up-threshold = <0>;
- qcom,cpr-down-threshold = <1>;
+ qcom,cpr-down-threshold = <2>;
qcom,cpr-apc-volt-step = <10000>;
- qcom,cpr-quotient-adjustment = <96>;
+ qcom,cpr-quotient-adjustment = <0 72 72>;
};
&tsens {
diff --git a/arch/arm/boot/dts/msm8974-fluid.dtsi b/arch/arm/boot/dts/msm8974-fluid.dtsi
index 484b9bc..eb5a5b6 100644
--- a/arch/arm/boot/dts/msm8974-fluid.dtsi
+++ b/arch/arm/boot/dts/msm8974-fluid.dtsi
@@ -133,6 +133,20 @@
};
};
+ i2c@f9928000 { /* BLSP1 QUP6 */
+ nfc-nci@e {
+ compatible = "qcom,nfc-nci";
+ reg = <0x0e>;
+ qcom,irq-gpio = <&msmgpio 59 0x00>;
+ qcom,dis-gpio = <&msmgpio 13 0x00>;
+ qcom,clk-src = "BBCLK2";
+ qcom,clk-en-gpio = <&msmgpio 0 0x00>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <59 0>;
+ qcom,clk-gpio = <&pm8941_gpios 32 0>;
+ };
+ };
+
i2c@f9967000 {
sii8334@72 {
compatible = "qcom,mhl-sii8334";
@@ -607,6 +621,11 @@
};
gpio@df00 { /* GPIO 32 */
+ 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@e000 { /* GPIO 33 */
diff --git a/arch/arm/boot/dts/msm8974-liquid.dtsi b/arch/arm/boot/dts/msm8974-liquid.dtsi
index 397e10e..942aba4 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_s3>;
+ qca,bt-vdd-io-supply = <&pm8941_l10>;
qca,bt-vdd-pa-supply = <&pm8941_l19>;
};
@@ -380,47 +380,34 @@
qcom,prim-auxpcm-gpio-set = "prim-gpio-tert";
};
- 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>;
+ 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_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>;
- };
+ 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 {
@@ -445,6 +432,38 @@
qcom,msm-bus,name = "wlan_sdio";
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 bf330f3..89f4af8 100644
--- a/arch/arm/boot/dts/msm8974-mdss.dtsi
+++ b/arch/arm/boot/dts/msm8974-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
@@ -21,6 +21,21 @@
qcom,max-bandwidth-low-kbps = <2300000>;
qcom,max-bandwidth-high-kbps = <3000000>;
+
+ /* Bus Scale Settings */
+ qcom,msm-bus,name = "mdss_mdp";
+ qcom,msm-bus,num-cases = <3>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <22 512 0 0>,
+ <22 512 0 6400000>,
+ <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-clk-factor = <5 4>; /* 1.25 times */
+
qcom,max-clk-rate = <320000000>;
qcom,mdss-pipe-vig-off = <0x00001200 0x00001600
0x00001A00>;
@@ -66,6 +81,15 @@
<0x04B0 0xCCCCC0C0>,
<0x04B8 0xCCCCC000>;
+ /* buffer parameters to calculate prefill bandwidth */
+ qcom,mdss-prefill-outstanding-buffer-bytes = <1024>;
+ qcom,mdss-prefill-y-buffer-bytes = <4096>;
+ qcom,mdss-prefill-scaler-buffer-lines-bilinear = <2>;
+ qcom,mdss-prefill-scaler-buffer-lines-caf = <4>;
+ qcom,mdss-prefill-post-scaler-buffer-pixels = <0>;
+ qcom,mdss-prefill-pingpong-buffer-pixels = <4096>;
+ qcom,mdss-prefill-fbc-lines = <2>;
+
mdss_fb0: qcom,mdss_fb_primary {
cell-index = <0>;
compatible = "qcom,mdss-fb";
diff --git a/arch/arm/boot/dts/msm8974-v1-pm.dtsi b/arch/arm/boot/dts/msm8974-v1-pm.dtsi
index 179edac..886177d 100644
--- a/arch/arm/boot/dts/msm8974-v1-pm.dtsi
+++ b/arch/arm/boot/dts/msm8974-v1-pm.dtsi
@@ -124,7 +124,7 @@
qcom,phase-port = <0x1>;
qcom,pfm-port = <0x2>;
qcom,saw2-spm-cmd-ret = [1f 00 20 03 22 00 0f];
- qcom,saw2-spm-cmd-gdhs = [00 20 32 42 07 44 22 50 02 32 50 0f];
+ qcom,saw2-spm-cmd-gdhs = [00 20 32 42 03 44 22 50 02 32 50 0f];
qcom,saw2-spm-cmd-pc = [00 10 32 b0 11 42 07 01 b0 12 44
50 02 32 50 0f];
qcom,L2-spm-is-apcs-master;
diff --git a/arch/arm/boot/dts/msm8974-v2-pm.dtsi b/arch/arm/boot/dts/msm8974-v2-pm.dtsi
index cdd5fde..5e6efb9 100644
--- a/arch/arm/boot/dts/msm8974-v2-pm.dtsi
+++ b/arch/arm/boot/dts/msm8974-v2-pm.dtsi
@@ -120,7 +120,7 @@
qcom,phase-port = <0x1>;
qcom,pfm-port = <0x2>;
qcom,saw2-spm-cmd-ret = [1f 00 03 00 0f];
- qcom,saw2-spm-cmd-gdhs = [00 32 42 07 44 50 02 32 50 0f];
+ qcom,saw2-spm-cmd-gdhs = [00 32 42 03 44 50 02 32 50 0f];
qcom,saw2-spm-cmd-pc = [00 10 32 b0 11 42 07 01 b0 12 44
50 02 32 50 0f];
qcom,L2-spm-is-apcs-master;
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index bfe52a3..23ddc8c 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -2012,7 +2012,7 @@
qcom,disk-encrypt-pipe-pair = <2>;
qcom,hlos-ce-hw-instance = <1>;
qcom,qsee-ce-hw-instance = <0>;
- qcom,support-bus-scaling = <1>;
+ qcom,support-bus-scaling;
qcom,msm-bus,name = "qseecom-noc";
qcom,msm-bus,num-cases = <4>;
qcom,msm-bus,num-paths = <1>;
@@ -2133,9 +2133,13 @@
interrupts = <0 236 0>;
qcom,bam-pipe-pair = <2>;
qcom,ce-hw-instance = <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>;
@@ -2347,6 +2351,22 @@
compatible = "qcom,bcl";
};
+ i2c@f9928000 { /* BLSP-1 QUP-6 */
+ cell-index = <3>;
+ compatible = "qcom,i2c-qup";
+ reg = <0xf9928000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "qup_phys_addr";
+ interrupts = <0 100 0>;
+ interrupt-names = "qup_err_intr";
+ qcom,i2c-bus-freq = <400000>;
+ qcom,i2c-src-freq = <19200000>;
+ qcom,scl-gpio = <&msmgpio 30 0>;
+ qcom,sda-gpio = <&msmgpio 29 0>;
+ qcom,master-id = <86>;
+ };
+
qcom,ssm {
compatible = "qcom,ssm";
qcom,channel-name = "SSM_RTR";
diff --git a/arch/arm/boot/dts/msm8974pro-ab-pm8941-fluid-hbtp.dts b/arch/arm/boot/dts/msm8974pro-ab-pm8941-fluid-hbtp.dts
new file mode 100644
index 0000000..f4f7968
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974pro-ab-pm8941-fluid-hbtp.dts
@@ -0,0 +1,42 @@
+/* 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.
+ */
+
+/dts-v1/;
+
+/include/ "msm8974pro-ab-pm8941.dtsi"
+/include/ "msm8974-fluid.dtsi"
+
+/ {
+ model = "Qualcomm MSM 8974Pro-AA/AB FLUID";
+ compatible = "qcom,msm8974-fluid", "qcom,msm8974", "qcom,fluid";
+ qcom,board-id = <3 1>;
+};
+
+&soc {
+ spi@f9966000 { /* BLSP2 QUP4 */
+ /* Leave the SPI bus for QDSP to use */
+ status = "disabled";
+ };
+
+ hbtp {
+ compatible = "qcom,hbtp";
+ vcc_ana-supply = <&pm8941_l18>;
+ };
+};
+
+&i2c_0 {
+ /* mhl-sii8334 is on i2c_0 and uses gpio 12 as mhl-pwr-gpio.
+ * Display panel needs gpio 12 as TE gpio in command mode.
+ * Disabling the bus to make sure that display gets the gpio.
+ */
+ status = "disabled";
+};
diff --git a/arch/arm/boot/dts/msm8974pro-pm.dtsi b/arch/arm/boot/dts/msm8974pro-pm.dtsi
index 29aeccd..a1e981e 100644
--- a/arch/arm/boot/dts/msm8974pro-pm.dtsi
+++ b/arch/arm/boot/dts/msm8974pro-pm.dtsi
@@ -120,7 +120,7 @@
qcom,phase-port = <0x1>;
qcom,pfm-port = <0x2>;
qcom,saw2-spm-cmd-ret = [1f 00 03 00 0f];
- qcom,saw2-spm-cmd-gdhs = [00 32 42 07 44 50 02 32 50 0f];
+ qcom,saw2-spm-cmd-gdhs = [00 32 42 03 44 50 02 32 50 0f];
qcom,saw2-spm-cmd-pc = [00 10 32 b0 11 42 07 01 b0 12 44
50 02 32 50 0f];
qcom,L2-spm-is-apcs-master;
diff --git a/arch/arm/configs/msm8226-perf_defconfig b/arch/arm/configs/msm8226-perf_defconfig
index 18402ad..3dd9c55 100644
--- a/arch/arm/configs/msm8226-perf_defconfig
+++ b/arch/arm/configs/msm8226-perf_defconfig
@@ -221,6 +221,7 @@
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_QSEECOM=y
+CONFIG_USB_HSIC_SMSC_HUB=y
CONFIG_SCSI=y
CONFIG_SCSI_TGT=y
CONFIG_BLK_DEV_SD=y
@@ -302,6 +303,8 @@
CONFIG_THERMAL_QPNP_ADC_TM=y
CONFIG_WCD9306_CODEC=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_FAN53555=y
+CONFIG_REGULATOR_ONSEMI_NCP6335D=y
CONFIG_REGULATOR_STUB=y
CONFIG_REGULATOR_QPNP=y
CONFIG_MEDIA_SUPPORT=y
diff --git a/arch/arm/configs/msm8226_defconfig b/arch/arm/configs/msm8226_defconfig
index bfe05c0..38171e6 100644
--- a/arch/arm/configs/msm8226_defconfig
+++ b/arch/arm/configs/msm8226_defconfig
@@ -221,6 +221,7 @@
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_QSEECOM=y
+CONFIG_USB_HSIC_SMSC_HUB=y
CONFIG_SCSI=y
CONFIG_SCSI_TGT=y
CONFIG_BLK_DEV_SD=y
@@ -304,6 +305,8 @@
CONFIG_THERMAL_QPNP_ADC_TM=y
CONFIG_WCD9306_CODEC=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_FAN53555=y
+CONFIG_REGULATOR_ONSEMI_NCP6335D=y
CONFIG_REGULATOR_STUB=y
CONFIG_REGULATOR_QPNP=y
CONFIG_MEDIA_SUPPORT=y
diff --git a/arch/arm/configs/msm8974-perf_defconfig b/arch/arm/configs/msm8974-perf_defconfig
index 5904aea..915b1c3 100755
--- a/arch/arm/configs/msm8974-perf_defconfig
+++ b/arch/arm/configs/msm8974-perf_defconfig
@@ -295,6 +295,7 @@
CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE=y
CONFIG_INPUT_MISC=y
+CONFIG_INPUT_HBTP_INPUT=y
CONFIG_INPUT_UINPUT=y
CONFIG_SERIAL_MSM_HS=y
CONFIG_SERIAL_MSM_HSL=y
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index 1f7eb49..f1c285d 100755
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -301,6 +301,7 @@
CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE=y
CONFIG_INPUT_MISC=y
+CONFIG_INPUT_HBTP_INPUT=y
CONFIG_INPUT_UINPUT=y
CONFIG_SERIAL_MSM_HS=y
CONFIG_SERIAL_MSM_HSL=y
diff --git a/arch/arm/mach-msm/Makefile.boot b/arch/arm/mach-msm/Makefile.boot
index 78df289..c0e9efc 100644
--- a/arch/arm/mach-msm/Makefile.boot
+++ b/arch/arm/mach-msm/Makefile.boot
@@ -69,6 +69,7 @@
dtb-$(CONFIG_ARCH_MSM8974) += msm8974-v2.2-mtp.dtb
dtb-$(CONFIG_ARCH_MSM8974) += msm8974pro-ab-pm8941-cdp.dtb
dtb-$(CONFIG_ARCH_MSM8974) += msm8974pro-ab-pm8941-fluid.dtb
+ dtb-$(CONFIG_ARCH_MSM8974) += msm8974pro-ab-pm8941-fluid-hbtp.dtb
dtb-$(CONFIG_ARCH_MSM8974) += msm8974pro-ab-pm8941-liquid.dtb
dtb-$(CONFIG_ARCH_MSM8974) += msm8974pro-ab-pm8941-mtp.dtb
dtb-$(CONFIG_ARCH_MSM8974) += msm8974pro-ac-pm8941-cdp.dtb
@@ -111,13 +112,22 @@
dtb-$(CONFIG_ARCH_MSM8226) += msm8226-v2-1080p-mtp.dtb
dtb-$(CONFIG_ARCH_MSM8226) += msm8226-v2-qrd-evt.dtb
dtb-$(CONFIG_ARCH_MSM8226) += msm8226-v2-qrd-dvt.dtb
- dtb-$(CONFIG_ARCH_MSM8226) += msm8926-720p-cdp.dtb
- dtb-$(CONFIG_ARCH_MSM8226) += msm8926-1080p-cdp.dtb
- dtb-$(CONFIG_ARCH_MSM8226) += msm8926-720p-mtp.dtb
- dtb-$(CONFIG_ARCH_MSM8226) += msm8926-1080p-mtp.dtb
- dtb-$(CONFIG_ARCH_MSM8226) += msm8926-qrd.dtb
- dtb-$(CONFIG_ARCH_MSM8226) += msm8926-qrd-skug.dtb
- dtb-$(CONFIG_ARCH_MSM8226) += msm8926-qrd-skug-pvt.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8926-v1-720p-cdp.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8926-v1-1080p-cdp.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8926-v1-720p-mtp.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8926-v1-1080p-mtp.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8926-v1-qrd.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8926-v1-qrd-skug.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8926-v1-qrd-skug-pvt.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8926-v2-720p-cdp.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8926-v2-1080p-cdp.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8926-v2-1080p-ext-buck-cdp.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8926-v2-720p-mtp.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8926-v2-1080p-mtp.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8926-v2-1080p-ext-buck-mtp.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8926-v2-qrd.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8926-v2-qrd-skug.dtb
+ dtb-$(CONFIG_ARCH_MSM8226) += msm8926-v2-qrd-skug-pvt.dtb
dtb-$(CONFIG_ARCH_MSM8226) += msm8226-v1-qrd-skuf.dtb
dtb-$(CONFIG_ARCH_MSM8226) += msm8226-v2-qrd-skuf.dtb
dtb-$(CONFIG_ARCH_MSM8226) += apq8026-v1-xpm.dtb
diff --git a/arch/arm/mach-msm/board-8226-gpiomux.c b/arch/arm/mach-msm/board-8226-gpiomux.c
index 9767746..5882ebc 100644
--- a/arch/arm/mach-msm/board-8226-gpiomux.c
+++ b/arch/arm/mach-msm/board-8226-gpiomux.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
@@ -216,13 +216,6 @@
},
},
{
- .gpio = 2, /* BLSP1 QUP1 SPI_CS1 */
- .settings = {
- [GPIOMUX_ACTIVE] = &gpio_spi_cs_act_config,
- [GPIOMUX_SUSPENDED] = &gpio_spi_susp_config,
- },
- },
- {
.gpio = 3, /* BLSP1 QUP1 SPI_CLK */
.settings = {
[GPIOMUX_ACTIVE] = &gpio_spi_act_config,
@@ -279,6 +272,16 @@
},
};
+static struct msm_gpiomux_config msm_blsp_spi_cs_config[] __initdata = {
+ {
+ .gpio = 2, /* BLSP1 QUP1 SPI_CS1 */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &gpio_spi_cs_act_config,
+ [GPIOMUX_SUSPENDED] = &gpio_spi_susp_config,
+ },
+ },
+};
+
static struct msm_gpiomux_config msm_synaptics_configs[] __initdata = {
{
.gpio = 16,
@@ -827,9 +830,13 @@
if (of_board_is_skuf())
msm_gpiomux_install(msm_skuf_blsp_configs,
ARRAY_SIZE(msm_skuf_blsp_configs));
- else
+ else {
msm_gpiomux_install(msm_blsp_configs,
ARRAY_SIZE(msm_blsp_configs));
+ if (machine_is_msm8226())
+ msm_gpiomux_install(msm_blsp_spi_cs_config,
+ ARRAY_SIZE(msm_blsp_spi_cs_config));
+ }
msm_gpiomux_install(wcnss_5wire_interface,
ARRAY_SIZE(wcnss_5wire_interface));
diff --git a/arch/arm/mach-msm/board-8226.c b/arch/arm/mach-msm/board-8226.c
index 5244918..3539ad3 100644
--- a/arch/arm/mach-msm/board-8226.c
+++ b/arch/arm/mach-msm/board-8226.c
@@ -15,6 +15,7 @@
#include <linux/errno.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/i2c/i2c-qup.h>
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
@@ -24,6 +25,9 @@
#include <linux/of_fdt.h>
#include <linux/of_irq.h>
#include <linux/memory.h>
+#include <linux/regulator/cpr-regulator.h>
+#include <linux/regulator/fan53555.h>
+#include <linux/regulator/onsemi-ncp6335d.h>
#include <linux/regulator/qpnp-regulator.h>
#include <linux/msm_tsens.h>
#include <asm/mach/map.h>
@@ -31,6 +35,7 @@
#include <asm/mach/arch.h>
#include <asm/mach/time.h>
#include <mach/board.h>
+#include <mach/msm_bus.h>
#include <mach/gpiomux.h>
#include <mach/msm_iomap.h>
#include <mach/restart.h>
@@ -52,6 +57,7 @@
#include "spm.h"
#include "pm.h"
#include "modem_notifier.h"
+#include "spm-regulator.h"
static struct memtype_reserve msm8226_reserve_table[] __initdata = {
[MEMTYPE_SMI] = {
@@ -121,10 +127,16 @@
msm_pm_sleep_status_init();
rpm_regulator_smd_driver_init();
qpnp_regulator_init();
+ spm_regulator_init();
if (of_board_is_rumi())
msm_clock_init(&msm8226_rumi_clock_init_data);
else
msm_clock_init(&msm8226_clock_init_data);
+ msm_bus_fabric_init_driver();
+ qup_i2c_init_driver();
+ ncp6335d_regulator_init();
+ fan53555_regulator_init();
+ cpr_regulator_init();
tsens_tm_init_driver();
msm_thermal_device_init();
}
diff --git a/arch/arm/mach-msm/board-8610-gpiomux.c b/arch/arm/mach-msm/board-8610-gpiomux.c
index 20bb0e4..2e12fc2 100644
--- a/arch/arm/mach-msm/board-8610-gpiomux.c
+++ b/arch/arm/mach-msm/board-8610-gpiomux.c
@@ -615,6 +615,13 @@
static struct msm_gpiomux_config msm_interrupt_configs[] __initdata = {
{
+ .gpio = 75, /* NFC_CLK_REQ_IRQ*/
+ .settings = {
+ [GPIOMUX_ACTIVE] = &interrupt_gpio_active,
+ [GPIOMUX_SUSPENDED] = &interrupt_gpio_suspend_pullup,
+ },
+ },
+ {
.gpio = 77, /* NFC_IRQ */
.settings = {
[GPIOMUX_ACTIVE] = &interrupt_gpio_active,
diff --git a/arch/arm/mach-msm/board-8974-gpiomux.c b/arch/arm/mach-msm/board-8974-gpiomux.c
index 8ab916c..cec1a8f 100755
--- a/arch/arm/mach-msm/board-8974-gpiomux.c
+++ b/arch/arm/mach-msm/board-8974-gpiomux.c
@@ -22,27 +22,28 @@
static struct gpiomux_setting ap2mdm_cfg = {
.func = GPIOMUX_FUNC_GPIO,
- .drv = GPIOMUX_DRV_8MA,
+ .drv = GPIOMUX_DRV_2MA,
.pull = GPIOMUX_PULL_NONE,
+ .dir = GPIOMUX_OUT_LOW,
};
static struct gpiomux_setting mdm2ap_status_cfg = {
.func = GPIOMUX_FUNC_GPIO,
- .drv = GPIOMUX_DRV_8MA,
- .pull = GPIOMUX_PULL_DOWN,
- .dir = GPIOMUX_IN,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+ .dir = GPIOMUX_OUT_LOW,
};
static struct gpiomux_setting mdm2ap_errfatal_cfg = {
.func = GPIOMUX_FUNC_GPIO,
- .drv = GPIOMUX_DRV_8MA,
- .pull = GPIOMUX_PULL_DOWN,
- .dir = GPIOMUX_IN,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+ .dir = GPIOMUX_OUT_LOW,
};
static struct gpiomux_setting mdm2ap_pblrdy = {
.func = GPIOMUX_FUNC_GPIO,
- .drv = GPIOMUX_DRV_8MA,
+ .drv = GPIOMUX_DRV_2MA,
.pull = GPIOMUX_PULL_NONE,
.dir = GPIOMUX_IN,
};
@@ -50,14 +51,16 @@
static struct gpiomux_setting ap2mdm_soft_reset_cfg = {
.func = GPIOMUX_FUNC_GPIO,
- .drv = GPIOMUX_DRV_8MA,
+ .drv = GPIOMUX_DRV_2MA,
.pull = GPIOMUX_PULL_NONE,
+ .dir = GPIOMUX_OUT_LOW,
};
static struct gpiomux_setting ap2mdm_wakeup = {
.func = GPIOMUX_FUNC_GPIO,
- .drv = GPIOMUX_DRV_8MA,
- .pull = GPIOMUX_PULL_DOWN,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+ .dir = GPIOMUX_OUT_LOW,
};
static struct msm_gpiomux_config mdm_configs[] __initdata = {
diff --git a/arch/arm/mach-msm/clock-8610.c b/arch/arm/mach-msm/clock-8610.c
index 672c512..d68762a 100644
--- a/arch/arm/mach-msm/clock-8610.c
+++ b/arch/arm/mach-msm/clock-8610.c
@@ -3150,6 +3150,12 @@
CLK_LOOKUP("bus_clk", gcc_ce1_axi_clk.c, "scm"),
CLK_LOOKUP("core_clk_src", ce1_clk_src.c, "scm"),
+ /* GUD Clocks */
+ CLK_LOOKUP("core_clk", gcc_ce1_clk.c, "mcd"),
+ CLK_LOOKUP("iface_clk", gcc_ce1_ahb_clk.c, "mcd"),
+ CLK_LOOKUP("bus_clk", gcc_ce1_axi_clk.c, "mcd"),
+ CLK_LOOKUP("core_clk_src", ce1_clk_src.c, "mcd"),
+
/* Add QCEDEV clocks */
CLK_LOOKUP("core_clk", gcc_ce1_clk.c, "fd400000.qcom,qcedev"),
CLK_LOOKUP("iface_clk", gcc_ce1_ahb_clk.c, "fd400000.qcom,qcedev"),
diff --git a/arch/arm/mach-msm/cpr-regulator.c b/arch/arm/mach-msm/cpr-regulator.c
index 28afc91..c245079 100644
--- a/arch/arm/mach-msm/cpr-regulator.c
+++ b/arch/arm/mach-msm/cpr-regulator.c
@@ -1,5 +1,5 @@
/*
- * 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
@@ -144,6 +144,10 @@
int quot_adjust;
};
+static const char * const vdd_apc_name[] = {"vdd-apc-optional-prim",
+ "vdd-apc-optional-sec",
+ "vdd-apc"};
+
enum voltage_change_dir {
NO_CHANGE,
DOWN,
@@ -176,6 +180,7 @@
int vdd_mx_vmax;
int vdd_mx_vmin_method;
int vdd_mx_vmin;
+ int vdd_mx_corner_map[CPR_FUSE_CORNER_MAX];
/* CPR parameters */
u64 cpr_fuse_bits;
@@ -219,7 +224,6 @@
int *corner_map;
u32 num_corners;
int *quot_adjust;
- u32 quotient_adjustment;
};
#define CPR_DEBUG_MASK_IRQ BIT(0)
@@ -497,6 +501,9 @@
case VDD_MX_VMIN_MX_VMAX:
vdd_mx = cpr_vreg->vdd_mx_vmax;
break;
+ case VDD_MX_VMIN_APC_CORNER_MAP:
+ vdd_mx = cpr_vreg->vdd_mx_corner_map[fuse_corner];
+ break;
default:
vdd_mx = 0;
break;
@@ -597,6 +604,10 @@
RBCPR_CTL_UP_THRESHOLD_SHIFT;
reg_val = reg_mask;
cpr_ctl_modify(cpr_vreg, reg_mask, reg_val);
+
+ /* Disable UP interrupt */
+ cpr_irq_set(cpr_vreg, CPR_INT_DEFAULT & ~CPR_INT_UP);
+
return;
}
@@ -693,6 +704,9 @@
RBCPR_CTL_UP_THRESHOLD_SHIFT;
cpr_ctl_modify(cpr_vreg, reg_mask, reg_val);
+ /* Re-enable default interrupts */
+ cpr_irq_set(cpr_vreg, CPR_INT_DEFAULT);
+
/* Ack */
cpr_irq_clr_ack(cpr_vreg);
@@ -1084,7 +1098,7 @@
u64 efuse_bits;
int rc, process;
u32 pvs_fuse[4], pvs_fuse_redun_sel[5];
- u32 init_v, quot_adjust;
+ u32 init_v;
bool redundant;
size_t pvs_bins;
@@ -1161,11 +1175,6 @@
cpr_vreg->process = process;
- rc = of_property_read_u32(of_node,
- "qcom,cpr-quotient-adjustment", "_adjust);
- if (!rc)
- cpr_vreg->quotient_adjustment = quot_adjust;
-
return 0;
}
@@ -1186,11 +1195,17 @@
struct cpr_regulator *cpr_vreg)
{
struct device_node *of_node = pdev->dev.of_node;
- int rc;
+ int i, rc = 0;
- cpr_vreg->vdd_apc = devm_regulator_get(&pdev->dev, "vdd-apc");
- if (IS_ERR_OR_NULL(cpr_vreg->vdd_apc)) {
+ for (i = 0; i < ARRAY_SIZE(vdd_apc_name); i++) {
+ cpr_vreg->vdd_apc = devm_regulator_get(&pdev->dev,
+ vdd_apc_name[i]);
rc = PTR_RET(cpr_vreg->vdd_apc);
+ if (!IS_ERR_OR_NULL(cpr_vreg->vdd_apc))
+ break;
+ }
+
+ if (rc) {
if (rc != -EPROBE_DEFER)
pr_err("devm_regulator_get: rc=%d\n", rc);
return rc;
@@ -1223,11 +1238,23 @@
pr_err("vdd-mx-vmin-method missing: rc=%d\n", rc);
return rc;
}
- if (cpr_vreg->vdd_mx_vmin_method > VDD_MX_VMIN_MX_VMAX) {
+ if (cpr_vreg->vdd_mx_vmin_method > VDD_MX_VMIN_APC_CORNER_MAP) {
pr_err("Invalid vdd-mx-vmin-method(%d)\n",
cpr_vreg->vdd_mx_vmin_method);
return -EINVAL;
}
+
+ rc = of_property_read_u32_array(of_node,
+ "qcom,vdd-mx-corner-map",
+ &cpr_vreg->vdd_mx_corner_map[1],
+ CPR_FUSE_CORNER_MAX - 1);
+ if (rc && cpr_vreg->vdd_mx_vmin_method ==
+ VDD_MX_VMIN_APC_CORNER_MAP) {
+ pr_err("qcom,vdd-mx-corner-map missing: rc=%d\n",
+ rc);
+ return rc;
+ }
+
}
return 0;
@@ -1374,6 +1401,7 @@
int bp_ro_sel[CPR_FUSE_CORNER_MAX];
u32 ro_sel, val;
u64 fuse_bits, fuse_bits_2;
+ u32 quot_adjust[CPR_FUSE_CORNER_MAX];
rc = of_property_read_u32_array(of_node, "qcom,cpr-fuse-redun-sel",
cpr_fuse_redun_sel, 5);
@@ -1475,13 +1503,22 @@
& CPR_FUSE_RO_SEL_BITS_MASK;
val = (fuse_bits >> bp_target_quot[i])
& CPR_FUSE_TARGET_QUOT_BITS_MASK;
- val += cpr_vreg->quotient_adjustment;
cpr_vreg->cpr_fuse_target_quot[i] = val;
cpr_vreg->cpr_fuse_ro_sel[i] = ro_sel;
pr_info("Corner[%d]: ro_sel = %d, target quot = %d\n",
i, ro_sel, val);
}
+ rc = of_property_read_u32_array(of_node, "qcom,cpr-quotient-adjustment",
+ "_adjust[1], CPR_FUSE_CORNER_MAX - 1);
+ if (!rc) {
+ for (i = CPR_FUSE_CORNER_SVS; i < CPR_FUSE_CORNER_MAX; i++) {
+ cpr_vreg->cpr_fuse_target_quot[i] += quot_adjust[i];
+ pr_info("Corner[%d]: adjusted target quot = %d\n",
+ i, cpr_vreg->cpr_fuse_target_quot[i]);
+ }
+ }
+
if (cpr_vreg->flags & FLAGS_UPLIFT_QUOT_VOLT) {
cpr_voltage_uplift_wa_inc_quot(cpr_vreg, of_node);
for (i = CPR_FUSE_CORNER_SVS; i < CPR_FUSE_CORNER_MAX; i++) {
diff --git a/arch/arm/mach-msm/cpufreq.c b/arch/arm/mach-msm/cpufreq.c
index 46505e0..f1904de 100644
--- a/arch/arm/mach-msm/cpufreq.c
+++ b/arch/arm/mach-msm/cpufreq.c
@@ -259,7 +259,7 @@
init_completion(&cpu_work->complete);
/* synchronous cpus share the same policy */
- if (!cpu_clk[policy->cpu])
+ if (is_clk && !cpu_clk[policy->cpu])
return 0;
if (cpufreq_frequency_table_cpuinfo(policy, table)) {
diff --git a/arch/arm/mach-msm/include/mach/msm_bus.h b/arch/arm/mach-msm/include/mach/msm_bus.h
index ebc43da..57f781f 100644
--- a/arch/arm/mach-msm/include/mach/msm_bus.h
+++ b/arch/arm/mach-msm/include/mach/msm_bus.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, 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
@@ -75,6 +75,7 @@
*/
#ifdef CONFIG_MSM_BUS_SCALING
+int __init msm_bus_fabric_init_driver(void);
uint32_t msm_bus_scale_register_client(struct msm_bus_scale_pdata *pdata);
int msm_bus_scale_client_update_request(uint32_t cl, unsigned int index);
void msm_bus_scale_unregister_client(uint32_t cl);
@@ -83,6 +84,8 @@
int msm_bus_axi_portunhalt(int master_port);
#else
+static inline int __init msm_bus_fabric_init_driver(void) { return 0; }
+
static inline uint32_t
msm_bus_scale_register_client(struct msm_bus_scale_pdata *pdata)
{
diff --git a/arch/arm/mach-msm/include/mach/qseecomi.h b/arch/arm/mach-msm/include/mach/qseecomi.h
index 222a171..cb850c2 100644
--- a/arch/arm/mach-msm/include/mach/qseecomi.h
+++ b/arch/arm/mach-msm/include/mach/qseecomi.h
@@ -1,5 +1,5 @@
/*
- * 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
@@ -27,6 +27,7 @@
#define QSEOS_RESULT_FAIL_KS_ALREADY_DONE -69
#define QSEOS_RESULT_FAIL_KEY_ID_DNE -70
#define QSEOS_RESULT_FAIL_INCORRECT_PSWD -71
+#define QSEOS_RESULT_FAIL_MAX_ATTEMPT -72
enum qseecom_command_scm_resp_type {
QSEOS_APP_ID = 0xEE01,
diff --git a/arch/arm/mach-msm/include/mach/remote_spinlock.h b/arch/arm/mach-msm/include/mach/remote_spinlock.h
index 8c8b821..8ec68a1 100644
--- a/arch/arm/mach-msm/include/mach/remote_spinlock.h
+++ b/arch/arm/mach-msm/include/mach/remote_spinlock.h
@@ -1,4 +1,5 @@
-/* Copyright (c) 2009, 2011, 2013 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2009, 2011, 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 +23,9 @@
#include <linux/io.h>
#include <linux/types.h>
+#define REMOTE_SPINLOCK_NUM_PID 128
+#define REMOTE_SPINLOCK_TID_START REMOTE_SPINLOCK_NUM_PID
+
/* Remote spinlock definitions. */
struct dek_spinlock {
@@ -48,6 +52,8 @@
int _remote_spin_trylock(_remote_spinlock_t *lock);
int _remote_spin_release(_remote_spinlock_t *lock, uint32_t pid);
int _remote_spin_owner(_remote_spinlock_t *lock);
+void _remote_spin_lock_rlock_id(_remote_spinlock_t *lock, uint32_t tid);
+void _remote_spin_unlock_rlock(_remote_spinlock_t *lock);
#else
static inline
int _remote_spin_lock_init(remote_spinlock_id_t id, _remote_spinlock_t *lock)
@@ -69,6 +75,9 @@
{
return -ENODEV;
}
+static inline void _remote_spin_lock_rlock_id(_remote_spinlock_t *lock,
+ uint32_t tid) {}
+static inline void _remote_spin_unlock_rlock(_remote_spinlock_t *lock) {}
#endif
diff --git a/arch/arm/mach-msm/lpm_levels.c b/arch/arm/mach-msm/lpm_levels.c
index 7553f82..a3a2f56 100644
--- a/arch/arm/mach-msm/lpm_levels.c
+++ b/arch/arm/mach-msm/lpm_levels.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
@@ -341,7 +341,8 @@
const struct cpumask *nextcpu;
spin_lock(&system_state->sync_lock);
- if (num_powered_cores != system_state->num_cores_in_sync) {
+ if (index < 0 ||
+ num_powered_cores != system_state->num_cores_in_sync) {
spin_unlock(&system_state->sync_lock);
return;
}
@@ -418,7 +419,7 @@
system_lvl->num_cpu_votes--;
}
- if (!first_core_up)
+ if (!first_core_up || index < 0)
goto unlock_and_return;
if (default_l2_mode != system_state->system_level[index].l2_mode)
@@ -429,6 +430,7 @@
msm_mpm_exit_sleep(from_idle);
}
unlock_and_return:
+ system_state->last_entered_cluster_index = -1;
spin_unlock(&system_state->sync_lock);
}
@@ -724,8 +726,7 @@
idx = lpm_system_select(system_state, cpu_index, from_idle);
- if (idx >= 0)
- lpm_system_prepare(system_state, idx, from_idle);
+ lpm_system_prepare(system_state, idx, from_idle);
msm_cpu_pm_enter_sleep(cpu_level->mode, from_idle);
@@ -1011,6 +1012,7 @@
}
sys_state.system_level = level;
sys_state.num_system_levels = num_levels;
+ sys_state.last_entered_cluster_index = -1;
return ret;
fail:
kfree(level);
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c b/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
index 7c694a7..626c5e8 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2013, Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2014, 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
@@ -896,9 +896,17 @@
},
};
-static int __init msm_bus_fabric_init_driver(void)
+int __init msm_bus_fabric_init_driver(void)
{
+ static bool initialized;
+
+ if (initialized)
+ return 0;
+ else
+ initialized = true;
+
MSM_BUS_ERR("msm_bus_fabric_init_driver\n");
return platform_driver_register(&msm_bus_fabric_driver);
}
+EXPORT_SYMBOL(msm_bus_fabric_init_driver);
subsys_initcall(msm_bus_fabric_init_driver);
diff --git a/arch/arm/mach-msm/pil-pronto.c b/arch/arm/mach-msm/pil-pronto.c
index 69df3ae..a7fc204 100644
--- a/arch/arm/mach-msm/pil-pronto.c
+++ b/arch/arm/mach-msm/pil-pronto.c
@@ -85,6 +85,7 @@
bool crash;
struct delayed_work cancel_vote_work;
struct ramdump_device *ramdump_dev;
+ struct work_struct wcnss_wdog_bite_work;
};
static int pil_pronto_make_proxy_vote(struct pil_desc *pil)
@@ -322,6 +323,16 @@
return IRQ_HANDLED;
}
+static void wcnss_wdog_bite_work_hdlr(struct work_struct *wcnss_work)
+{
+ struct pronto_data *drv = container_of(wcnss_work, struct pronto_data,
+ wcnss_wdog_bite_work);
+
+ wcnss_log_debug_regs_on_bite();
+
+ restart_wcnss(drv);
+}
+
static irqreturn_t wcnss_wdog_bite_irq_hdlr(int irq, void *dev_id)
{
struct pronto_data *drv = subsys_to_drv(dev_id);
@@ -334,10 +345,9 @@
pr_err("Ignoring wcnss bite irq, restart in progress\n");
return IRQ_HANDLED;
}
- wcnss_log_debug_regs_on_bite();
drv->restart_inprogress = true;
- restart_wcnss(drv);
+ schedule_work(&drv->wcnss_wdog_bite_work);
return IRQ_HANDLED;
}
@@ -490,6 +500,7 @@
drv->subsys_desc.wdog_bite_handler = wcnss_wdog_bite_irq_hdlr;
INIT_DELAYED_WORK(&drv->cancel_vote_work, wcnss_post_bootup);
+ INIT_WORK(&drv->wcnss_wdog_bite_work, wcnss_wdog_bite_work_hdlr);
drv->subsys = subsys_register(&drv->subsys_desc);
if (IS_ERR(drv->subsys)) {
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_utils.c b/arch/arm/mach-msm/qdsp6v2/audio_utils.c
index 2f1ff3e..109e120 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_utils.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_utils.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
@@ -23,6 +23,11 @@
#include <asm/ioctls.h>
#include "audio_utils.h"
+#define MIN_FRAME_SIZE 1536
+#define NUM_FRAMES 5
+#define META_SIZE (sizeof(struct meta_out_dsp))
+#define FRAME_SIZE (1 + ((MIN_FRAME_SIZE + META_SIZE) * NUM_FRAMES))
+
static int audio_in_pause(struct q6audio_in *audio)
{
int rc;
@@ -258,6 +263,11 @@
rc = -EINVAL;
break;
}
+ if ((cfg.buffer_size > FRAME_SIZE) ||
+ (cfg.buffer_count != FRAME_NUM)) {
+ rc = -EINVAL;
+ break;
+ }
audio->str_cfg.buffer_size = cfg.buffer_size;
audio->str_cfg.buffer_count = cfg.buffer_count;
if(audio->opened){
diff --git a/arch/arm/mach-msm/qdsp6v2/msm_audio_ion.c b/arch/arm/mach-msm/qdsp6v2/msm_audio_ion.c
index fc6de64..399e073 100644
--- a/arch/arm/mach-msm/qdsp6v2/msm_audio_ion.c
+++ b/arch/arm/mach-msm/qdsp6v2/msm_audio_ion.c
@@ -1,5 +1,5 @@
/*
- * 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
@@ -391,7 +391,9 @@
int msm_audio_ion_free_legacy(struct ion_client *client,
struct ion_handle *handle)
{
- /* To add condition for SMMU enabled */
+ if (msm_audio_ion_data.smmu_enabled)
+ ion_unmap_iommu(client, handle,
+ msm_audio_ion_data.domain_id, 0);
ion_unmap_kernel(client, handle);
ion_free(client, handle);
diff --git a/arch/arm/mach-msm/remote_spinlock.c b/arch/arm/mach-msm/remote_spinlock.c
index a9ebd7c..0a953ac 100644
--- a/arch/arm/mach-msm/remote_spinlock.c
+++ b/arch/arm/mach-msm/remote_spinlock.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2009, 2011-2013 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2009, 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
@@ -81,6 +81,8 @@
int (*trylock)(raw_remote_spinlock_t *lock);
int (*release)(raw_remote_spinlock_t *lock, uint32_t pid);
int (*owner)(raw_remote_spinlock_t *lock);
+ void (*lock_rlock_id)(raw_remote_spinlock_t *lock, uint32_t tid);
+ void (*unlock_rlock)(raw_remote_spinlock_t *lock);
};
static struct spinlock_ops current_ops;
@@ -364,6 +366,27 @@
writel_relaxed(0, lock);
smp_mb();
}
+
+static void __raw_remote_sfpb_spin_lock_rlock_id(raw_remote_spinlock_t *lock,
+ uint32_t tid)
+{
+ if (unlikely(!tid)) {
+ pr_err("%s: unsupported rlock tid=0\n", __func__);
+ BUG();
+ }
+
+ do {
+ writel_relaxed(tid, lock);
+ smp_mb();
+ } while (readl_relaxed(lock) != tid);
+}
+
+static void __raw_remote_sfpb_spin_unlock_rlock(raw_remote_spinlock_t *lock)
+{
+ writel_relaxed(0, lock);
+ smp_mb();
+}
+
/* end sfpb implementation -------------------------------------------------- */
/* common spinlock API ------------------------------------------------------ */
@@ -458,6 +481,9 @@
current_ops.trylock = __raw_remote_sfpb_spin_trylock;
current_ops.release = __raw_remote_gen_spin_release;
current_ops.owner = __raw_remote_gen_spin_owner;
+ current_ops.lock_rlock_id =
+ __raw_remote_sfpb_spin_lock_rlock_id;
+ current_ops.unlock_rlock = __raw_remote_sfpb_spin_unlock_rlock;
is_hw_lock_type = 1;
break;
case AUTO_MODE:
@@ -474,6 +500,10 @@
current_ops.trylock = __raw_remote_sfpb_spin_trylock;
current_ops.release = __raw_remote_gen_spin_release;
current_ops.owner = __raw_remote_gen_spin_owner;
+ current_ops.lock_rlock_id =
+ __raw_remote_sfpb_spin_lock_rlock_id;
+ current_ops.unlock_rlock =
+ __raw_remote_sfpb_spin_unlock_rlock;
is_hw_lock_type = 1;
break;
}
@@ -517,6 +547,11 @@
int n;
_remote_spinlock_t lock;
+ if (pid >= REMOTE_SPINLOCK_NUM_PID) {
+ pr_err("%s: unsupported PID %d\n", __func__, pid);
+ return;
+ }
+
for (n = 0; n < count; ++n) {
if (remote_spinlock_init_address(n, &lock) == 0)
_remote_spin_release(&lock, pid);
@@ -671,6 +706,23 @@
return current_ops.owner((raw_remote_spinlock_t *)(*lock));
}
EXPORT_SYMBOL(_remote_spin_owner);
+
+void _remote_spin_lock_rlock_id(_remote_spinlock_t *lock, uint32_t tid)
+{
+ if (unlikely(!current_ops.lock_rlock_id))
+ BUG();
+ current_ops.lock_rlock_id((raw_remote_spinlock_t *)(*lock), tid);
+}
+EXPORT_SYMBOL(_remote_spin_lock_rlock_id);
+
+void _remote_spin_unlock_rlock(_remote_spinlock_t *lock)
+{
+ if (unlikely(!current_ops.unlock_rlock))
+ BUG();
+ current_ops.unlock_rlock((raw_remote_spinlock_t *)(*lock));
+}
+EXPORT_SYMBOL(_remote_spin_unlock_rlock);
+
/* end common spinlock API -------------------------------------------------- */
/* remote mutex implementation ---------------------------------------------- */
diff --git a/arch/arm/mach-msm/rpm-regulator-smd.c b/arch/arm/mach-msm/rpm-regulator-smd.c
index 7995e9a..c7e9e75 100644
--- a/arch/arm/mach-msm/rpm-regulator-smd.c
+++ b/arch/arm/mach-msm/rpm-regulator-smd.c
@@ -163,6 +163,7 @@
struct mutex mlock;
unsigned long flags;
bool sleep_request_sent;
+ bool apps_only;
struct msm_rpm_request *handle_active;
struct msm_rpm_request *handle_sleep;
};
@@ -241,6 +242,16 @@
& BIT(RPM_REGULATOR_PARAM_ENABLE)));
}
+static inline bool rpm_vreg_shared_active_or_sleep_enabled_valid
+ (struct rpm_vreg *rpm_vreg)
+{
+ return !rpm_vreg->apps_only &&
+ ((rpm_vreg->aggr_req_active.valid
+ & BIT(RPM_REGULATOR_PARAM_ENABLE))
+ || (rpm_vreg->aggr_req_sleep.valid
+ & BIT(RPM_REGULATOR_PARAM_ENABLE)));
+}
+
/*
* This is used when voting for LPM or HPM by subtracting or adding to the
* hpm_min_load of a regulator. It has units of uA.
@@ -660,7 +671,8 @@
* if the regulator has been configured to always send voltage updates.
*/
if (reg->always_send_voltage
- || rpm_vreg_active_or_sleep_enabled(reg->rpm_vreg))
+ || rpm_vreg_active_or_sleep_enabled(reg->rpm_vreg)
+ || rpm_vreg_shared_active_or_sleep_enabled_valid(reg->rpm_vreg))
rc = rpm_vreg_aggregate_requests(reg);
if (rc) {
@@ -719,7 +731,8 @@
* updates.
*/
if (reg->always_send_voltage
- || rpm_vreg_active_or_sleep_enabled(reg->rpm_vreg))
+ || rpm_vreg_active_or_sleep_enabled(reg->rpm_vreg)
+ || rpm_vreg_shared_active_or_sleep_enabled_valid(reg->rpm_vreg))
rc = rpm_vreg_aggregate_requests(reg);
if (rc) {
@@ -774,7 +787,8 @@
* voltage updates.
*/
if (reg->always_send_voltage
- || rpm_vreg_active_or_sleep_enabled(reg->rpm_vreg))
+ || rpm_vreg_active_or_sleep_enabled(reg->rpm_vreg)
+ || rpm_vreg_shared_active_or_sleep_enabled_valid(reg->rpm_vreg))
rc = rpm_vreg_aggregate_requests(reg);
if (rc) {
@@ -829,7 +843,8 @@
* current updates.
*/
if (reg->always_send_current
- || rpm_vreg_active_or_sleep_enabled(reg->rpm_vreg))
+ || rpm_vreg_active_or_sleep_enabled(reg->rpm_vreg)
+ || rpm_vreg_shared_active_or_sleep_enabled_valid(reg->rpm_vreg))
rc = rpm_vreg_aggregate_requests(reg);
if (rc) {
@@ -1579,6 +1594,7 @@
of_property_read_u32(node, "qcom,enable-time", &rpm_vreg->enable_time);
of_property_read_u32(node, "qcom,hpm-min-load",
&rpm_vreg->hpm_min_load);
+ rpm_vreg->apps_only = of_property_read_bool(node, "qcom,apps-only");
rpm_vreg->handle_active = msm_rpm_create_request(RPM_SET_ACTIVE,
resource_type, rpm_vreg->resource_id, RPM_REGULATOR_PARAM_MAX);
diff --git a/block/blk-core.c b/block/blk-core.c
index 123f79a..00eab3b 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -314,7 +314,8 @@
if (!q->notified_urgent &&
q->elevator->type->ops.elevator_is_urgent_fn &&
q->urgent_request_fn &&
- q->elevator->type->ops.elevator_is_urgent_fn(q)) {
+ q->elevator->type->ops.elevator_is_urgent_fn(q) &&
+ list_empty(&q->flush_data_in_flight)) {
q->notified_urgent = true;
q->urgent_request_fn(q);
} else
diff --git a/drivers/bluetooth/bluetooth-power.c b/drivers/bluetooth/bluetooth-power.c
index 13c9080..9ccee05 100644
--- a/drivers/bluetooth/bluetooth-power.c
+++ b/drivers/bluetooth/bluetooth-power.c
@@ -30,7 +30,7 @@
#define BT_PWR_DBG(fmt, arg...) pr_debug("%s: " fmt "\n" , __func__ , ## arg)
#define BT_PWR_INFO(fmt, arg...) pr_info("%s: " fmt "\n" , __func__ , ## arg)
#define BT_PWR_ERR(fmt, arg...) pr_err("%s: " fmt "\n" , __func__ , ## arg)
-
+#define BT_VDD_PA_CURRENT 60000
static struct of_device_id bt_power_match_table[] = {
{ .compatible = "qca,ar3002" },
@@ -213,6 +213,9 @@
BT_PWR_ERR("bt_power vddpa config failed");
goto vdd_pa_fail;
}
+ regulator_set_optimum_mode(
+ bt_power_pdata->bt_vdd_pa->reg,
+ BT_VDD_PA_CURRENT);
}
if (bt_power_pdata->bt_chip_pwd) {
rc = bt_configure_vreg(bt_power_pdata->bt_chip_pwd);
diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c
index 0383d8f..8272528 100644
--- a/drivers/bluetooth/hci_ath.c
+++ b/drivers/bluetooth/hci_ath.c
@@ -5,7 +5,7 @@
* power management protocol extension to H4 to support AR300x Bluetooth Chip.
*
* Copyright (c) 2009-2010 Atheros Communications Inc.
- * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2014 The Linux Foundation. All rights reserved.
*
* Acknowledgements:
* This file is based on hci_h4.c, which was written
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index 37c236d..0edfdad 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -847,6 +847,22 @@
return ret;
}
+int diag_dci_find_client_index_health(int client_id)
+{
+ int i, ret = DCI_CLIENT_INDEX_INVALID;
+
+ for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+ if (driver->dci_client_tbl[i].client != NULL) {
+ if (driver->dci_client_tbl[i].client_id ==
+ client_id) {
+ ret = i;
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
int diag_dci_find_client_index(int client_id)
{
int i, ret = DCI_CLIENT_INDEX_INVALID;
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
index 2ab8a36..870b0f3 100644
--- a/drivers/char/diag/diag_dci.h
+++ b/drivers/char/diag/diag_dci.h
@@ -56,6 +56,7 @@
} __packed;
struct diag_dci_client_tbl {
+ uint32_t client_id;
struct task_struct *client;
uint16_t list; /* bit mask */
int signal_type;
@@ -74,6 +75,7 @@
/* This is used for DCI health stats */
struct diag_dci_health_stats {
+ int client_id;
int dropped_logs;
int dropped_events;
int received_logs;
@@ -119,6 +121,8 @@
int recd_bytes);
int diag_process_dci_transaction(unsigned char *buf, int len);
void extract_dci_pkt_rsp(struct diag_smd_info *smd_info, unsigned char *buf);
+
+int diag_dci_find_client_index_health(int client_id);
int diag_dci_find_client_index(int client_id);
/* DCI Log streaming functions */
void create_dci_log_mask_tbl(unsigned char *tbl_buf);
diff --git a/drivers/char/diag/diag_debugfs.c b/drivers/char/diag/diag_debugfs.c
index d8b5599..3a1c96b 100644
--- a/drivers/char/diag/diag_debugfs.c
+++ b/drivers/char/diag/diag_debugfs.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
@@ -33,14 +33,14 @@
{
char *buf;
int ret;
-
+ unsigned int buf_size;
buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
if (!buf) {
pr_err("diag: %s, Error allocating memory\n", __func__);
return -ENOMEM;
}
-
- ret = scnprintf(buf, DEBUG_BUF_SIZE,
+ buf_size = ksize(buf);
+ ret = scnprintf(buf, buf_size,
"modem ch: 0x%x\n"
"lpass ch: 0x%x\n"
"riva ch: 0x%x\n"
@@ -181,7 +181,7 @@
driver->real_time_mode);
#ifdef CONFIG_DIAG_OVER_USB
- ret += scnprintf(buf+ret, DEBUG_BUF_SIZE,
+ ret += scnprintf(buf+ret, buf_size-ret,
"usb_connected: %d\n",
driver->usb_connected);
#endif
@@ -198,7 +198,8 @@
unsigned int bytes_remaining, bytes_written = 0;
unsigned int bytes_in_buf = 0, i = 0;
struct diag_dci_data_info *temp_data = dci_data_smd;
- int buf_size = (DEBUG_BUF_SIZE < count) ? DEBUG_BUF_SIZE : count;
+ unsigned int buf_size;
+ buf_size = (DEBUG_BUF_SIZE < count) ? DEBUG_BUF_SIZE : count;
if (diag_dbgfs_dci_finished) {
diag_dbgfs_dci_finished = 0;
@@ -211,6 +212,7 @@
return -ENOMEM;
}
+ buf_size = ksize(buf);
bytes_remaining = buf_size;
if (diag_dbgfs_dci_data_index == 0) {
@@ -287,6 +289,7 @@
{
char *buf;
int ret;
+ unsigned int buf_size;
buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
if (!buf) {
@@ -294,7 +297,8 @@
return -ENOMEM;
}
- ret = scnprintf(buf, DEBUG_BUF_SIZE,
+ buf_size = ksize(buf);
+ ret = scnprintf(buf, buf_size,
"Pending status for work_stucts:\n"
"diag_drain_work: %d\n"
"Modem data diag_read_smd_work: %d\n"
@@ -342,7 +346,7 @@
diag_notify_update_smd_work)));
#ifdef CONFIG_DIAG_OVER_USB
- ret += scnprintf(buf+ret, DEBUG_BUF_SIZE,
+ ret += scnprintf(buf+ret, buf_size-ret,
"diag_proc_hdlc_work: %d\n"
"diag_read_work: %d\n",
work_pending(&(driver->diag_proc_hdlc_work)),
@@ -363,7 +367,8 @@
unsigned int bytes_remaining;
unsigned int bytes_in_buffer = 0;
unsigned int bytes_written;
- int buf_size = (DEBUG_BUF_SIZE < count) ? DEBUG_BUF_SIZE : count;
+ unsigned int buf_size;
+ buf_size = (DEBUG_BUF_SIZE < count) ? DEBUG_BUF_SIZE : count;
if (diag_dbgfs_table_index >= diag_max_reg) {
/* Done. Reset to prepare for future requests */
@@ -376,7 +381,7 @@
pr_err("diag: %s, Error allocating memory\n", __func__);
return -ENOMEM;
}
-
+ buf_size = ksize(buf);
bytes_remaining = buf_size;
if (diag_dbgfs_table_index == 0) {
@@ -385,6 +390,7 @@
"WCNSS: %d, APPS: %d\n",
MODEM_DATA, LPASS_DATA, WCNSS_DATA, APPS_DATA);
bytes_in_buffer += bytes_written;
+ bytes_remaining -= bytes_written;
}
for (i = diag_dbgfs_table_index; i < diag_max_reg; i++) {
@@ -428,14 +434,15 @@
{
char *buf = NULL;
int ret = 0, i = 0;
-
+ unsigned int buf_size;
buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
if (ZERO_OR_NULL_PTR(buf)) {
pr_err("diag: %s, Error allocating memory\n", __func__);
return -ENOMEM;
}
+ buf_size = ksize(buf);
- ret = scnprintf(buf, DEBUG_BUF_SIZE,
+ ret = scnprintf(buf, buf_size,
"POOL_TYPE_COPY: [0x%p : 0x%p] count = %d\n"
"POOL_TYPE_HDLC: [0x%p : 0x%p] count = %d\n"
"POOL_TYPE_USER: [0x%p : 0x%p] count = %d\n"
@@ -456,7 +463,7 @@
for (i = 0; i < MAX_HSIC_CH; i++) {
if (!diag_hsic[i].hsic_inited)
continue;
- ret += scnprintf(buf+ret, DEBUG_BUF_SIZE-ret,
+ ret += scnprintf(buf+ret, buf_size-ret,
"POOL_TYPE_HSIC_%d: [0x%p : 0x%p] count = %d\n",
i+1,
diag_hsic[i].diag_hsic_pool,
@@ -467,7 +474,7 @@
for (i = 0; i < MAX_HSIC_CH; i++) {
if (!diag_hsic[i].hsic_inited)
continue;
- ret += scnprintf(buf+ret, DEBUG_BUF_SIZE-ret,
+ ret += scnprintf(buf+ret, buf_size-ret,
"POOL_TYPE_HSIC_%d_WRITE: [0x%p : 0x%p] count = %d\n",
i+1,
diag_hsic[i].diag_hsic_write_pool,
@@ -486,6 +493,7 @@
{
char *buf = NULL;
int ret = 0;
+ unsigned int buf_size;
buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
if (ZERO_OR_NULL_PTR(buf)) {
@@ -493,7 +501,8 @@
return -ENOMEM;
}
- ret = scnprintf(buf, DEBUG_BUF_SIZE,
+ buf_size = ksize(buf);
+ ret = scnprintf(buf, buf_size,
"POOL_TYPE_COPY: [0x%p : 0x%p] count = %d\n"
"POOL_TYPE_HDLC: [0x%p : 0x%p] count = %d\n"
"POOL_TYPE_USER: [0x%p : 0x%p] count = %d\n"
@@ -528,10 +537,12 @@
unsigned int bytes_remaining;
unsigned int bytes_in_buffer = 0;
unsigned int bytes_written;
- int buf_size = (DEBUG_BUF_SIZE < count) ? DEBUG_BUF_SIZE : count;
+ unsigned int buf_size;
int bytes_hsic_inited = 45;
int bytes_hsic_not_inited = 410;
+ buf_size = (DEBUG_BUF_SIZE < count) ? DEBUG_BUF_SIZE : count;
+
if (diag_dbgfs_finished) {
diag_dbgfs_finished = 0;
return 0;
@@ -543,6 +554,7 @@
return -ENOMEM;
}
+ buf_size = ksize(buf);
bytes_remaining = buf_size;
/* Only one smux for now */
@@ -584,8 +596,8 @@
"in_busy_hsic_write: %d\n"
"count_hsic_pool: %d\n"
"count_hsic_write_pool: %d\n"
- "diag_hsic_pool: %x\n"
- "diag_hsic_write_pool: %x\n"
+ "diag_hsic_pool: %p\n"
+ "diag_hsic_write_pool: %p\n"
"HSIC write_len: %d\n"
"num_hsic_buf_tbl_entries: %d\n"
"HSIC usb_connected: %d\n"
@@ -602,8 +614,8 @@
diag_hsic[i].in_busy_hsic_write,
diag_hsic[i].count_hsic_pool,
diag_hsic[i].count_hsic_write_pool,
- (unsigned int)diag_hsic[i].diag_hsic_pool,
- (unsigned int)diag_hsic[i].diag_hsic_write_pool,
+ diag_hsic[i].diag_hsic_pool,
+ diag_hsic[i].diag_hsic_write_pool,
diag_bridge[i].write_len,
diag_hsic[i].num_hsic_buf_tbl_entries,
diag_bridge[i].usb_connected,
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 0e27693..0e475c9 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -342,6 +342,9 @@
else if (subsys_id == 0x32 && cmd_code_hi >= 0x03 &&
cmd_code_lo <= 0x03)
return 1;
+ else if (subsys_id == 0x57 && cmd_code_hi >= 0x0E &&
+ cmd_code_lo <= 0x0E)
+ return 1;
}
return 0;
}
@@ -476,8 +479,8 @@
for (i = 0; i < diag_hsic[index].poolsize_hsic_write; i++) {
if (hsic_buf_tbl[i].length > 0) {
- pr_debug("diag: HSIC copy to user, i: %d, buf: %x, len: %d\n",
- i, (unsigned int)hsic_buf_tbl[i].buf,
+ pr_debug("diag: HSIC copy to user, i: %d, buf: %p, len: %d\n",
+ i, hsic_buf_tbl[i].buf,
hsic_buf_tbl[i].length);
num_data++;
@@ -940,6 +943,8 @@
for (i = 0; i < MAX_DCI_CLIENTS; i++) {
if (driver->dci_client_tbl[i].client == NULL) {
driver->dci_client_tbl[i].client = current;
+ driver->dci_client_tbl[i].client_id =
+ driver->dci_client_id;
driver->dci_client_tbl[i].list =
dci_params->list;
driver->dci_client_tbl[i].signal_type =
@@ -1040,7 +1045,7 @@
sizeof(struct diag_dci_health_stats)))
return -EFAULT;
mutex_lock(&dci_health_mutex);
- i = diag_dci_find_client_index(current->tgid);
+ i = diag_dci_find_client_index_health(stats.client_id);
if (i != DCI_CLIENT_INDEX_INVALID) {
dci_params = &(driver->dci_client_tbl[i]);
stats.dropped_logs = dci_params->dropped_logs;
@@ -1195,10 +1200,9 @@
for (i = 0; i < driver->buf_tbl_size; i++) {
if (driver->buf_tbl[i].length > 0) {
#ifdef DIAG_DEBUG
- pr_debug("diag: WRITING the buf address "
- "and length is %x , %d\n", (unsigned int)
- (driver->buf_tbl[i].buf),
- driver->buf_tbl[i].length);
+ pr_debug("diag: WRITING the buf address and length is %p , %d\n",
+ driver->buf_tbl[i].buf,
+ driver->buf_tbl[i].length);
#endif
num_data++;
/* Copy the length of data being passed */
@@ -1219,10 +1223,9 @@
ret += driver->buf_tbl[i].length;
drop:
#ifdef DIAG_DEBUG
- pr_debug("diag: DEQUEUE buf address and"
- " length is %x,%d\n", (unsigned int)
- (driver->buf_tbl[i].buf), driver->
- buf_tbl[i].length);
+ pr_debug("diag: DEQUEUE buf address and length is %p, %d\n",
+ driver->buf_tbl[i].buf,
+ driver->buf_tbl[i].length);
#endif
diagmem_free(driver, (unsigned char *)
(driver->buf_tbl[i].buf), POOL_TYPE_HDLC);
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 395bea0..028f045 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -825,10 +825,9 @@
driver->buf_tbl[i].length =
driver->used;
#ifdef DIAG_DEBUG
- pr_debug("diag: ENQUEUE buf ptr"
- " and length is %x , %d\n",
- (unsigned int)(driver->buf_
- tbl[i].buf), driver->buf_tbl[i].length);
+ pr_debug("diag: ENQUEUE buf ptr and length is %p , %d\n",
+ driver->buf_tbl[i].buf,
+ driver->buf_tbl[i].length);
#endif
break;
}
@@ -860,9 +859,9 @@
if (foundIndex == -1)
err = -1;
else
- pr_debug("diag: ENQUEUE HSIC buf ptr and length is %x , %d, ch %d\n",
- (unsigned int)buf,
- diag_bridge[index].write_len, index);
+ pr_debug("diag: ENQUEUE HSIC buf ptr and length is %p , %d, ch %d\n",
+ buf, diag_bridge[index].write_len,
+ index);
}
#endif
for (i = 0; i < driver->num_clients; i++)
diff --git a/drivers/cpufreq/cpu-boost.c b/drivers/cpufreq/cpu-boost.c
index 8cd5ef9..f20510d 100644
--- a/drivers/cpufreq/cpu-boost.c
+++ b/drivers/cpufreq/cpu-boost.c
@@ -1,5 +1,5 @@
/*
- * 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
@@ -17,6 +17,7 @@
#include <linux/init.h>
#include <linux/notifier.h>
#include <linux/cpufreq.h>
+#include <linux/cpu.h>
#include <linux/sched.h>
#include <linux/jiffies.h>
#include <linux/kthread.h>
@@ -62,6 +63,12 @@
* The CPUFREQ_ADJUST notifier is used to override the current policy min to
* make sure policy min >= boost_min. The cpufreq framework then does the job
* of enforcing the new policy.
+ *
+ * The sync kthread needs to run on the CPU in question to avoid deadlocks in
+ * the wake up code. Achieve this by binding the thread to the respective
+ * CPU. But a CPU going offline unbinds threads from that CPU. So, set it up
+ * again each time the CPU comes back up. We can use CPUFREQ_START to figure
+ * out a CPU is coming online instead of registering for hotplug notifiers.
*/
static int boost_adjust_notify(struct notifier_block *nb, unsigned long val, void *data)
{
@@ -72,22 +79,27 @@
unsigned int ib_min = s->input_boost_min;
unsigned int min;
- if (val != CPUFREQ_ADJUST)
- return NOTIFY_OK;
+ switch (val) {
+ case CPUFREQ_ADJUST:
+ if (!b_min && !ib_min)
+ break;
- if (!b_min && !ib_min)
- return NOTIFY_OK;
+ min = max(b_min, ib_min);
- min = max(b_min, ib_min);
+ pr_debug("CPU%u policy min before boost: %u kHz\n",
+ cpu, policy->min);
+ pr_debug("CPU%u boost min: %u kHz\n", cpu, min);
- pr_debug("CPU%u policy min before boost: %u kHz\n",
- cpu, policy->min);
- pr_debug("CPU%u boost min: %u kHz\n", cpu, min);
+ cpufreq_verify_within_limits(policy, min, UINT_MAX);
- cpufreq_verify_within_limits(policy, min, UINT_MAX);
+ pr_debug("CPU%u policy min after boost: %u kHz\n",
+ cpu, policy->min);
+ break;
- pr_debug("CPU%u policy min after boost: %u kHz\n",
- cpu, policy->min);
+ case CPUFREQ_START:
+ set_cpus_allowed(s->thread, *cpumask_of(cpu));
+ break;
+ }
return NOTIFY_OK;
}
@@ -165,9 +177,15 @@
s->boost_min = src_policy.cur;
}
/* Force policy re-evaluation to trigger adjust notifier. */
- cpufreq_update_policy(dest_cpu);
- queue_delayed_work_on(s->cpu, cpu_boost_wq,
- &s->boost_rem, msecs_to_jiffies(boost_ms));
+ get_online_cpus();
+ if (cpu_online(dest_cpu)) {
+ cpufreq_update_policy(dest_cpu);
+ queue_delayed_work_on(dest_cpu, cpu_boost_wq,
+ &s->boost_rem, msecs_to_jiffies(boost_ms));
+ } else {
+ s->boost_min = 0;
+ }
+ put_online_cpus();
}
return 0;
@@ -202,6 +220,7 @@
struct cpu_sync *i_sync_info;
struct cpufreq_policy policy;
+ get_online_cpus();
for_each_online_cpu(i) {
i_sync_info = &per_cpu(sync_info, i);
@@ -218,6 +237,7 @@
&i_sync_info->input_boost_rem,
msecs_to_jiffies(input_boost_ms));
}
+ put_online_cpus();
}
static void cpuboost_input_event(struct input_handle *handle,
@@ -332,6 +352,7 @@
INIT_DELAYED_WORK(&s->input_boost_rem, do_input_boost_rem);
s->thread = kthread_run(boost_mig_sync_thread, (void *)cpu,
"boost_sync/%d", cpu);
+ set_cpus_allowed(s->thread, *cpumask_of(cpu));
}
atomic_notifier_chain_register(&migration_notifier_head,
&boost_migration_nb);
diff --git a/drivers/crypto/msm/qce.h b/drivers/crypto/msm/qce.h
index 4250202..73438d0 100644
--- a/drivers/crypto/msm/qce.h
+++ b/drivers/crypto/msm/qce.h
@@ -122,6 +122,7 @@
bool use_sw_ahash_algo;
bool use_sw_hmac_algo;
bool use_sw_aes_ccm_algo;
+ bool clk_mgmt_sus_res;
};
/* Sha operation parameters */
diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c
index 63e18c9..62fd948 100644
--- a/drivers/crypto/msm/qce50.c
+++ b/drivers/crypto/msm/qce50.c
@@ -73,6 +73,7 @@
int is_shared; /* CE HW is shared */
bool support_cmd_dscr;
bool support_hw_key;
+ bool support_clk_mgmt_sus_res;
void __iomem *iobase; /* Virtual io base of CE HW */
unsigned int phy_iobase; /* Physical io base of CE HW */
@@ -5104,6 +5105,8 @@
pce_dev->use_sw_aes_ccm_algo =
of_property_read_bool((&pdev->dev)->of_node,
"qcom,use-sw-aes-ccm-algo");
+ pce_dev->support_clk_mgmt_sus_res = of_property_read_bool(
+ (&pdev->dev)->of_node, "qcom,clk-mgmt-sus-res");
if (of_property_read_u32((&pdev->dev)->of_node,
"qcom,bam-pipe-pair",
@@ -5432,6 +5435,7 @@
ce_support->is_shared = (pce_dev->is_shared == 1) ? true : false;
ce_support->hw_key = pce_dev->support_hw_key;
ce_support->aes_ccm = true;
+ ce_support->clk_mgmt_sus_res = pce_dev->support_clk_mgmt_sus_res;
if (pce_dev->ce_sps.minor_version)
ce_support->aligned_only = false;
else
diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c
index 974606d..3aebaf0 100644
--- a/drivers/crypto/msm/qcrypto.c
+++ b/drivers/crypto/msm/qcrypto.c
@@ -4092,6 +4092,100 @@
};
+static int _qcrypto_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int ret = 0;
+ struct crypto_engine *pengine;
+ struct crypto_priv *cp;
+
+ pengine = platform_get_drvdata(pdev);
+ if (!pengine)
+ return -EINVAL;
+
+ /*
+ * Check if this platform supports clock management in suspend/resume
+ * If not, just simply return 0.
+ */
+ 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;
+ }
+ }
+
+ mutex_unlock(&cp->engine_lock);
+ return 0;
+}
+
+static int _qcrypto_resume(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct crypto_engine *pengine;
+ struct crypto_priv *cp;
+
+ pengine = platform_get_drvdata(pdev);
+
+ if (!pengine)
+ return -EINVAL;
+
+ cp = pengine->pcp;
+ 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));
+ }
+
+ mutex_unlock(&cp->engine_lock);
+
+ return 0;
+}
+
static struct of_device_id qcrypto_match[] = {
{ .compatible = "qcom,qcrypto",
},
@@ -4101,6 +4195,8 @@
static struct platform_driver _qualcomm_crypto = {
.probe = _qcrypto_probe,
.remove = _qcrypto_remove,
+ .suspend = _qcrypto_suspend,
+ .resume = _qcrypto_resume,
.driver = {
.owner = THIS_MODULE,
.name = "qcrypto",
diff --git a/drivers/gpio/qpnp-pin.c b/drivers/gpio/qpnp-pin.c
index 2753082..8d934df 100644
--- a/drivers/gpio/qpnp-pin.c
+++ b/drivers/gpio/qpnp-pin.c
@@ -55,11 +55,13 @@
#define Q_GPIO_SUBTYPE_GPIOC_8CH 0xD
/* mpp peripheral type and subtype values */
-#define Q_MPP_TYPE 0x11
-#define Q_MPP_SUBTYPE_4CH_NO_ANA_OUT 0x3
-#define Q_MPP_SUBTYPE_4CH_NO_SINK 0x5
-#define Q_MPP_SUBTYPE_4CH_FULL_FUNC 0x7
-#define Q_MPP_SUBTYPE_8CH_FULL_FUNC 0xF
+#define Q_MPP_TYPE 0x11
+#define Q_MPP_SUBTYPE_4CH_NO_ANA_OUT 0x3
+#define Q_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT 0x4
+#define Q_MPP_SUBTYPE_4CH_NO_SINK 0x5
+#define Q_MPP_SUBTYPE_ULT_4CH_NO_SINK 0x6
+#define Q_MPP_SUBTYPE_4CH_FULL_FUNC 0x7
+#define Q_MPP_SUBTYPE_8CH_FULL_FUNC 0xF
/* control register base address offsets */
#define Q_REG_MODE_CTL 0x40
@@ -235,22 +237,29 @@
static int qpnp_pin_check_config(enum qpnp_pin_param_type idx,
struct qpnp_pin_spec *q_spec, uint32_t val)
{
+ u8 subtype = q_spec->subtype;
+
switch (idx) {
case Q_PIN_CFG_MODE:
if (q_spec->type == Q_GPIO_TYPE &&
val >= QPNP_PIN_GPIO_MODE_INVALID)
return -EINVAL;
- else if (q_spec->type == Q_MPP_TYPE &&
- val >= QPNP_PIN_MPP_MODE_INVALID)
+ else if (q_spec->type == Q_MPP_TYPE) {
+ if (val >= QPNP_PIN_MPP_MODE_INVALID)
return -EINVAL;
+ if ((subtype == Q_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT ||
+ subtype == Q_MPP_SUBTYPE_ULT_4CH_NO_SINK) &&
+ (val == QPNP_PIN_MODE_BIDIR))
+ return -ENXIO;
+ }
break;
case Q_PIN_CFG_OUTPUT_TYPE:
if (q_spec->type != Q_GPIO_TYPE)
return -ENXIO;
if ((val == QPNP_PIN_OUT_BUF_OPEN_DRAIN_NMOS ||
val == QPNP_PIN_OUT_BUF_OPEN_DRAIN_PMOS) &&
- (q_spec->subtype == Q_GPIO_SUBTYPE_GPIOC_4CH ||
- (q_spec->subtype == Q_GPIO_SUBTYPE_GPIOC_8CH)))
+ (subtype == Q_GPIO_SUBTYPE_GPIOC_4CH ||
+ (subtype == Q_GPIO_SUBTYPE_GPIOC_8CH)))
return -EINVAL;
else if (val >= QPNP_PIN_OUT_BUF_INVALID)
return -EINVAL;
@@ -263,22 +272,28 @@
if (q_spec->type == Q_GPIO_TYPE &&
val >= QPNP_PIN_GPIO_PULL_INVALID)
return -EINVAL;
- if (q_spec->type == Q_MPP_TYPE &&
- val >= QPNP_PIN_MPP_PULL_INVALID)
- return -EINVAL;
+ if (q_spec->type == Q_MPP_TYPE) {
+ if (val >= QPNP_PIN_MPP_PULL_INVALID)
+ return -EINVAL;
+ if (subtype == Q_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT ||
+ subtype == Q_MPP_SUBTYPE_ULT_4CH_NO_SINK)
+ return -ENXIO;
+ }
break;
case Q_PIN_CFG_VIN_SEL:
if (val >= QPNP_PIN_VIN_8CH_INVALID)
return -EINVAL;
else if (val >= QPNP_PIN_VIN_4CH_INVALID) {
if (q_spec->type == Q_GPIO_TYPE &&
- (q_spec->subtype == Q_GPIO_SUBTYPE_GPIO_4CH ||
- q_spec->subtype == Q_GPIO_SUBTYPE_GPIOC_4CH))
+ (subtype == Q_GPIO_SUBTYPE_GPIO_4CH ||
+ subtype == Q_GPIO_SUBTYPE_GPIOC_4CH))
return -EINVAL;
if (q_spec->type == Q_MPP_TYPE &&
- (q_spec->subtype == Q_MPP_SUBTYPE_4CH_NO_ANA_OUT ||
- q_spec->subtype == Q_MPP_SUBTYPE_4CH_NO_SINK ||
- q_spec->subtype == Q_MPP_SUBTYPE_4CH_FULL_FUNC))
+ (subtype == Q_MPP_SUBTYPE_4CH_NO_ANA_OUT ||
+ subtype == Q_MPP_SUBTYPE_4CH_NO_SINK ||
+ subtype == Q_MPP_SUBTYPE_4CH_FULL_FUNC ||
+ subtype == Q_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT ||
+ subtype == Q_MPP_SUBTYPE_ULT_4CH_NO_SINK))
return -EINVAL;
}
break;
@@ -304,7 +319,8 @@
case Q_PIN_CFG_AOUT_REF:
if (q_spec->type != Q_MPP_TYPE)
return -ENXIO;
- if (q_spec->subtype == Q_MPP_SUBTYPE_4CH_NO_ANA_OUT)
+ if (subtype == Q_MPP_SUBTYPE_4CH_NO_ANA_OUT ||
+ subtype == Q_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT)
return -ENXIO;
if (val >= QPNP_PIN_AOUT_REF_INVALID)
return -EINVAL;
@@ -318,7 +334,8 @@
case Q_PIN_CFG_CS_OUT:
if (q_spec->type != Q_MPP_TYPE)
return -ENXIO;
- if (q_spec->subtype == Q_MPP_SUBTYPE_4CH_NO_SINK)
+ if (subtype == Q_MPP_SUBTYPE_4CH_NO_SINK ||
+ subtype == Q_MPP_SUBTYPE_ULT_4CH_NO_SINK)
return -ENXIO;
if (val >= QPNP_PIN_CS_OUT_INVALID)
return -EINVAL;
@@ -410,9 +427,11 @@
else if (q_spec->type == Q_MPP_TYPE)
switch (q_spec->subtype) {
case Q_MPP_SUBTYPE_4CH_NO_SINK:
+ case Q_MPP_SUBTYPE_ULT_4CH_NO_SINK:
q_spec->num_ctl_regs = 12;
break;
case Q_MPP_SUBTYPE_4CH_NO_ANA_OUT:
+ case Q_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT:
case Q_MPP_SUBTYPE_4CH_FULL_FUNC:
case Q_MPP_SUBTYPE_8CH_FULL_FUNC:
q_spec->num_ctl_regs = 13;
@@ -1144,7 +1163,9 @@
else if (q_spec->type == Q_MPP_TYPE)
switch (q_spec->subtype) {
case Q_MPP_SUBTYPE_4CH_NO_ANA_OUT:
+ case Q_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT:
case Q_MPP_SUBTYPE_4CH_NO_SINK:
+ case Q_MPP_SUBTYPE_ULT_4CH_NO_SINK:
case Q_MPP_SUBTYPE_4CH_FULL_FUNC:
case Q_MPP_SUBTYPE_8CH_FULL_FUNC:
return 1;
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 10eaf4e..7717829 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -1205,9 +1205,11 @@
* after the command has been retired
*/
if (result)
- kgsl_mmu_disable_clk_on_ts(&device->mmu, 0, false);
+ kgsl_mmu_disable_clk(&device->mmu,
+ KGSL_IOMMU_CONTEXT_USER);
else
- kgsl_mmu_disable_clk_on_ts(&device->mmu, rb->global_ts, true);
+ kgsl_mmu_disable_clk_on_ts(&device->mmu, rb->global_ts,
+ KGSL_IOMMU_CONTEXT_USER);
done:
kgsl_context_put(context);
@@ -2336,15 +2338,33 @@
const char *buf, size_t count)
{
struct adreno_device *adreno_dev = _get_adreno_dev(dev);
- int ret;
+ int ret = 0;
+ unsigned int policy = 0;
if (adreno_dev == NULL)
return 0;
mutex_lock(&adreno_dev->dev.mutex);
- ret = _ft_sysfs_store(buf, count, &adreno_dev->ft_pf_policy);
+
+ /* MMU option changed call function to reset MMU options */
+ if (count != _ft_sysfs_store(buf, count, &policy))
+ ret = -EINVAL;
+
+ if (!ret) {
+ policy &= (KGSL_FT_PAGEFAULT_INT_ENABLE |
+ KGSL_FT_PAGEFAULT_GPUHALT_ENABLE |
+ KGSL_FT_PAGEFAULT_LOG_ONE_PER_PAGE |
+ KGSL_FT_PAGEFAULT_LOG_ONE_PER_INT);
+ ret = kgsl_mmu_set_pagefault_policy(&(adreno_dev->dev.mmu),
+ adreno_dev->ft_pf_policy);
+ if (!ret)
+ adreno_dev->ft_pf_policy = policy;
+ }
mutex_unlock(&adreno_dev->dev.mutex);
- return ret;
+ if (!ret)
+ return count;
+ else
+ return 0;
}
/**
@@ -2635,12 +2655,56 @@
return status;
}
-static int adreno_setproperty(struct kgsl_device *device,
+static int adreno_set_constraint(struct kgsl_device *device,
+ struct kgsl_context *context,
+ struct kgsl_device_constraint *constraint)
+{
+ int status = 0;
+
+ switch (constraint->type) {
+ case KGSL_CONSTRAINT_PWRLEVEL: {
+ struct kgsl_device_constraint_pwrlevel pwr;
+
+ if (constraint->size != sizeof(pwr)) {
+ status = -EINVAL;
+ break;
+ }
+
+ if (copy_from_user(&pwr,
+ (void __user *)constraint->data,
+ sizeof(pwr))) {
+ status = -EFAULT;
+ break;
+ }
+ if (pwr.level >= KGSL_CONSTRAINT_PWR_MAXLEVELS) {
+ status = -EINVAL;
+ break;
+ }
+
+ context->pwr_constraint.type =
+ KGSL_CONSTRAINT_PWRLEVEL;
+ context->pwr_constraint.sub_type = pwr.level;
+ }
+ break;
+ case KGSL_CONSTRAINT_NONE:
+ context->pwr_constraint.type = KGSL_CONSTRAINT_NONE;
+ break;
+
+ default:
+ status = -EINVAL;
+ break;
+ }
+
+ return status;
+}
+
+static int adreno_setproperty(struct kgsl_device_private *dev_priv,
enum kgsl_property_type type,
void *value,
unsigned int sizebytes)
{
int status = -EINVAL;
+ struct kgsl_device *device = dev_priv->device;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
switch (type) {
@@ -2678,6 +2742,28 @@
status = 0;
}
break;
+ case KGSL_PROP_PWR_CONSTRAINT: {
+ struct kgsl_device_constraint constraint;
+ struct kgsl_context *context;
+
+ if (sizebytes != sizeof(constraint))
+ break;
+
+ if (copy_from_user(&constraint, value,
+ sizeof(constraint))) {
+ status = -EFAULT;
+ break;
+ }
+
+ context = kgsl_context_get_owner(dev_priv,
+ constraint.context_id);
+ if (context == NULL)
+ break;
+ status = adreno_set_constraint(device, context,
+ &constraint);
+ kgsl_context_put(context);
+ }
+ break;
default:
break;
}
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 800caf1..0f1e01d 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -20,6 +20,8 @@
#include "kgsl_iommu.h"
#include <mach/ocmem.h>
+#include "a3xx_reg.h"
+
#define DEVICE_3D_NAME "kgsl-3d"
#define DEVICE_3D0_NAME "kgsl-3d0"
@@ -895,4 +897,48 @@
return result;
}
+/*
+ * adreno_set_protected_registers() - Protect the specified range of registers
+ * from being accessed by the GPU
+ * @device: pointer to the KGSL device
+ * @index: Pointer to the index of the protect mode register to write to
+ * @reg: Starting dword register to write
+ * @mask_len: Size of the mask to protect (# of registers = 2 ** mask_len)
+ *
+ * Add the range of registers to the list of protected mode registers that will
+ * cause an exception if the GPU accesses them. There are 16 available
+ * protected mode registers. Index is used to specify which register to write
+ * to - the intent is to call this function multiple times with the same index
+ * pointer for each range and the registers will be magically programmed in
+ * incremental fashion
+ */
+static inline void adreno_set_protected_registers(struct kgsl_device *device,
+ unsigned int *index, unsigned int reg, int mask_len)
+{
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ unsigned int val;
+
+ /* This function is only for adreno A3XX and beyond */
+ BUG_ON(adreno_is_a2xx(adreno_dev));
+
+ /* There are only 16 registers available */
+ BUG_ON(*index >= 16);
+
+ val = 0x60000000 | ((mask_len & 0x1F) << 24) | ((reg << 2) & 0x1FFFF);
+
+ /*
+ * Write the protection range to the next available protection
+ * register
+ */
+
+ kgsl_regwrite(device, A3XX_CP_PROTECT_REG_0 + *index, val);
+ *index = *index + 1;
+}
+
+#ifdef CONFIG_DEBUG_FS
+void adreno_debugfs_init(struct kgsl_device *device);
+#else
+static inline void adreno_debugfs_init(struct kgsl_device *device) { }
+#endif
+
#endif /*__ADRENO_H */
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index 3d4c66a..eed11c3 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -3029,8 +3029,8 @@
GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000001);
GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000000);
GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000000);
- /* Protected mode control - turned off for A3XX */
- GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000000);
+ /* Enable protected mode */
+ GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x20000000);
GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000000);
GSL_RB_WRITE(rb->device, cmds, cmds_gpu, 0x00000000);
@@ -3092,9 +3092,16 @@
case A3XX_INT_CP_HW_FAULT:
err = "ringbuffer hardware fault";
break;
- case A3XX_INT_CP_REG_PROTECT_FAULT:
- err = "ringbuffer protected mode error interrupt";
- break;
+ case A3XX_INT_CP_REG_PROTECT_FAULT: {
+ unsigned int reg;
+ kgsl_regread(device, A3XX_CP_PROTECT_STATUS, ®);
+
+ KGSL_DRV_CRIT(device,
+ "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;
@@ -4115,29 +4122,35 @@
*/
static void a3xx_protect_init(struct kgsl_device *device)
{
+ int index = 0;
+
/* enable access protection to privileged registers */
kgsl_regwrite(device, A3XX_CP_PROTECT_CTRL, 0x00000007);
/* RBBM registers */
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_0, 0x63000040);
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_1, 0x62000080);
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_2, 0x600000CC);
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_3, 0x60000108);
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_4, 0x64000140);
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_5, 0x66000400);
+ adreno_set_protected_registers(device, &index, 0x18, 0);
+ adreno_set_protected_registers(device, &index, 0x20, 2);
+ adreno_set_protected_registers(device, &index, 0x33, 0);
+ adreno_set_protected_registers(device, &index, 0x42, 0);
+ adreno_set_protected_registers(device, &index, 0x50, 4);
+ adreno_set_protected_registers(device, &index, 0x63, 0);
+ adreno_set_protected_registers(device, &index, 0x100, 4);
/* CP registers */
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_6, 0x65000700);
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_7, 0x610007D8);
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_8, 0x620007E0);
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_9, 0x61001178);
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_A, 0x64001180);
+ adreno_set_protected_registers(device, &index, 0x1C0, 5);
+ adreno_set_protected_registers(device, &index, 0x1F6, 1);
+ adreno_set_protected_registers(device, &index, 0x1F8, 2);
+ adreno_set_protected_registers(device, &index, 0x45E, 2);
+ adreno_set_protected_registers(device, &index, 0x460, 4);
/* RB registers */
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_B, 0x60003300);
+ adreno_set_protected_registers(device, &index, 0xCC0, 0);
/* VBIF registers */
- kgsl_regwrite(device, A3XX_CP_PROTECT_REG_C, 0x6B00C000);
+ adreno_set_protected_registers(device, &index, 0x3000, 6);
+
+ /* SMMU registers */
+ adreno_set_protected_registers(device, &index, 0x4000, 14);
}
static void a3xx_start(struct adreno_device *adreno_dev)
diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c
index 1b5573f..4db045a 100644
--- a/drivers/gpu/msm/adreno_drawctxt.c
+++ b/drivers/gpu/msm/adreno_drawctxt.c
@@ -271,15 +271,13 @@
}
static int _check_global_timestamp(struct kgsl_device *device,
- unsigned int timestamp)
+ struct adreno_context *drawctxt, unsigned int timestamp)
{
- int ret;
+ /* Stop waiting if the context is invalidated */
+ if (drawctxt->state == ADRENO_CONTEXT_STATE_INVALID)
+ return 1;
- mutex_lock(&device->mutex);
- ret = kgsl_check_timestamp(device, NULL, timestamp);
- mutex_unlock(&device->mutex);
-
- return ret;
+ return kgsl_check_timestamp(device, NULL, timestamp);
}
int adreno_drawctxt_wait_global(struct adreno_device *adreno_dev,
@@ -288,7 +286,7 @@
{
struct kgsl_device *device = &adreno_dev->dev;
struct adreno_context *drawctxt = ADRENO_CONTEXT(context);
- int ret;
+ int ret = 0;
/* Needs to hold the device mutex */
BUG_ON(!mutex_is_locked(&device->mutex));
@@ -298,6 +296,15 @@
goto done;
}
+ /*
+ * If the context is invalid then return immediately - we may end up
+ * waiting for a timestamp that will never come
+ */
+ if (drawctxt->state == ADRENO_CONTEXT_STATE_INVALID) {
+ kgsl_context_put(context);
+ goto done;
+ }
+
trace_adreno_drawctxt_wait_start(KGSL_MEMSTORE_GLOBAL, timestamp);
ret = kgsl_add_event(device, KGSL_MEMSTORE_GLOBAL, timestamp,
@@ -311,7 +318,7 @@
if (timeout) {
ret = (int) wait_event_timeout(drawctxt->waiting,
- _check_global_timestamp(device, timestamp),
+ _check_global_timestamp(device, drawctxt, timestamp),
msecs_to_jiffies(timeout));
if (ret == 0)
@@ -320,7 +327,7 @@
ret = 0;
} else {
wait_event(drawctxt->waiting,
- _check_global_timestamp(device, timestamp));
+ _check_global_timestamp(device, drawctxt, timestamp));
}
mutex_lock(&device->mutex);
@@ -425,7 +432,8 @@
KGSL_CONTEXT_PER_CONTEXT_TS |
KGSL_CONTEXT_USER_GENERATED_TS |
KGSL_CONTEXT_NO_FAULT_TOLERANCE |
- KGSL_CONTEXT_TYPE_MASK);
+ KGSL_CONTEXT_TYPE_MASK |
+ KGSL_CONTEXT_PWR_CONSTRAINT);
/* Always enable per-context timestamps */
drawctxt->base.flags |= KGSL_CONTEXT_PER_CONTEXT_TS;
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index b23d3ab..9aae497 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2007-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
@@ -696,7 +696,7 @@
/* Add space for the power on shader fixup if we need it */
if (flags & KGSL_CMD_FLAGS_PWRON_FIXUP)
- total_sizedwords += 5;
+ total_sizedwords += 9;
ringcmds = adreno_ringbuffer_allocspace(rb, drawctxt, total_sizedwords);
@@ -718,6 +718,11 @@
}
if (flags & KGSL_CMD_FLAGS_PWRON_FIXUP) {
+ /* Disable protected mode for the fixup */
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
+ cp_type3_packet(CP_SET_PROTECTED_MODE, 1));
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, 0);
+
GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, cp_nop_packet(1));
GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
KGSL_PWRON_FIXUP_IDENTIFIER);
@@ -727,6 +732,11 @@
adreno_dev->pwron_fixup.gpuaddr);
GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
adreno_dev->pwron_fixup_dwords);
+
+ /* Re-enable protected mode */
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
+ cp_type3_packet(CP_SET_PROTECTED_MODE, 1));
+ GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, 1);
}
/* Add any IB required for profiling if it is enabled */
@@ -1132,6 +1142,65 @@
return ret;
}
+unsigned int adreno_ringbuffer_get_constraint(struct kgsl_device *device,
+ struct kgsl_context *context)
+{
+ unsigned int pwrlevel = device->pwrctrl.active_pwrlevel;
+
+ switch (context->pwr_constraint.type) {
+ case KGSL_CONSTRAINT_PWRLEVEL: {
+ switch (context->pwr_constraint.sub_type) {
+ case KGSL_CONSTRAINT_PWR_MAX:
+ pwrlevel = device->pwrctrl.max_pwrlevel;
+ break;
+ case KGSL_CONSTRAINT_PWR_MIN:
+ pwrlevel = device->pwrctrl.min_pwrlevel;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+
+ }
+
+ return pwrlevel;
+}
+
+void adreno_ringbuffer_set_constraint(struct kgsl_device *device,
+ struct kgsl_cmdbatch *cmdbatch)
+{
+ unsigned int constraint;
+ struct kgsl_context *context = cmdbatch->context;
+ /*
+ * Check if the context has a constraint and constraint flags are
+ * set.
+ */
+ if (context->pwr_constraint.type &&
+ ((context->flags & KGSL_CONTEXT_PWR_CONSTRAINT) ||
+ (cmdbatch->flags & KGSL_CONTEXT_PWR_CONSTRAINT))) {
+
+ constraint = adreno_ringbuffer_get_constraint(device, context);
+
+ /*
+ * If a constraint is already set, set a new
+ * constraint only if it is faster
+ */
+ if ((device->pwrctrl.constraint.type ==
+ KGSL_CONSTRAINT_NONE) || (constraint <
+ device->pwrctrl.constraint.hint.pwrlevel.level)) {
+
+ kgsl_pwrctrl_pwrlevel_change(device, constraint);
+ device->pwrctrl.constraint.type =
+ context->pwr_constraint.type;
+ device->pwrctrl.constraint.hint.
+ pwrlevel.level = constraint;
+ }
+
+ }
+
+}
+
/* adreno_rindbuffer_submitcmd - submit userspace IBs to the GPU */
int adreno_ringbuffer_submitcmd(struct adreno_device *adreno_dev,
struct kgsl_cmdbatch *cmdbatch)
@@ -1242,6 +1311,9 @@
test_bit(ADRENO_DEVICE_PWRON_FIXUP, &adreno_dev->priv))
flags |= KGSL_CMD_FLAGS_PWRON_FIXUP;
+ /* Set the constraints before adding to ringbuffer */
+ adreno_ringbuffer_set_constraint(device, cmdbatch);
+
ret = adreno_ringbuffer_addcmds(&adreno_dev->ringbuffer,
drawctxt,
flags,
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 1743e4a..11b7429 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -1379,8 +1379,8 @@
if (dev_priv->device->ftbl->setproperty)
result = dev_priv->device->ftbl->setproperty(
- dev_priv->device, param->type,
- param->value, param->sizebytes);
+ dev_priv, param->type, param->value,
+ param->sizebytes);
return result;
}
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index 7fc6fae..7d009ce 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2007-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
@@ -129,7 +129,7 @@
void (*drawctxt_destroy) (struct kgsl_context *context);
long (*ioctl) (struct kgsl_device_private *dev_priv,
unsigned int cmd, void *data);
- int (*setproperty) (struct kgsl_device *device,
+ int (*setproperty) (struct kgsl_device_private *dev_priv,
enum kgsl_property_type type, void *value,
unsigned int sizebytes);
int (*postmortem_dump) (struct kgsl_device *device, int manual);
@@ -349,6 +349,7 @@
* @pagefault_ts: global timestamp of the pagefault, if KGSL_CONTEXT_PAGEFAULT
* is set.
* @flags: flags from userspace controlling the behavior of this context
+ * @pwr_constraint: power constraint from userspace for this context
*/
struct kgsl_context {
struct kref refcount;
@@ -366,6 +367,7 @@
struct list_head events_list;
unsigned int pagefault_ts;
unsigned int flags;
+ struct kgsl_pwr_constraint pwr_constraint;
};
/**
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 8f65705..2af8d27 100755
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -461,7 +461,7 @@
* Disables iommu clocks
* Return - void
*/
-static void kgsl_iommu_disable_clk(struct kgsl_mmu *mmu)
+static void kgsl_iommu_disable_clk(struct kgsl_mmu *mmu, int ctx_id)
{
struct kgsl_iommu *iommu = mmu->priv;
struct msm_iommu_drvdata *iommu_drvdata;
@@ -470,8 +470,15 @@
for (i = 0; i < iommu->unit_count; i++) {
struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
for (j = 0; j < iommu_unit->dev_count; j++) {
- if (!iommu_unit->dev[j].clk_enabled)
+ if (ctx_id != iommu_unit->dev[j].ctx_id)
continue;
+ atomic_dec(&iommu_unit->dev[j].clk_enable_count);
+ BUG_ON(
+ atomic_read(&iommu_unit->dev[j].clk_enable_count) < 0);
+ /*
+ * the clock calls have a refcount so call them on every
+ * enable/disable call
+ */
iommu_drvdata = dev_get_drvdata(
iommu_unit->dev[j].dev->parent);
if (iommu_drvdata->aclk)
@@ -479,7 +486,6 @@
if (iommu_drvdata->clk)
clk_disable_unprepare(iommu_drvdata->clk);
clk_disable_unprepare(iommu_drvdata->pclk);
- iommu_unit->dev[j].clk_enabled = false;
}
}
}
@@ -500,32 +506,14 @@
unsigned int id, unsigned int ts,
u32 type)
{
- struct kgsl_mmu *mmu = data;
- struct kgsl_iommu *iommu = mmu->priv;
+ struct kgsl_iommu_disable_clk_param *param = data;
- if (!iommu->clk_event_queued) {
- if (0 > timestamp_cmp(ts, iommu->iommu_last_cmd_ts))
- KGSL_DRV_ERR(device,
- "IOMMU disable clock event being cancelled, "
- "iommu_last_cmd_ts: %x, retired ts: %x\n",
- iommu->iommu_last_cmd_ts, ts);
- return;
- }
-
- if (0 <= timestamp_cmp(ts, iommu->iommu_last_cmd_ts)) {
- kgsl_iommu_disable_clk(mmu);
- iommu->clk_event_queued = false;
- } else {
- /* add new event to fire when ts is reached, this can happen
- * if we queued an event and someone requested the clocks to
- * be disbaled on a later timestamp */
- if (kgsl_add_event(device, id, iommu->iommu_last_cmd_ts,
- kgsl_iommu_clk_disable_event, mmu, mmu)) {
- KGSL_DRV_ERR(device,
- "Failed to add IOMMU disable clk event\n");
- iommu->clk_event_queued = false;
- }
- }
+ if ((0 <= timestamp_cmp(ts, param->ts)) ||
+ (KGSL_EVENT_CANCELLED == type))
+ kgsl_iommu_disable_clk(param->mmu, param->ctx_id);
+ else
+ /* something went wrong with the event handling mechanism */
+ BUG_ON(1);
}
/*
@@ -535,6 +523,8 @@
* @ts_valid - Indicates whether ts parameter is valid, if this parameter
* is false then it means that the calling function wants to disable the
* IOMMU clocks immediately without waiting for any timestamp
+ * @ctx_id: Context id of the IOMMU context for which clocks are to be
+ * turned off
*
* Creates an event to disable the IOMMU clocks on timestamp and if event
* already exists then updates the timestamp of disabling the IOMMU clocks
@@ -543,28 +533,25 @@
* Return - void
*/
static void
-kgsl_iommu_disable_clk_on_ts(struct kgsl_mmu *mmu, unsigned int ts,
- bool ts_valid)
+kgsl_iommu_disable_clk_on_ts(struct kgsl_mmu *mmu,
+ unsigned int ts, int ctx_id)
{
- struct kgsl_iommu *iommu = mmu->priv;
+ struct kgsl_iommu_disable_clk_param *param;
- if (iommu->clk_event_queued) {
- if (ts_valid && (0 <
- timestamp_cmp(ts, iommu->iommu_last_cmd_ts)))
- iommu->iommu_last_cmd_ts = ts;
- } else {
- if (ts_valid) {
- iommu->iommu_last_cmd_ts = ts;
- iommu->clk_event_queued = true;
- if (kgsl_add_event(mmu->device, KGSL_MEMSTORE_GLOBAL,
- ts, kgsl_iommu_clk_disable_event, mmu, mmu)) {
- KGSL_DRV_ERR(mmu->device,
- "Failed to add IOMMU disable clk event\n");
- iommu->clk_event_queued = false;
- }
- } else {
- kgsl_iommu_disable_clk(mmu);
- }
+ param = kzalloc(sizeof(*param), GFP_KERNEL);
+ if (!param) {
+ KGSL_CORE_ERR("kzalloc(%d) failed\n", sizeof(*param));
+ return;
+ }
+ param->mmu = mmu;
+ param->ctx_id = ctx_id;
+ param->ts = ts;
+
+ if (kgsl_add_event(mmu->device, KGSL_MEMSTORE_GLOBAL,
+ ts, kgsl_iommu_clk_disable_event, param, mmu)) {
+ KGSL_DRV_ERR(mmu->device,
+ "Failed to add IOMMU disable clk event\n");
+ kfree(param);
}
}
@@ -587,8 +574,7 @@
for (i = 0; i < iommu->unit_count; i++) {
struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
for (j = 0; j < iommu_unit->dev_count; j++) {
- if (iommu_unit->dev[j].clk_enabled ||
- ctx_id != iommu_unit->dev[j].ctx_id)
+ if (ctx_id != iommu_unit->dev[j].ctx_id)
continue;
iommu_drvdata =
dev_get_drvdata(iommu_unit->dev[j].dev->parent);
@@ -614,12 +600,25 @@
goto done;
}
}
- iommu_unit->dev[j].clk_enabled = true;
+ atomic_inc(&iommu_unit->dev[j].clk_enable_count);
}
}
done:
- if (ret)
- kgsl_iommu_disable_clk(mmu);
+ if (ret) {
+ struct kgsl_iommu_unit *iommu_unit;
+ if (iommu->unit_count == i)
+ i--;
+ iommu_unit = &iommu->iommu_units[i];
+ do {
+ for (j--; j >= 0; j--)
+ kgsl_iommu_disable_clk(mmu, ctx_id);
+ i--;
+ if (i >= 0) {
+ iommu_unit = &iommu->iommu_units[i];
+ j = iommu_unit->dev_count;
+ }
+ } while (i >= 0);
+ }
return ret;
}
@@ -848,6 +847,9 @@
ret = -EINVAL;
goto done;
}
+ atomic_set(
+ &(iommu_unit->dev[iommu_unit->dev_count].clk_enable_count),
+ 0);
iommu_unit->dev[iommu_unit->dev_count].dev =
msm_iommu_get_ctx(data->iommu_ctxs[i].iommu_ctx_name);
@@ -1674,6 +1676,7 @@
}
status = kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_PRIV);
if (status) {
+ kgsl_iommu_disable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
KGSL_CORE_ERR("clk enable failed\n");
goto done;
}
@@ -1728,14 +1731,11 @@
KGSL_IOMMU_SETSTATE_NOP_OFFSET,
cp_nop_packet(1), sizeof(unsigned int));
- kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
+ kgsl_iommu_disable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
+ kgsl_iommu_disable_clk(mmu, KGSL_IOMMU_CONTEXT_PRIV);
mmu->flags |= KGSL_FLAGS_STARTED;
done:
- if (status) {
- kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
- kgsl_detach_pagetable_iommu_domain(mmu);
- }
return status;
}
@@ -1858,6 +1858,7 @@
iommu_unit,
iommu_unit->dev[j].ctx_id,
FSR, 0);
+ kgsl_iommu_disable_clk(mmu, j);
_iommu_unlock(iommu);
iommu_unit->dev[j].fault = 0;
}
@@ -1870,7 +1871,6 @@
static void kgsl_iommu_stop(struct kgsl_mmu *mmu)
{
- struct kgsl_iommu *iommu = mmu->priv;
/*
* stop device mmu
*
@@ -1886,9 +1886,7 @@
kgsl_iommu_pagefault_resume(mmu);
}
/* switch off MMU clocks and cancel any events it has queued */
- iommu->clk_event_queued = false;
kgsl_cancel_events(mmu->device, mmu);
- kgsl_iommu_disable_clk(mmu);
}
static int kgsl_iommu_close(struct kgsl_mmu *mmu)
@@ -1938,10 +1936,10 @@
return 0;
/* Return the current pt base by reading IOMMU pt_base register */
kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
- pt_base = KGSL_IOMMU_GET_CTX_REG(iommu, (&iommu->iommu_units[0]),
- KGSL_IOMMU_CONTEXT_USER,
- TTBR0);
- kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
+ pt_base = KGSL_IOMMU_GET_CTX_REG(iommu,
+ (&iommu->iommu_units[0]),
+ KGSL_IOMMU_CONTEXT_USER, TTBR0);
+ kgsl_iommu_disable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
return pt_base & KGSL_IOMMU_CTX_TTBR0_ADDR_MASK;
}
@@ -1969,7 +1967,6 @@
phys_addr_t pt_val;
ret = kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
-
if (ret) {
KGSL_DRV_ERR(mmu->device, "Failed to enable iommu clocks\n");
return ret;
@@ -2056,7 +2053,7 @@
_iommu_unlock(iommu);
/* Disable smmu clock */
- kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
+ kgsl_iommu_disable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
return ret;
}
@@ -2103,6 +2100,72 @@
return iommu->unit_count;
}
+/*
+ * kgsl_iommu_set_pf_policy() - Set the pagefault policy for IOMMU
+ * @mmu: Pointer to mmu structure
+ * @pf_policy: The pagefault polict to set
+ *
+ * Check if the new policy indicated by pf_policy is same as current
+ * policy, if same then return else set the policy
+ */
+static int kgsl_iommu_set_pf_policy(struct kgsl_mmu *mmu,
+ unsigned int pf_policy)
+{
+ int i, j;
+ struct kgsl_iommu *iommu = mmu->priv;
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(mmu->device);
+ int ret = 0;
+ unsigned int sctlr_val;
+
+ if ((adreno_dev->ft_pf_policy & KGSL_FT_PAGEFAULT_GPUHALT_ENABLE) ==
+ (pf_policy & KGSL_FT_PAGEFAULT_GPUHALT_ENABLE))
+ return ret;
+ if (msm_soc_version_supports_iommu_v0())
+ return ret;
+
+ ret = kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
+
+ if (ret) {
+ KGSL_DRV_ERR(mmu->device, "Failed to enable iommu clocks\n");
+ return ret;
+ }
+ ret = kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_PRIV);
+
+ if (ret) {
+ KGSL_DRV_ERR(mmu->device, "Failed to enable iommu clocks\n");
+ kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
+ return ret;
+ }
+ /* Need to idle device before changing options */
+ ret = mmu->device->ftbl->idle(mmu->device);
+ if (ret) {
+ kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
+ return ret;
+ }
+
+ for (i = 0; i < iommu->unit_count; i++) {
+ struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
+ for (j = 0; j < iommu_unit->dev_count; j++) {
+ sctlr_val = KGSL_IOMMU_GET_CTX_REG(iommu,
+ iommu_unit,
+ iommu_unit->dev[j].ctx_id,
+ SCTLR);
+ if (pf_policy & KGSL_FT_PAGEFAULT_GPUHALT_ENABLE)
+ sctlr_val &= ~(0x1 <<
+ KGSL_IOMMU_SCTLR_HUPCF_SHIFT);
+ else
+ sctlr_val |= (0x1 <<
+ KGSL_IOMMU_SCTLR_HUPCF_SHIFT);
+ KGSL_IOMMU_SET_CTX_REG(iommu,
+ iommu_unit,
+ iommu_unit->dev[j].ctx_id,
+ SCTLR, sctlr_val);
+ }
+ }
+ kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
+ return ret;
+}
+
struct kgsl_mmu_ops iommu_ops = {
.mmu_init = kgsl_iommu_init,
.mmu_close = kgsl_iommu_close,
@@ -2128,6 +2191,7 @@
.mmu_cleanup_pt = NULL,
.mmu_sync_lock = kgsl_iommu_sync_lock,
.mmu_sync_unlock = kgsl_iommu_sync_unlock,
+ .mmu_set_pf_policy = kgsl_iommu_set_pf_policy,
};
struct kgsl_mmu_pt_ops iommu_pt_ops = {
diff --git a/drivers/gpu/msm/kgsl_iommu.h b/drivers/gpu/msm/kgsl_iommu.h
index 7dca40e..3878107 100644
--- a/drivers/gpu/msm/kgsl_iommu.h
+++ b/drivers/gpu/msm/kgsl_iommu.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
@@ -143,6 +143,7 @@
* are on, else the clocks are off
* fault: Flag when set indicates that this iommu device has caused a page
* fault
+ * @clk_enable_count: The ref count of clock enable calls
*/
struct kgsl_iommu_device {
struct device *dev;
@@ -152,6 +153,7 @@
bool clk_enabled;
struct kgsl_device *kgsldev;
int fault;
+ atomic_t clk_enable_count;
};
/*
@@ -182,10 +184,6 @@
* iommu contexts owned by graphics cores
* @unit_count: Number of IOMMU units that are available for this
* instance of the IOMMU driver
- * @iommu_last_cmd_ts: The timestamp of last command submitted that
- * aceeses iommu registers
- * @clk_event_queued: Indicates whether an event to disable clocks
- * is already queued or not
* @device: Pointer to kgsl device
* @ctx_offset: The context offset to be added to base address when
* accessing IOMMU registers
@@ -201,8 +199,6 @@
struct kgsl_iommu {
struct kgsl_iommu_unit iommu_units[KGSL_IOMMU_MAX_UNITS];
unsigned int unit_count;
- unsigned int iommu_last_cmd_ts;
- bool clk_event_queued;
struct kgsl_device *device;
unsigned int ctx_offset;
struct kgsl_iommu_register_list *iommu_reg_list;
@@ -222,4 +218,18 @@
struct kgsl_iommu *iommu;
};
+/*
+ * struct kgsl_iommu_disable_clk_param - Parameter struct for disble clk event
+ * @mmu: The mmu pointer
+ * @rb_level: the rb level in which the timestamp of the event belongs to
+ * @ctx_id: The IOMMU context whose clock is to be turned off
+ * @ts: Timestamp on which clock is to be disabled
+ */
+struct kgsl_iommu_disable_clk_param {
+ struct kgsl_mmu *mmu;
+ int rb_level;
+ int ctx_id;
+ unsigned int ts;
+};
+
#endif
diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h
index 8bc9962..5e3386a 100644
--- a/drivers/gpu/msm/kgsl_mmu.h
+++ b/drivers/gpu/msm/kgsl_mmu.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2007-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
@@ -144,11 +144,12 @@
void (*mmu_pagefault_resume)
(struct kgsl_mmu *mmu);
void (*mmu_disable_clk_on_ts)
- (struct kgsl_mmu *mmu, uint32_t ts, bool ts_valid);
+ (struct kgsl_mmu *mmu,
+ uint32_t ts, int ctx_id);
int (*mmu_enable_clk)
(struct kgsl_mmu *mmu, int ctx_id);
void (*mmu_disable_clk)
- (struct kgsl_mmu *mmu);
+ (struct kgsl_mmu *mmu, int ctx_id);
phys_addr_t (*mmu_get_default_ttbr0)(struct kgsl_mmu *mmu,
unsigned int unit_id,
enum kgsl_iommu_context_id ctx_id);
@@ -173,6 +174,7 @@
unsigned int (*mmu_sync_unlock)
(struct kgsl_mmu *mmu, unsigned int *cmds);
int (*mmu_hw_halt_supported)(struct kgsl_mmu *mmu, int iommu_unit_num);
+ int (*mmu_set_pf_policy)(struct kgsl_mmu *mmu, unsigned int pf_policy);
};
struct kgsl_mmu_pt_ops {
@@ -326,17 +328,18 @@
return 0;
}
-static inline void kgsl_mmu_disable_clk(struct kgsl_mmu *mmu)
+static inline void kgsl_mmu_disable_clk(struct kgsl_mmu *mmu, int ctx_id)
{
if (mmu->mmu_ops && mmu->mmu_ops->mmu_disable_clk)
- mmu->mmu_ops->mmu_disable_clk(mmu);
+ mmu->mmu_ops->mmu_disable_clk(mmu, ctx_id);
}
static inline void kgsl_mmu_disable_clk_on_ts(struct kgsl_mmu *mmu,
- unsigned int ts, bool ts_valid)
+ unsigned int ts,
+ int ctx_id)
{
if (mmu->mmu_ops && mmu->mmu_ops->mmu_disable_clk_on_ts)
- mmu->mmu_ops->mmu_disable_clk_on_ts(mmu, ts, ts_valid);
+ mmu->mmu_ops->mmu_disable_clk_on_ts(mmu, ts, ctx_id);
}
static inline unsigned int kgsl_mmu_get_int_mask(void)
@@ -477,4 +480,13 @@
return 0;
}
+static inline int kgsl_mmu_set_pagefault_policy(struct kgsl_mmu *mmu,
+ unsigned int pf_policy)
+{
+ if (mmu->mmu_ops && mmu->mmu_ops->mmu_set_pf_policy)
+ return mmu->mmu_ops->mmu_set_pf_policy(mmu, pf_policy);
+ else
+ return 0;
+}
+
#endif /* __KGSL_MMU_H */
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 96ff1b8..c00e978 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -1430,8 +1430,6 @@
break;
}
- kgsl_mmu_disable_clk_on_ts(&device->mmu, 0, false);
-
return 0;
}
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h
index 6ec809d..c811b78 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.h
+++ b/drivers/gpu/msm/kgsl_pwrctrl.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
@@ -27,6 +27,9 @@
#define KGSL_MAX_CLKS 6
+/* Only two supported levels, min & max */
+#define KGSL_CONSTRAINT_PWR_MAXLEVELS 2
+
struct platform_device;
struct kgsl_clk_stats {
@@ -40,6 +43,16 @@
unsigned int elapsed_old;
};
+struct kgsl_pwr_constraint {
+ unsigned int type;
+ unsigned int sub_type;
+ union {
+ struct {
+ unsigned int level;
+ } pwrlevel;
+ } hint;
+};
+
/**
* struct kgsl_pwrctrl - Power control settings for a KGSL device
* @interrupt_num - The interrupt number for the device
@@ -67,6 +80,7 @@
* @bus_control - true if the bus calculation is independent
* @bus_index - default bus index into the bus_ib table
* @bus_ib - the set of unique ib requests needed for the bus calculation
+ * @constraint - currently active power constraint
*/
struct kgsl_pwrctrl {
@@ -98,6 +112,7 @@
int bus_mod;
unsigned int bus_index[KGSL_MAX_PWRLEVELS];
uint64_t bus_ib[KGSL_MAX_PWRLEVELS];
+ struct kgsl_pwr_constraint constraint;
};
void kgsl_pwrctrl_irq(struct kgsl_device *device, int state);
diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c
index 52732cf..acee4d4 100644
--- a/drivers/gpu/msm/kgsl_pwrscale.c
+++ b/drivers/gpu/msm/kgsl_pwrscale.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
@@ -228,6 +228,10 @@
}
kgsl_pwrctrl_pwrlevel_change(device, level);
+
+ /*Invalidate the constraint set */
+ pwr->constraint.type = KGSL_CONSTRAINT_NONE;
+
*freq = kgsl_pwrctrl_active_freq(pwr);
mutex_unlock(&device->mutex);
diff --git a/drivers/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c
index 32c94dc..067a887 100644
--- a/drivers/hwmon/qpnp-adc-current.c
+++ b/drivers/hwmon/qpnp-adc-current.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
@@ -129,6 +129,12 @@
#define QPNP_BIT_SHIFT_8 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 QPNP_IADC1_USR_TRIM2_ADC_FULLSCALE1_CONST 127
+#define QPNP_IADC_RSENSE_DEFAULT_VALUE 7800000
+#define QPNP_IADC_RSENSE_DEFAULT_TYPEB_GF 9000000
+#define QPNP_IADC_RSENSE_DEFAULT_TYPEB_SMIC 9700000
struct qpnp_iadc_comp {
bool ext_rsense;
@@ -143,6 +149,7 @@
struct qpnp_adc_drv *adc;
int32_t rsense;
bool external_rsense;
+ bool default_internal_rsense;
struct device *iadc_hwmon;
struct list_head list;
int64_t die_temp;
@@ -153,11 +160,20 @@
struct work_struct trigger_completion_work;
bool skip_auto_calibrations;
bool iadc_poll_eoc;
+ u16 batt_id_trim_cnst_rds;
+ int rds_trim_default_type;
+ bool rds_trim_default_check;
+ int32_t rsense_workaround_value;
struct sensor_device_attribute sens_attr[0];
};
LIST_HEAD(qpnp_iadc_device_list);
+enum qpnp_iadc_rsense_rds_workaround {
+ QPNP_IADC_RDS_DEFAULT_TYPEA,
+ QPNP_IADC_RDS_DEFAULT_TYPEB,
+};
+
static int32_t qpnp_iadc_read_reg(struct qpnp_iadc_chip *iadc,
uint32_t reg, u8 *data)
{
@@ -573,6 +589,67 @@
}
EXPORT_SYMBOL(qpnp_iadc_comp_result);
+static int qpnp_iadc_rds_trim_update_check(struct qpnp_iadc_chip *iadc)
+{
+ int rc = 0;
+ u8 trim2_val = 0, smbb_batt_trm_data = 0;
+
+ if (!iadc->rds_trim_default_check) {
+ pr_debug("No internal rds trim check needed\n");
+ return 0;
+ }
+
+ rc = qpnp_iadc_read_reg(iadc, QPNP_IADC_NOMINAL_RSENSE, &trim2_val);
+ if (rc < 0) {
+ pr_err("qpnp adc trim2_fullscale1 reg read failed %d\n", rc);
+ return rc;
+ }
+
+ rc = spmi_ext_register_readl(iadc->adc->spmi->ctrl, iadc->adc->slave,
+ iadc->batt_id_trim_cnst_rds, &smbb_batt_trm_data, 1);
+ if (rc < 0) {
+ pr_err("batt_id trim_cnst rds reg read failed %d\n", rc);
+ return rc;
+ }
+
+ 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) &&
+ (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 (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) &&
+ (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) &&
+ (trim2_val ==
+ QPNP_IADC1_USR_TRIM2_ADC_FULLSCALE1_CONST)) {
+ if (iadc->iadc_comp.id == COMP_ID_GF) {
+ iadc->rsense_workaround_value =
+ QPNP_IADC_RSENSE_DEFAULT_TYPEB_GF;
+ iadc->default_internal_rsense = true;
+ } else if (iadc->iadc_comp.id == COMP_ID_SMIC) {
+ iadc->rsense_workaround_value =
+ QPNP_IADC_RSENSE_DEFAULT_TYPEB_SMIC;
+ iadc->default_internal_rsense = true;
+ }
+ }
+ }
+
+ return 0;
+}
+
static int32_t qpnp_iadc_comp_info(struct qpnp_iadc_chip *iadc)
{
int rc = 0;
@@ -948,6 +1025,11 @@
return rc;
}
+ if (iadc->default_internal_rsense) {
+ *rsense = iadc->rsense_workaround_value;
+ return rc;
+ }
+
rc = qpnp_iadc_read_reg(iadc, QPNP_IADC_NOMINAL_RSENSE, &rslt_rsense);
if (rc < 0) {
pr_err("qpnp adc rsense read failed with %d\n", rc);
@@ -968,6 +1050,8 @@
*rsense = QPNP_IADC_INTERNAL_RSENSE_N_OHMS_FACTOR +
(rslt_rsense * QPNP_IADC_RSENSE_LSB_N_OHMS_PER_BIT);
+ pr_debug("rsense value is %d\n", *rsense);
+
return rc;
}
EXPORT_SYMBOL(qpnp_iadc_get_rsense);
@@ -1258,6 +1342,7 @@
struct qpnp_adc_drv *adc_qpnp;
struct device_node *node = spmi->dev.of_node;
struct device_node *child;
+ struct resource *res;
int rc, count_adc_channel_list = 0, i = 0;
for_each_child_of_node(node, child)
@@ -1292,6 +1377,22 @@
return rc;
}
+ res = spmi_get_resource_byname(spmi, NULL, IORESOURCE_MEM,
+ "batt-id-trim-cnst-rds");
+ if (!res) {
+ dev_err(&spmi->dev, "failed to read batt_id trim register\n");
+ return -EINVAL;
+ }
+ iadc->batt_id_trim_cnst_rds = res->start;
+ rc = of_property_read_u32(node, "qcom,use-default-rds-trim",
+ &iadc->rds_trim_default_type);
+ if (rc)
+ pr_debug("No trim workaround needed\n");
+ else {
+ pr_debug("Use internal RDS trim workaround\n");
+ iadc->rds_trim_default_check = true;
+ }
+
iadc->vadc_dev = qpnp_get_vadc(&spmi->dev, "iadc");
if (IS_ERR(iadc->vadc_dev)) {
rc = PTR_ERR(iadc->vadc_dev);
@@ -1345,6 +1446,12 @@
goto fail;
}
+ rc = qpnp_iadc_rds_trim_update_check(iadc);
+ if (rc) {
+ dev_err(&spmi->dev, "Rds trim update failed!\n");
+ goto fail;
+ }
+
dev_set_drvdata(&spmi->dev, iadc);
list_add(&iadc->list, &qpnp_iadc_device_list);
rc = qpnp_iadc_calibrate_for_trim(iadc, true);
diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index d9a332e..1704105 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -22,6 +22,7 @@
#include <linux/err.h>
#include <linux/init.h>
#include <linux/i2c.h>
+#include <linux/i2c/i2c-qup.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
@@ -1853,11 +1854,18 @@
};
/* QUP may be needed to bring up other drivers */
-static int __init
-qup_i2c_init_driver(void)
+int __init qup_i2c_init_driver(void)
{
+ static bool initialized;
+
+ if (initialized)
+ return 0;
+ else
+ initialized = true;
+
return platform_driver_register(&qup_i2c_driver);
}
+EXPORT_SYMBOL(qup_i2c_init_driver);
arch_initcall(qup_i2c_init_driver);
static void __exit qup_i2c_exit_driver(void)
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 716ea0d..0c22694 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -73,6 +73,16 @@
To compile this driver as a module, choose M here: the
module will be called bma150.
+config INPUT_HBTP_INPUT
+ tristate "HBTP input driver support"
+ help
+ Say Y to enable HBTP input driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called hbtp_input.
+
+endif
+
config INPUT_PCSPKR
tristate "PC Speaker support"
depends on PCSPKR_PLATFORM
@@ -714,4 +724,3 @@
To compile this driver as a module, choose M here: the
module will be called CM36283.
-endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index c927e0e..c9a65fc 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -26,6 +26,7 @@
obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o
obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o
obj-$(CONFIG_INPUT_GPIO) += gpio_event.o gpio_matrix.o gpio_input.o gpio_output.o gpio_axis.o
+obj-$(CONFIG_INPUT_HBTP_INPUT) += hbtp_input.o
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o
obj-$(CONFIG_INPUT_KEYCHORD) += keychord.o
diff --git a/drivers/input/misc/hbtp_input.c b/drivers/input/misc/hbtp_input.c
new file mode 100644
index 0000000..83ba652
--- /dev/null
+++ b/drivers/input/misc/hbtp_input.c
@@ -0,0 +1,477 @@
+
+/* 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/poll.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/hbtp_input.h>
+#include <linux/input/mt.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include "../input-compat.h"
+
+#if defined(CONFIG_FB)
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#endif
+
+#define HBTP_INPUT_NAME "hbtp_input"
+#define HBTP_AFE_LOAD_UA 150000
+#define HBTP_AFE_VTG_MIN_UV 2700000
+#define HBTP_AFE_VTG_MAX_UV 3300000
+
+struct hbtp_data {
+ struct platform_device *pdev;
+ struct input_dev *input_dev;
+ s32 count;
+ bool touch_status[HBTP_MAX_FINGER];
+#if defined(CONFIG_FB)
+ struct notifier_block fb_notif;
+#endif
+ struct regulator *vcc_ana;
+};
+
+static struct hbtp_data *hbtp;
+
+#if defined(CONFIG_FB)
+static int fb_notifier_callback(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ int blank;
+ struct fb_event *evdata = data;
+ struct hbtp_data *hbtp_data =
+ container_of(self, struct hbtp_data, fb_notif);
+
+ if (evdata && evdata->data && event == FB_EVENT_BLANK &&
+ hbtp_data && hbtp_data->input_dev) {
+ blank = *(int *)(evdata->data);
+ if (blank == FB_BLANK_UNBLANK)
+ kobject_uevent(&hbtp_data->input_dev->dev.kobj,
+ KOBJ_ONLINE);
+ else if (blank == FB_BLANK_POWERDOWN)
+ kobject_uevent(&hbtp_data->input_dev->dev.kobj,
+ KOBJ_OFFLINE);
+ }
+
+ return 0;
+}
+#endif
+
+static int hbtp_input_open(struct inode *inode, struct file *file)
+{
+ if (hbtp->count) {
+ pr_err("%s is busy\n", HBTP_INPUT_NAME);
+ return -EBUSY;
+ }
+ hbtp->count++;
+
+ return 0;
+}
+
+static int hbtp_input_release(struct inode *inode, struct file *file)
+{
+ if (!hbtp->count) {
+ pr_err("%s wasn't opened\n", HBTP_INPUT_NAME);
+ return -ENOTTY;
+ }
+ hbtp->count--;
+
+ return 0;
+}
+
+static int hbtp_input_create_input_dev(struct hbtp_input_absinfo *absinfo)
+{
+ struct input_dev *input_dev;
+ struct hbtp_input_absinfo *abs;
+ int error;
+ int i;
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ pr_err("%s: input_allocate_device failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ kfree(input_dev->name);
+ input_dev->name = kstrndup(HBTP_INPUT_NAME, sizeof(HBTP_INPUT_NAME),
+ GFP_KERNEL);
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+ __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+ /* For multi touch */
+ input_mt_init_slots(input_dev, HBTP_MAX_FINGER);
+ for (i = 0; i <= ABS_MT_LAST - ABS_MT_FIRST; i++) {
+ abs = absinfo + i;
+ if (abs->active)
+ input_set_abs_params(input_dev, abs->code,
+ abs->minimum, abs->maximum, 0, 0);
+ }
+
+ error = input_register_device(input_dev);
+ if (error) {
+ pr_err("%s: input_register_device failed\n", __func__);
+ goto err_input_reg_dev;
+ }
+
+ hbtp->input_dev = input_dev;
+ return 0;
+
+err_input_reg_dev:
+ input_free_device(input_dev);
+
+ return error;
+}
+
+static int hbtp_input_report_events(struct hbtp_data *hbtp_data,
+ struct hbtp_input_mt *mt_data)
+{
+ int i;
+ struct hbtp_input_touch *tch;
+
+ for (i = 0; i < HBTP_MAX_FINGER; i++) {
+ tch = &(mt_data->touches[i]);
+ if (tch->active || hbtp_data->touch_status[i]) {
+ input_mt_slot(hbtp_data->input_dev, i);
+ input_mt_report_slot_state(hbtp_data->input_dev,
+ MT_TOOL_FINGER, tch->active);
+
+ if (tch->active) {
+ input_report_abs(hbtp_data->input_dev,
+ ABS_MT_TOOL_TYPE,
+ tch->tool);
+ input_report_abs(hbtp_data->input_dev,
+ ABS_MT_TOUCH_MAJOR,
+ tch->major);
+ input_report_abs(hbtp_data->input_dev,
+ ABS_MT_TOUCH_MINOR,
+ tch->minor);
+ input_report_abs(hbtp_data->input_dev,
+ ABS_MT_ORIENTATION,
+ tch->orientation);
+ input_report_abs(hbtp_data->input_dev,
+ ABS_MT_PRESSURE,
+ tch->pressure);
+ input_report_abs(hbtp_data->input_dev,
+ ABS_MT_POSITION_X,
+ tch->x);
+ input_report_abs(hbtp_data->input_dev,
+ ABS_MT_POSITION_Y,
+ tch->y);
+ }
+ hbtp_data->touch_status[i] = tch->active;
+ }
+ }
+
+ input_report_key(hbtp->input_dev, BTN_TOUCH, mt_data->num_touches > 0);
+ input_sync(hbtp->input_dev);
+
+ return 0;
+}
+
+static int reg_set_optimum_mode_check(struct regulator *reg, int load_uA)
+{
+ return (regulator_count_voltages(reg) > 0) ?
+ regulator_set_optimum_mode(reg, load_uA) : 0;
+}
+
+static int hbtp_pdev_power_on(struct hbtp_data *hbtp, bool on)
+{
+ int ret, error;
+
+ if (!hbtp->vcc_ana) {
+ pr_err("%s: regulator is not available\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!on)
+ goto reg_off;
+
+ ret = reg_set_optimum_mode_check(hbtp->vcc_ana, HBTP_AFE_LOAD_UA);
+ if (ret < 0) {
+ pr_err("%s: Regulator vcc_ana set_opt failed rc=%d\n",
+ __func__, ret);
+ return -EINVAL;
+ }
+
+ ret = regulator_enable(hbtp->vcc_ana);
+ if (ret) {
+ pr_err("%s: Regulator vcc_ana enable failed rc=%d\n",
+ __func__, ret);
+ error = -EINVAL;
+ goto error_reg_en_vcc_ana;
+ }
+
+ return 0;
+
+error_reg_en_vcc_ana:
+ reg_set_optimum_mode_check(hbtp->vcc_ana, 0);
+ return error;
+
+reg_off:
+ reg_set_optimum_mode_check(hbtp->vcc_ana, 0);
+ regulator_disable(hbtp->vcc_ana);
+ return 0;
+}
+
+static long hbtp_input_ioctl_handler(struct file *file, unsigned int cmd,
+ unsigned long arg, void __user *p)
+{
+ int error;
+ struct hbtp_input_mt mt_data;
+ struct hbtp_input_absinfo absinfo[ABS_MT_LAST - ABS_MT_FIRST + 1];
+ enum hbtp_afe_power_cmd power_cmd;
+
+ switch (cmd) {
+ case HBTP_SET_ABSPARAM:
+ if (hbtp && hbtp->input_dev) {
+ pr_err("%s: The input device is already created\n",
+ __func__);
+ return 0;
+ }
+
+ if (copy_from_user(absinfo, (void *)arg,
+ sizeof(struct hbtp_input_absinfo) *
+ (ABS_MT_LAST - ABS_MT_FIRST + 1))) {
+ pr_err("%s: Error copying data for ABS param\n",
+ __func__);
+ return -EFAULT;
+ }
+
+ error = hbtp_input_create_input_dev(absinfo);
+ if (error)
+ pr_err("%s, hbtp_input_create_input_dev failed (%d)\n",
+ __func__, error);
+ break;
+
+ case HBTP_SET_TOUCHDATA:
+ if (!hbtp || !hbtp->input_dev) {
+ pr_err("%s: The input device hasn't been created\n",
+ __func__);
+ return -EFAULT;
+ }
+
+ if (copy_from_user(&mt_data, (void *)arg,
+ sizeof(struct hbtp_input_mt))) {
+ pr_err("%s: Error copying data\n", __func__);
+ return -EFAULT;
+ }
+
+ hbtp_input_report_events(hbtp, &mt_data);
+ error = 0;
+ break;
+
+ case HBTP_SET_POWERSTATE:
+ if (!hbtp || !hbtp->input_dev) {
+ pr_err("%s: The input device hasn't been created\n",
+ __func__);
+ return -EFAULT;
+ }
+
+ if (copy_from_user(&power_cmd, (void *)arg,
+ sizeof(enum hbtp_afe_power_cmd))) {
+ pr_err("%s: Error copying data\n", __func__);
+ return -EFAULT;
+ }
+
+ switch (power_cmd) {
+ case HBTP_AFE_POWER_ON:
+ error = hbtp_pdev_power_on(hbtp, true);
+ if (error)
+ pr_err("%s: failed to power on\n", __func__);
+ break;
+ case HBTP_AFE_POWER_OFF:
+ error = hbtp_pdev_power_on(hbtp, false);
+ if (error)
+ pr_err("%s: failed to power off\n", __func__);
+ break;
+ default:
+ pr_err("%s: Unsupported command for power state, %d\n",
+ __func__, power_cmd);
+ return -EINVAL;
+ }
+ break;
+
+ default:
+ pr_err("%s: Unsupported ioctl command %u\n", __func__, cmd);
+ error = -EINVAL;
+ break;
+ }
+
+ return error;
+}
+
+static long hbtp_input_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return hbtp_input_ioctl_handler(file, cmd, arg, (void __user *)arg);
+}
+
+#ifdef CONFIG_COMPAT
+static long hbtp_input_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return hbtp_input_ioctl_handler(file, cmd, arg, compat_ptr(arg));
+}
+#endif
+
+static const struct file_operations hbtp_input_fops = {
+ .owner = THIS_MODULE,
+ .open = hbtp_input_open,
+ .release = hbtp_input_release,
+ .unlocked_ioctl = hbtp_input_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = hbtp_input_compat_ioctl,
+#endif
+};
+
+static struct miscdevice hbtp_input_misc = {
+ .fops = &hbtp_input_fops,
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = HBTP_INPUT_NAME,
+};
+MODULE_ALIAS_MISCDEV(MISC_DYNAMIC_MINOR);
+MODULE_ALIAS("devname:" HBTP_INPUT_NAME);
+
+static int __devinit hbtp_pdev_probe(struct platform_device *pdev)
+{
+ int ret, error;
+ struct regulator *vcc_ana;
+
+ vcc_ana = regulator_get(&pdev->dev, "vcc_ana");
+ if (IS_ERR(vcc_ana)) {
+ ret = PTR_ERR(vcc_ana);
+ pr_err("%s: Regulator get failed vcc_ana rc=%d\n",
+ __func__, ret);
+ return -EINVAL;
+ }
+
+ if (regulator_count_voltages(vcc_ana) > 0) {
+ ret = regulator_set_voltage(vcc_ana,
+ HBTP_AFE_VTG_MIN_UV, HBTP_AFE_VTG_MAX_UV);
+ if (ret) {
+ pr_err("%s: regulator set_vtg failed rc=%d\n",
+ __func__, ret);
+ error = -EINVAL;
+ goto error_set_vtg_vcc_ana;
+ }
+ }
+
+ hbtp->vcc_ana = vcc_ana;
+ hbtp->pdev = pdev;
+ return 0;
+
+error_set_vtg_vcc_ana:
+ regulator_put(vcc_ana);
+
+ return error;
+};
+
+static int __devexit hbtp_pdev_remove(struct platform_device *pdev)
+{
+ if (hbtp->vcc_ana) {
+ hbtp_pdev_power_on(hbtp, false);
+ regulator_put(hbtp->vcc_ana);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id hbtp_match_table[] = {
+ { .compatible = "qcom,hbtp",},
+ { },
+};
+#else
+#define hbtp_match_table NULL
+#endif
+
+static struct platform_driver hbtp_pdev_driver = {
+ .probe = hbtp_pdev_probe,
+ .remove = __devexit_p(hbtp_pdev_remove),
+ .driver = {
+ .name = "hbtp",
+ .owner = THIS_MODULE,
+ .of_match_table = hbtp_match_table,
+ },
+};
+
+static int __init hbtp_init(void)
+{
+ int error;
+
+ hbtp = kzalloc(sizeof(struct hbtp_data), GFP_KERNEL);
+ if (!hbtp)
+ return -ENOMEM;
+
+ error = misc_register(&hbtp_input_misc);
+ if (error) {
+ pr_err("%s: misc_register failed\n", HBTP_INPUT_NAME);
+ goto err_misc_reg;
+ }
+
+#if defined(CONFIG_FB)
+ hbtp->fb_notif.notifier_call = fb_notifier_callback;
+ error = fb_register_client(&hbtp->fb_notif);
+ if (error) {
+ pr_err("%s: Unable to register fb_notifier: %d\n",
+ HBTP_INPUT_NAME, error);
+ goto err_fb_reg;
+ }
+#endif
+
+ error = platform_driver_register(&hbtp_pdev_driver);
+ if (error) {
+ pr_err("Failed to register platform driver: %d\n", error);
+ goto err_platform_drv_reg;
+ }
+
+ return 0;
+
+err_platform_drv_reg:
+#if defined(CONFIG_FB)
+ fb_unregister_client(&hbtp->fb_notif);
+err_fb_reg:
+#endif
+ misc_deregister(&hbtp_input_misc);
+err_misc_reg:
+ kfree(hbtp);
+
+ return error;
+}
+
+static void __exit hbtp_exit(void)
+{
+ misc_deregister(&hbtp_input_misc);
+ if (hbtp->input_dev)
+ input_unregister_device(hbtp->input_dev);
+
+#if defined(CONFIG_FB)
+ fb_unregister_client(&hbtp->fb_notif);
+#endif
+
+ platform_driver_unregister(&hbtp_pdev_driver);
+
+ kfree(hbtp);
+}
+
+MODULE_DESCRIPTION("Kernel driver to support host based touch processing");
+MODULE_LICENSE("GPLv2");
+
+module_init(hbtp_init);
+module_exit(hbtp_exit);
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.c b/drivers/input/touchscreen/gt9xx/gt9xx.c
index 912d87c..91d787f 100644
--- a/drivers/input/touchscreen/gt9xx/gt9xx.c
+++ b/drivers/input/touchscreen/gt9xx/gt9xx.c
@@ -1542,15 +1542,14 @@
const char *buf, size_t size)
{
struct goodix_ts_data *ts = dev_get_drvdata(dev);
- unsigned long val;
+ unsigned int val;
int ret;
if (size > 2)
return -EINVAL;
- ret = kstrtoul(buf, 10, &val);
- if (ret != 0)
- return ret;
+ if (sscanf(buf, "%u", &val) != 1);
+ return -EINVAL;
if (ts->gtp_is_suspend) {
dev_err(&ts->client->dev,
@@ -1576,16 +1575,60 @@
return size;
}
+static ssize_t gtp_force_fw_upgrade_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct goodix_ts_data *ts = dev_get_drvdata(dev);
+ unsigned int val;
+ int ret;
+
+ if (size > 2)
+ return -EINVAL;
+
+ if (sscanf(buf, "%u", &val) != 1);
+ return -EINVAL;
+
+ if (ts->gtp_is_suspend) {
+ dev_err(&ts->client->dev,
+ "Can't start fw upgrade. Device is in suspend state.");
+ return -EBUSY;
+ }
+
+ mutex_lock(&ts->input_dev->mutex);
+ if (!ts->fw_loading && val) {
+ disable_irq(ts->client->irq);
+ ts->fw_loading = true;
+ ts->force_update = true;
+ if (config_enabled(CONFIG_GT9XX_TOUCHPANEL_UPDATE)) {
+ ret = gup_update_proc(NULL);
+ if (ret == FAIL)
+ dev_err(&ts->client->dev,
+ "Fail to force update GTP firmware.\n");
+ }
+ ts->force_update = false;
+ ts->fw_loading = false;
+ enable_irq(ts->client->irq);
+ }
+ mutex_unlock(&ts->input_dev->mutex);
+
+ return size;
+}
+
static DEVICE_ATTR(fw_name, (S_IRUGO | S_IWUSR | S_IWGRP),
gtp_fw_name_show,
gtp_fw_name_store);
static DEVICE_ATTR(fw_upgrade, (S_IRUGO | S_IWUSR | S_IWGRP),
gtp_fw_upgrade_show,
gtp_fw_upgrade_store);
+static DEVICE_ATTR(force_fw_upgrade, (S_IRUGO | S_IWUSR | S_IWGRP),
+ gtp_fw_upgrade_show,
+ gtp_force_fw_upgrade_store);
static struct attribute *gtp_attrs[] = {
&dev_attr_fw_name.attr,
&dev_attr_fw_upgrade.attr,
+ &dev_attr_force_fw_upgrade.attr,
NULL
};
@@ -1802,8 +1845,8 @@
pdata->i2c_pull_up = of_property_read_bool(np,
"goodix,i2c-pull-up");
- pdata->no_force_update = of_property_read_bool(np,
- "goodix,no-force-update");
+ pdata->force_update = of_property_read_bool(np,
+ "goodix,force-update");
pdata->enable_power_off = of_property_read_bool(np,
"goodix,enable-power-off");
@@ -1971,6 +2014,9 @@
goto exit_power_off;
}
+ if (pdata->force_update)
+ ts->force_update = true;
+
if (pdata->fw_name)
strlcpy(ts->fw_name, pdata->fw_name,
strlen(pdata->fw_name) + 1);
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.h b/drivers/input/touchscreen/gt9xx/gt9xx.h
index 7a1af23..0ba6895 100644
--- a/drivers/input/touchscreen/gt9xx/gt9xx.h
+++ b/drivers/input/touchscreen/gt9xx/gt9xx.h
@@ -56,7 +56,7 @@
u32 panel_miny;
u32 panel_maxx;
u32 panel_maxy;
- bool no_force_update;
+ bool force_update;
bool i2c_pull_up;
bool enable_power_off;
size_t config_data_len[GOODIX_MAX_CFG_GROUP];
@@ -94,6 +94,7 @@
bool power_on;
struct mutex lock;
bool fw_loading;
+ bool force_update;
struct regulator *avdd;
struct regulator *vdd;
struct regulator *vcc_i2c;
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx_update.c b/drivers/input/touchscreen/gt9xx/gt9xx_update.c
index 71e8a55..af80eef 100644
--- a/drivers/input/touchscreen/gt9xx/gt9xx_update.c
+++ b/drivers/input/touchscreen/gt9xx/gt9xx_update.c
@@ -1,7 +1,7 @@
/* drivers/input/touchscreen/gt9xx_update.c
*
* 2010 - 2012 Goodix Technology.
- * 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 as published by
@@ -1432,10 +1432,15 @@
goto file_fail;
}
- ret = gup_enter_update_judge(ts->client, &fw_head);
- if (ret == FAIL) {
- pr_err("Check *.bin file fail.");
- goto file_fail;
+ if (ts->force_update) {
+ dev_dbg(&ts->client->dev, "Enter force update.");
+ } else {
+ ret = gup_enter_update_judge(ts->client, &fw_head);
+ if (ret == FAIL) {
+ dev_err(&ts->client->dev,
+ "Check *.bin file fail.");
+ goto file_fail;
+ }
}
ts->enter_update = 1;
diff --git a/drivers/input/touchscreen/synaptics_fw_update.c b/drivers/input/touchscreen/synaptics_fw_update.c
index e577353..dce9dee 100644
--- a/drivers/input/touchscreen/synaptics_fw_update.c
+++ b/drivers/input/touchscreen/synaptics_fw_update.c
@@ -1905,12 +1905,20 @@
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
- unsigned long config_area;
+ unsigned short config_area;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
- retval = kstrtoul(buf, 10, &config_area);
+ retval = kstrtou16(buf, 10, &config_area);
if (retval)
return retval;
+ if (config_area < 0x00 || config_area > 0x03) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Incorrect value of config_area\n",
+ __func__);
+ return -EINVAL;
+ }
+
fwu->config_area = config_area;
return count;
diff --git a/drivers/input/touchscreen/synaptics_i2c_rmi4.c b/drivers/input/touchscreen/synaptics_i2c_rmi4.c
index 7152ec8..ca47547 100644
--- a/drivers/input/touchscreen/synaptics_i2c_rmi4.c
+++ b/drivers/input/touchscreen/synaptics_i2c_rmi4.c
@@ -113,16 +113,15 @@
static void synaptics_rmi4_sensor_wake(struct synaptics_rmi4_data *rmi4_data);
-static void synaptics_rmi4_sensor_sleep(struct synaptics_rmi4_data *rmi4_data);
-
static int synaptics_rmi4_check_configuration(struct synaptics_rmi4_data
*rmi4_data);
-#ifdef CONFIG_PM
+
static int synaptics_rmi4_suspend(struct device *dev);
static int synaptics_rmi4_resume(struct device *dev);
+#ifdef CONFIG_PM
static ssize_t synaptics_rmi4_full_pm_cycle_show(struct device *dev,
struct device_attribute *attr, char *buf);
@@ -425,6 +424,31 @@
static bool exp_fn_inited;
static struct mutex exp_fn_list_mutex;
static struct list_head exp_fn_list;
+
+static int synaptics_rmi4_debug_suspend_set(void *_data, u64 val)
+{
+ struct synaptics_rmi4_data *rmi4_data = _data;
+
+ if (val)
+ synaptics_rmi4_suspend(&rmi4_data->input_dev->dev);
+ else
+ synaptics_rmi4_resume(&rmi4_data->input_dev->dev);
+
+ return 0;
+}
+
+static int synaptics_rmi4_debug_suspend_get(void *_data, u64 *val)
+{
+ struct synaptics_rmi4_data *rmi4_data = _data;
+
+ *val = rmi4_data->suspended;
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, synaptics_rmi4_debug_suspend_get,
+ synaptics_rmi4_debug_suspend_set, "%lld\n");
+
#ifdef CONFIG_PM
static ssize_t synaptics_rmi4_full_pm_cycle_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -449,30 +473,6 @@
return count;
}
-static int synaptics_rmi4_debug_suspend_set(void *_data, u64 val)
-{
- struct synaptics_rmi4_data *rmi4_data = _data;
-
- if (val)
- synaptics_rmi4_suspend(&rmi4_data->input_dev->dev);
- else
- synaptics_rmi4_resume(&rmi4_data->input_dev->dev);
-
- return 0;
-}
-
-static ssize_t synaptics_rmi4_debug_suspend_get(void *_data, u64 *val)
-{
- struct synaptics_rmi4_data *rmi4_data = _data;
-
- *val = rmi4_data->suspended;
-
- return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, synaptics_rmi4_debug_suspend_get,
- synaptics_rmi4_debug_suspend_set, "%lld\n");
-
#ifdef CONFIG_FB
static void configure_sleep(struct synaptics_rmi4_data *rmi4_data)
{
@@ -502,6 +502,11 @@
return;
}
#endif
+#else
+static void configure_sleep(struct synaptics_rmi4_data *rmi4_data)
+{
+ return;
+}
#endif
static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev,
@@ -3665,6 +3670,14 @@
{
return 0;
};
+static int synaptics_rmi4_suspend(struct device *dev);
+{
+ return 0;
+}
+static int synaptics_rmi4_resume(struct device *dev);
+{
+ return 0;
+}
#endif
static const struct i2c_device_id synaptics_rmi4_id_table[] = {
diff --git a/drivers/input/touchscreen/synaptics_rmi_dev.c b/drivers/input/touchscreen/synaptics_rmi_dev.c
index 7abd909..8478b24 100644
--- a/drivers/input/touchscreen/synaptics_rmi_dev.c
+++ b/drivers/input/touchscreen/synaptics_rmi_dev.c
@@ -461,7 +461,7 @@
return;
}
-static char *rmi_char_devnode(struct device *dev, mode_t *mode)
+static char *rmi_char_devnode(struct device *dev, umode_t *mode)
{
if (!mode)
return NULL;
diff --git a/drivers/iommu/msm_iommu_dev-v1.c b/drivers/iommu/msm_iommu_dev-v1.c
index bbbe77b..a9d164e 100644
--- a/drivers/iommu/msm_iommu_dev-v1.c
+++ b/drivers/iommu/msm_iommu_dev-v1.c
@@ -208,10 +208,14 @@
ret = of_platform_populate(pdev->dev.of_node,
msm_iommu_v1_ctx_match_table,
NULL, &pdev->dev);
- if (ret)
+ if (ret) {
pr_err("Failed to create iommu context device\n");
+ goto fail;
+ }
msm_iommu_add_drv(drvdata);
+ return 0;
+
fail:
__put_bus_vote_client(drvdata);
return ret;
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 95673d5..d33d34b 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.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
@@ -404,6 +404,11 @@
uint32_t error_count;
};
+struct msm_vfe_frame_ts {
+ struct timeval buf_time;
+ uint32_t frame_id;
+};
+
struct vfe_device {
struct platform_device *pdev;
struct msm_sd_subdev subdev;
@@ -446,6 +451,7 @@
struct msm_vfe_axi_shared_data axi_data;
struct msm_vfe_stats_shared_data stats_data;
struct msm_vfe_error_info error_info;
+ struct msm_vfe_frame_ts frame_ts;
struct msm_isp_buf_mgr *buf_mgr;
int dump_reg;
int vfe_clk_idx;
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 c0e5a47..0264d6d 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
@@ -235,6 +235,21 @@
return size;
}
+static void msm_isp_get_buffer_ts(struct vfe_device *vfe_dev,
+ struct msm_isp_timestamp *irq_ts, struct msm_isp_timestamp *ts)
+{
+ struct msm_vfe_frame_ts *frame_ts = &vfe_dev->frame_ts;
+ uint32_t frame_count = vfe_dev->error_info.info_dump_frame_count;
+
+ *ts = *irq_ts;
+ if (frame_count == frame_ts->frame_id) {
+ ts->buf_time = frame_ts->buf_time;
+ } else {
+ frame_ts->buf_time = irq_ts->buf_time;
+ frame_ts->frame_id = frame_count;
+ }
+}
+
void msm_isp_axi_reserve_wm(struct msm_vfe_axi_shared_data *axi_data,
struct msm_vfe_axi_stream *stream_info)
{
@@ -1448,6 +1463,7 @@
struct msm_vfe_axi_stream *stream_info;
struct msm_vfe_axi_composite_info *comp_info;
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_isp_timestamp buf_ts;
comp_mask = vfe_dev->hw_info->vfe_ops.axi_ops.
get_comp_mask(irq_status0, irq_status1);
@@ -1460,6 +1476,8 @@
pingpong_status =
vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status(vfe_dev);
+ msm_isp_get_buffer_ts(vfe_dev, ts, &buf_ts);
+
for (i = 0; i < axi_data->hw_info->num_comp_mask; i++) {
comp_info = &axi_data->composite_info[i];
if (comp_mask & (1 << i)) {
@@ -1492,7 +1510,7 @@
}
if (done_buf && !rc)
msm_isp_process_done_buf(vfe_dev,
- stream_info, done_buf, ts);
+ stream_info, done_buf, &buf_ts);
}
}
wm_mask &= ~(comp_info->stream_composite_mask);
@@ -1524,7 +1542,7 @@
}
if (done_buf && !rc)
msm_isp_process_done_buf(vfe_dev,
- stream_info, done_buf, ts);
+ stream_info, done_buf, &buf_ts);
}
}
return;
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 9a8963d..3f49f68 100755
--- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
@@ -1198,6 +1198,7 @@
static const struct of_device_id msm_ispif_dt_match[] = {
{.compatible = "qcom,ispif"},
+ {}
};
MODULE_DEVICE_TABLE(of, msm_ispif_dt_match);
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw.c b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw.c
index 44a4014..cd35eb6 100644
--- a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw.c
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw.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
@@ -328,7 +328,7 @@
int msm_jpeg_hw_exec_cmds(struct msm_jpeg_hw_cmd *hw_cmd_p, uint32_t m_cmds,
uint32_t max_size, void *base)
{
- int is_copy_to_user = -1;
+ int is_copy_to_user = 0;
uint32_t data;
while (m_cmds--) {
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_sync.c b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_sync.c
index 4331f95..5c0dc22 100644
--- a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_sync.c
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_sync.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
@@ -722,6 +722,9 @@
kfree(hw_cmds_p);
return -EFAULT;
}
+ } else {
+ kfree(hw_cmds_p);
+ return is_copy_to_user;
}
kfree(hw_cmds_p);
return 0;
@@ -765,11 +768,12 @@
for (i = 0; i < 2; i++)
kfree(buf_out_free[i]);
+ pgmn_dev->state = MSM_JPEG_EXECUTING;
JPEG_DBG_HIGH("%s:%d] START\n", __func__, __LINE__);
wmb();
rc = msm_jpeg_ioctl_hw_cmds(pgmn_dev, arg);
wmb();
- pgmn_dev->state = MSM_JPEG_EXECUTING;
+
JPEG_DBG("%s:%d]", __func__, __LINE__);
return rc;
}
diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c
index b1a23b4..67f7c2b 100644
--- a/drivers/media/platform/msm/camera_v2/msm.c
+++ b/drivers/media/platform/msm/camera_v2/msm.c
@@ -1070,6 +1070,7 @@
static const struct of_device_id msm_dt_match[] = {
{.compatible = "qcom,msm-cam"},
+ {}
}
MODULE_DEVICE_TABLE(of, msm_dt_match);
diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
index cb8fcdd..6994258 100644
--- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
+++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
@@ -203,6 +203,7 @@
static const struct of_device_id msm_buf_mngr_dt_match[] = {
{.compatible = "qcom,msm_buf_mngr"},
+ {}
};
static int __init msm_buf_mngr_init(void)
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 ea16ebd..ef8b996 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
@@ -411,6 +411,43 @@
return rc;
}
+static int32_t msm_actuator_set_position(
+ struct msm_actuator_ctrl_t *a_ctrl,
+ struct msm_actuator_set_position_t *set_pos)
+{
+ int32_t rc = 0;
+ int32_t index;
+ uint16_t next_lens_position;
+ uint16_t delay;
+ uint32_t hw_params = 0;
+ struct msm_camera_i2c_reg_setting reg_setting;
+ CDBG("%s Enter %d\n", __func__, __LINE__);
+ if (set_pos->number_of_steps == 0)
+ return rc;
+
+ a_ctrl->i2c_tbl_index = 0;
+ for (index = 0; index < set_pos->number_of_steps; index++) {
+ next_lens_position = set_pos->pos[index];
+ delay = set_pos->delay[index];
+ a_ctrl->func_tbl->actuator_parse_i2c_params(a_ctrl,
+ next_lens_position, hw_params, delay);
+
+ reg_setting.reg_setting = a_ctrl->i2c_reg_tbl;
+ reg_setting.size = a_ctrl->i2c_tbl_index;
+ reg_setting.data_type = a_ctrl->i2c_data_type;
+
+ rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_write_table_w_microdelay(
+ &a_ctrl->i2c_client, ®_setting);
+ if (rc < 0) {
+ pr_err("%s Failed I2C write Line %d\n", __func__, __LINE__);
+ return rc;
+ }
+ a_ctrl->i2c_tbl_index = 0;
+ }
+ CDBG("%s exit %d\n", __func__, __LINE__);
+ return rc;
+}
+
static int32_t msm_actuator_init(struct msm_actuator_ctrl_t *a_ctrl,
struct msm_actuator_set_info_t *set_info) {
struct reg_settings_t *init_settings = NULL;
@@ -565,6 +602,12 @@
pr_err("move focus failed %d\n", rc);
break;
+ case CFG_SET_POSITION:
+ rc = a_ctrl->func_tbl->actuator_set_position(a_ctrl,
+ &cdata->cfg.setpos);
+ if (rc < 0)
+ pr_err("actuator_set_position failed %d\n", rc);
+ break;
default:
break;
}
@@ -919,6 +962,7 @@
.actuator_set_default_focus = msm_actuator_set_default_focus,
.actuator_init_focus = msm_actuator_init_focus,
.actuator_parse_i2c_params = msm_actuator_parse_i2c_params,
+ .actuator_set_position = msm_actuator_set_position,
},
};
diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.h b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.h
index 809c9cf..7ec9a49 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.h
@@ -43,6 +43,8 @@
struct damping_params_t *,
int8_t,
int16_t);
+ int32_t (*actuator_set_position)(struct msm_actuator_ctrl_t *,
+ struct msm_actuator_set_position_t *);
};
struct msm_actuator {
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index 4da0f6f..4b7a3be 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.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
@@ -360,6 +360,18 @@
case HAL_EXTRADATA_MPEG2_SEQDISP:
ret = HFI_PROPERTY_PARAM_VDEC_MPEG2_SEQDISP_EXTRADATA;
break;
+ case HAL_EXTRADATA_FRAME_QP:
+ ret = HFI_PROPERTY_PARAM_VDEC_FRAME_QP_EXTRADATA;
+ break;
+ case HAL_EXTRADATA_FRAME_BITS_INFO:
+ ret = HFI_PROPERTY_PARAM_VDEC_FRAME_BITS_INFO_EXTRADATA;
+ break;
+ case HAL_EXTRADATA_LTR_INFO:
+ ret = HFI_PROPERTY_PARAM_VENC_LTR_INFO;
+ break;
+ case HAL_EXTRADATA_METADATA_MBI:
+ ret = HFI_PROPERTY_PARAM_VENC_MBI_DUMPING;
+ break;
default:
dprintk(VIDC_WARN, "Extradata index not found: %d\n", index);
break;
@@ -389,6 +401,28 @@
return buf_mode;
}
+static u32 get_hfi_ltr_mode(enum ltr_mode ltr_mode_type)
+{
+ u32 ltrmode;
+ switch (ltr_mode_type) {
+ case HAL_LTR_MODE_DISABLE:
+ ltrmode = HFI_LTR_MODE_DISABLE;
+ break;
+ case HAL_LTR_MODE_MANUAL:
+ ltrmode = HFI_LTR_MODE_MANUAL;
+ break;
+ case HAL_LTR_MODE_PERIODIC:
+ ltrmode = HFI_LTR_MODE_PERIODIC;
+ break;
+ default:
+ dprintk(VIDC_ERR, "Invalid ltr mode :0x%x\n",
+ ltr_mode_type);
+ ltrmode = HFI_LTR_MODE_DISABLE;
+ break;
+ }
+ return ltrmode;
+}
+
int create_pkt_cmd_session_set_buffers(
struct hfi_cmd_session_set_buffers_packet *pkt,
u32 session_id,
@@ -1405,6 +1439,54 @@
pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
break;
}
+ case HAL_PARAM_VENC_LTRMODE:
+ {
+ struct hfi_ltrmode *hfi;
+ struct hal_ltrmode *hal = pdata;
+ pkt->rg_property_data[0] =
+ HFI_PROPERTY_PARAM_VENC_LTRMODE;
+ hfi = (struct hfi_ltrmode *) &pkt->rg_property_data[1];
+ hfi->ltrmode = get_hfi_ltr_mode(hal->ltrmode);
+ hfi->ltrcount = hal->ltrcount;
+ hfi->trustmode = hal->trustmode;
+ pkt->size += sizeof(u32) + sizeof(struct hfi_ltrmode);
+ pr_err("SET LTR\n");
+ break;
+ }
+ case HAL_CONFIG_VENC_USELTRFRAME:
+ {
+ struct hfi_ltruse *hfi;
+ struct hal_ltruse *hal = pdata;
+ pkt->rg_property_data[0] =
+ HFI_PROPERTY_CONFIG_VENC_USELTRFRAME;
+ hfi = (struct hfi_ltruse *) &pkt->rg_property_data[1];
+ hfi->frames = hal->frames;
+ hfi->refltr = hal->refltr;
+ hfi->useconstrnt = hal->useconstrnt;
+ pkt->size += sizeof(u32) + sizeof(struct hfi_ltruse);
+ pr_err("USE LTR\n");
+ break;
+ }
+ case HAL_CONFIG_VENC_MARKLTRFRAME:
+ {
+ struct hfi_ltrmark *hfi;
+ struct hal_ltrmark *hal = pdata;
+ pkt->rg_property_data[0] =
+ HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME;
+ hfi = (struct hfi_ltrmark *) &pkt->rg_property_data[1];
+ hfi->markframe = hal->markframe;
+ pkt->size += sizeof(u32) + sizeof(struct hfi_ltrmark);
+ pr_err("MARK LTR\n");
+ break;
+ }
+ case HAL_PARAM_VENC_HIER_P_NUM_FRAMES:
+ {
+ pkt->rg_property_data[0] =
+ HFI_PROPERTY_PARAM_VENC_HIER_P_NUM_ENH_LAYER;
+ pkt->rg_property_data[1] = *(u32 *)pdata;
+ pkt->size += sizeof(u32) * 2;
+ break;
+ }
/* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */
case HAL_CONFIG_BUFFER_REQUIREMENTS:
case HAL_CONFIG_PRIORITY:
@@ -1432,7 +1514,7 @@
case HAL_CONFIG_VENC_TIMESTAMP_SCALE:
case HAL_PARAM_VENC_LOW_LATENCY:
default:
- dprintk(VIDC_ERR, "DEFAULT: Calling 0x%x", ptype);
+ dprintk(VIDC_ERR, "DEFAULT: Calling 0x%x\n", ptype);
rc = -ENOTSUPP;
break;
}
diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c
index 189fca0..f4ad985 100644
--- a/drivers/media/platform/msm/vidc/hfi_response_handler.c
+++ b/drivers/media/platform/msm/vidc/hfi_response_handler.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
@@ -388,6 +388,10 @@
struct vidc_hal_session_init_done *sess_init_done)
{
struct hal_capability_supported *out = NULL;
+ if (!in) {
+ dprintk(VIDC_ERR, "Invalid input for supported capabilties\n");
+ return;
+ }
switch (in->capability_type) {
case HFI_CAPABILITY_FRAME_WIDTH:
out = &sess_init_done->width;
@@ -420,9 +424,17 @@
case HFI_CAPABILITY_BITRATE:
out = &sess_init_done->bitrate;
break;
+
+ case HFI_CAPABILITY_ENC_LTR_COUNT:
+ out = &sess_init_done->ltr_count;
+ break;
+
+ case HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS:
+ out = &sess_init_done->hier_p;
+ break;
}
- if (in && out) {
+ if (out) {
out->capability_type =
(enum hal_capability)in->capability_type;
out->min = in->min;
diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
index 5e3699d..176c612 100644
--- a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
@@ -120,6 +120,13 @@
return msm_vidc_g_ctrl((void *)vidc_inst, a);
}
+int msm_v4l2_s_ext_ctrl(struct file *file, void *fh,
+ struct v4l2_ext_controls *a)
+{
+ struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+ return msm_vidc_s_ext_ctrl((void *)vidc_inst, a);
+}
+
int msm_v4l2_reqbufs(struct file *file, void *fh,
struct v4l2_requestbuffers *b)
{
@@ -242,6 +249,7 @@
.vidioc_streamoff = msm_v4l2_streamoff,
.vidioc_s_ctrl = msm_v4l2_s_ctrl,
.vidioc_g_ctrl = msm_v4l2_g_ctrl,
+ .vidioc_s_ext_ctrls = msm_v4l2_s_ext_ctrl,
.vidioc_subscribe_event = msm_v4l2_subscribe_event,
.vidioc_unsubscribe_event = msm_v4l2_unsubscribe_event,
.vidioc_decoder_cmd = msm_v4l2_decoder_cmd,
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c
index 5c735d6..71ad080 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -220,7 +220,7 @@
.name = "Extradata Type",
.type = V4L2_CTRL_TYPE_MENU,
.minimum = V4L2_MPEG_VIDC_EXTRADATA_NONE,
- .maximum = V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP,
+ .maximum = V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO,
.default_value = V4L2_MPEG_VIDC_EXTRADATA_NONE,
.menu_skip_mask = ~(
(1 << V4L2_MPEG_VIDC_EXTRADATA_NONE) |
@@ -241,7 +241,9 @@
(1 << V4L2_MPEG_VIDC_INDEX_EXTRADATA_INPUT_CROP) |
(1 << V4L2_MPEG_VIDC_INDEX_EXTRADATA_DIGITAL_ZOOM) |
(1 << V4L2_MPEG_VIDC_INDEX_EXTRADATA_ASPECT_RATIO) |
- (1 << V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP)
+ (1 << V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP) |
+ (1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_QP) |
+ (1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO)
),
.qmenu = mpeg_video_vidc_extradata,
.step = 0,
@@ -574,15 +576,7 @@
core);
goto exit;
}
- if (!inst->in_reconfig) {
- rc = msm_comm_try_state(inst, MSM_VIDC_RELEASE_RESOURCES_DONE);
- if (rc) {
- dprintk(VIDC_ERR,
- "Failed to move inst: %p to relase res done\n",
- inst);
- goto exit;
- }
- }
+
switch (b->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
break;
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index de59e81..b01a507 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -37,6 +37,8 @@
#define B_FRAME_QP 30
#define MAX_INTRA_REFRESH_MBS 300
#define MAX_NUM_B_FRAMES 4
+#define MAX_LTR_FRAME_COUNT 10
+
#define L_MODE V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY
#define CODING V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY
@@ -114,6 +116,7 @@
"Extradata input crop",
"Extradata digital zoom",
"Extradata aspect ratio",
+ "Extradata macroblock metadata",
};
static const char *const perf_level[] = {
@@ -135,7 +138,8 @@
MSM_VENC_CTRL_CLUSTER_TIMING = 1 << 9,
MSM_VENC_CTRL_CLUSTER_VP8_PROFILE_LEVEL = 1 << 10,
MSM_VENC_CTRL_CLUSTER_DEINTERLACE = 1 << 11,
- MSM_VENC_CTRL_CLUSTER_MAX = 1 << 12,
+ MSM_VENC_CTRL_CLUSTER_USE_LTRFRAME = 1 << 12,
+ MSM_VENC_CTRL_CLUSTER_MAX = 1 << 13,
};
static struct msm_vidc_ctrl msm_venc_ctrls[] = {
@@ -651,7 +655,7 @@
.name = "Extradata Type",
.type = V4L2_CTRL_TYPE_MENU,
.minimum = V4L2_MPEG_VIDC_EXTRADATA_NONE,
- .maximum = V4L2_MPEG_VIDC_INDEX_EXTRADATA_ASPECT_RATIO,
+ .maximum = V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI,
.default_value = V4L2_MPEG_VIDC_EXTRADATA_NONE,
.menu_skip_mask = ~(
(1 << V4L2_MPEG_VIDC_EXTRADATA_NONE) |
@@ -671,7 +675,9 @@
(1 << V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER) |
(1 << V4L2_MPEG_VIDC_INDEX_EXTRADATA_INPUT_CROP) |
(1 << V4L2_MPEG_VIDC_INDEX_EXTRADATA_DIGITAL_ZOOM) |
- (1 << V4L2_MPEG_VIDC_INDEX_EXTRADATA_ASPECT_RATIO)
+ (1 << V4L2_MPEG_VIDC_INDEX_EXTRADATA_ASPECT_RATIO) |
+ (1 << V4L2_MPEG_VIDC_EXTRADATA_LTR) |
+ (1 << V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI)
),
.qmenu = mpeg_video_vidc_extradata,
.step = 0,
@@ -729,6 +735,61 @@
.default_value = V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE_DISABLED,
.step = 1,
.cluster = MSM_VENC_CTRL_CLUSTER_DEINTERLACE,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_USELTRFRAME,
+ .name = "H264 Use LTR",
+ .type = V4L2_CTRL_TYPE_BUTTON,
+ .minimum = 0,
+ .maximum = (MAX_LTR_FRAME_COUNT - 1),
+ .default_value = 0,
+ .step = 1,
+ .qmenu = NULL,
+ .cluster = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_LTRCOUNT,
+ .name = "Ltr Count",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = MAX_LTR_FRAME_COUNT,
+ .default_value = 0,
+ .step = 1,
+ .qmenu = NULL,
+ .cluster = MSM_VENC_CTRL_CLUSTER_USE_LTRFRAME,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_LTRMODE,
+ .name = "Ltr Mode",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = V4L2_MPEG_VIDC_VIDEO_LTR_MODE_DISABLE,
+ .maximum = V4L2_MPEG_VIDC_VIDEO_LTR_MODE_PERIODIC,
+ .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,
+ .name = "H264 Mark LTR",
+ .type = V4L2_CTRL_TYPE_BUTTON,
+ .minimum = 0,
+ .maximum = (MAX_LTR_FRAME_COUNT - 1),
+ .default_value = 0,
+ .step = 1,
+ .qmenu = NULL,
+ .cluster = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS,
+ .name = "Set Hier P num layers",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 3,
+ .default_value = 0,
+ .step = 1,
+ .qmenu = NULL,
+ .cluster = 0,
}
};
@@ -836,28 +897,33 @@
*num_buffers = buff_req->buffer_count_actual =
max(*num_buffers, buff_req->buffer_count_actual);
}
- if (*num_buffers < MIN_NUM_CAPTURE_BUFFERS)
- *num_buffers = MIN_NUM_CAPTURE_BUFFERS;
- if (*num_buffers > VIDEO_MAX_FRAME) {
- dprintk(VIDC_ERR,
- "Changing buffers requested, from %d to max"\
- " supported (%d) best effort encoding\n",
- *num_buffers, VIDEO_MAX_FRAME);
- *num_buffers = VIDEO_MAX_FRAME;
+ if (*num_buffers < MIN_NUM_CAPTURE_BUFFERS ||
+ *num_buffers > VIDEO_MAX_FRAME) {
+ int temp = *num_buffers;
+
+ *num_buffers = clamp_val(*num_buffers,
+ MIN_NUM_CAPTURE_BUFFERS,
+ VIDEO_MAX_FRAME);
+ dprintk(VIDC_INFO,
+ "Changing buffer count on CAPTURE_MPLANE from %d to %d for best effort encoding\n",
+ temp, *num_buffers);
}
+
ctrl = v4l2_ctrl_find(&inst->ctrl_handler,
V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA);
if (ctrl)
extradata = v4l2_ctrl_g_ctrl(ctrl);
- if (extradata)
+ if (extradata != V4L2_MPEG_VIDC_EXTRADATA_NONE)
*num_planes = *num_planes + 1;
inst->fmts[CAPTURE_PORT]->num_planes = *num_planes;
+
for (i = 0; i < *num_planes; i++) {
sizes[i] = inst->fmts[CAPTURE_PORT]->get_frame_size(
i, inst->prop.height[CAPTURE_PORT],
inst->prop.width[CAPTURE_PORT]);
}
+
property_id = HAL_PARAM_BUFFER_COUNT_ACTUAL;
new_buf_count.buffer_type = HAL_BUFFER_OUTPUT;
new_buf_count.buffer_count_actual = *num_buffers;
@@ -1273,6 +1339,9 @@
struct v4l2_ctrl *temp_ctrl = NULL;
struct hfi_device *hdev;
struct hal_extradata_enable extra;
+ struct hal_ltruse useltr;
+ struct hal_ltrmark markltr;
+ u32 hier_p_layers;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
@@ -1970,7 +2039,33 @@
pdata = &enable;
break;
}
+ case V4L2_CID_MPEG_VIDC_VIDEO_USELTRFRAME:
+ property_id = HAL_CONFIG_VENC_USELTRFRAME;
+ useltr.refltr = ctrl->val;
+ useltr.useconstrnt = false;
+ useltr.frames = 0;
+ pdata = &useltr;
+ break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_MARKLTRFRAME:
+ property_id = HAL_CONFIG_VENC_MARKLTRFRAME;
+ markltr.markframe = ctrl->val;
+ pdata = &markltr;
+ break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS:
+ property_id = HAL_PARAM_VENC_HIER_P_NUM_FRAMES;
+ hier_p_layers = ctrl->val;
+ if (hier_p_layers > (inst->capability.hier_p.max - 1)) {
+ dprintk(VIDC_ERR,
+ "Error setting hier p num layers = %d max supported by f/w = %d\n",
+ hier_p_layers,
+ inst->capability.hier_p.max - 1);
+ rc = -ENOTSUPP;
+ break;
+ }
+ pdata = &hier_p_layers;
+ break;
default:
+ dprintk(VIDC_ERR, "Unsupported index: %x\n", ctrl->id);
rc = -ENOTSUPP;
break;
}
@@ -1987,6 +2082,89 @@
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)
+{
+ int rc = 0, i;
+ 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;
+
+ 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;
+
+ control = ctrl->controls;
+ for (i = 0; i < ctrl->count; i++) {
+ switch (control[i].id) {
+ case V4L2_CID_MPEG_VIDC_VIDEO_LTRMODE:
+ ltrmode.ltrmode = control[i].value;
+ ltrmode.trustmode = 1;
+ property_id = HAL_PARAM_VENC_LTRMODE;
+ pdata = <rmode;
+ break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_LTRCOUNT:
+ ltrmode.ltrcount = control[i].value;
+ if (ltrmode.ltrcount > cap->ltr_count.max) {
+ dprintk(VIDC_ERR,
+ "Invalid LTR count %d. Supported max: %d\n",
+ ltrmode.ltrcount,
+ cap->ltr_count.max);
+ /*
+ * FIXME: Return an error (-EINVALID)
+ * here once VP8 supports LTR count
+ * capability
+ */
+ ltrmode.ltrcount = 1;
+ }
+ ltrmode.trustmode = 1;
+ property_id = HAL_PARAM_VENC_LTRMODE;
+ pdata = <rmode;
+ break;
+ default:
+ dprintk(VIDC_ERR, "Invalid id set: %d\n",
+ control[i].id);
+ rc = -ENOTSUPP;
+ break;
+ }
+ if (rc)
+ break;
+ }
+
+ if (!rc && property_id) {
+ dprintk(VIDC_DBG, "Control: HAL property=%x\n", property_id);
+ rc = call_hfi_op(hdev, session_set_property,
+ (void *)inst->session, property_id, pdata);
+ }
+ pr_err("Returning from %s\n", __func__);
+ return rc;
+}
+
static int msm_venc_op_s_ctrl(struct v4l2_ctrl *ctrl)
{
@@ -2072,6 +2250,22 @@
return v4l2_g_ctrl(&inst->ctrl_handler, ctrl);
}
+int msm_venc_s_ext_ctrl(struct msm_vidc_inst *inst,
+ struct v4l2_ext_controls *ctrl)
+{
+ int rc = 0;
+ if (ctrl->ctrl_class != V4L2_CTRL_CLASS_MPEG) {
+ dprintk(VIDC_ERR, "Invalid Class set for extended control\n");
+ return -EINVAL;
+ }
+ rc = try_set_ext_ctrl(inst, ctrl);
+ if (rc) {
+ dprintk(VIDC_ERR, "Error setting extended control\n");
+ return rc;
+ }
+ return rc;
+}
+
int msm_venc_cmd(struct msm_vidc_inst *inst, struct v4l2_encoder_cmd *enc)
{
int rc = 0;
diff --git a/drivers/media/platform/msm/vidc/msm_venc.h b/drivers/media/platform/msm/vidc/msm_venc.h
index 9020167..5965d39 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.h
+++ b/drivers/media/platform/msm/vidc/msm_venc.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
@@ -25,6 +25,7 @@
int msm_venc_g_fmt(void *instance, struct v4l2_format *f);
int msm_venc_s_ctrl(void *instance, struct v4l2_control *a);
int msm_venc_g_ctrl(void *instance, struct v4l2_control *a);
+int msm_venc_s_ext_ctrl(void *instance, struct v4l2_ext_controls *a);
int msm_venc_reqbufs(void *instance, struct v4l2_requestbuffers *b);
int msm_venc_prepare_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
int msm_venc_release_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index 9774c3c..59a1ec0 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -185,6 +185,15 @@
return msm_venc_g_ctrl(instance, control);
return -EINVAL;
}
+int msm_vidc_s_ext_ctrl(void *instance, struct v4l2_ext_controls *control)
+{
+ struct msm_vidc_inst *inst = instance;
+ if (!inst || !control)
+ return -EINVAL;
+ if (inst->session_type == MSM_VIDC_ENCODER)
+ return msm_venc_s_ext_ctrl(instance, control);
+ return -EINVAL;
+}
int msm_vidc_reqbufs(void *instance, struct v4l2_requestbuffers *b)
{
struct msm_vidc_inst *inst = instance;
@@ -718,6 +727,26 @@
if (!inst)
return -EINVAL;
+ if (!inst->in_reconfig) {
+ rc = msm_comm_try_state(inst, MSM_VIDC_RELEASE_RESOURCES_DONE);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to move inst: %p to release res done\n",
+ inst);
+ }
+ }
+
+ /*
+ * In dynamic buffer mode, driver needs to release resources,
+ * but not call release buffers on firmware, as the buffers
+ * were never registered with firmware.
+ */
+ if ((buffer_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) &&
+ (inst->buffer_mode_set[CAPTURE_PORT] ==
+ HAL_BUFFER_MODE_DYNAMIC)) {
+ goto free_and_unmap;
+ }
+
list_for_each_safe(ptr, next, &inst->registered_bufs) {
bool release_buf = false;
mutex_lock(&inst->lock);
@@ -756,6 +785,8 @@
buffer_info.m.planes[0].reserved[1],
buffer_info.m.planes[0].length);
}
+
+free_and_unmap:
mutex_lock(&inst->lock);
list_for_each_safe(ptr, next, &inst->registered_bufs) {
bi = list_entry(ptr, struct buffer_info, list);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 7ae2cb5..95afa2a 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -463,6 +463,9 @@
session_init_done->frame_rate;
inst->capability.scale_x = session_init_done->scale_x;
inst->capability.scale_y = session_init_done->scale_y;
+ inst->capability.ltr_count =
+ session_init_done->ltr_count;
+ inst->capability.hier_p = session_init_done->hier_p;
inst->capability.pixelprocess_capabilities =
call_hfi_op(hdev, get_core_capabilities);
inst->capability.capability_set = true;
@@ -1117,6 +1120,7 @@
struct vidc_hal_fbd *fill_buf_done;
enum hal_buffer buffer_type;
int64_t time_usec = 0;
+ int extra_idx = 0;
if (!response) {
dprintk(VIDC_ERR, "Invalid response from vidc_hal\n");
@@ -1163,6 +1167,15 @@
ns_to_timeval(time_usec * NSEC_PER_USEC);
}
vb->v4l2_buf.flags = 0;
+ extra_idx =
+ EXTRADATA_IDX(inst->fmts[CAPTURE_PORT]->num_planes);
+ if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) {
+ vb->v4l2_planes[extra_idx].m.userptr =
+ (unsigned long)fill_buf_done->extra_data_buffer;
+ vb->v4l2_planes[extra_idx].bytesused =
+ vb->v4l2_planes[extra_idx].length;
+ vb->v4l2_planes[extra_idx].data_offset = 0;
+ }
handle_dynamic_buffer(inst, (u32)fill_buf_done->packet_buffer1,
fill_buf_done->flags1);
@@ -1217,6 +1230,13 @@
fill_buf_done->start_y_coord, fill_buf_done->frame_width,
fill_buf_done->frame_height, fill_buf_done->picture_type);
+ if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) {
+ dprintk(VIDC_DBG,
+ "extradata: userptr = %p; bytesused = %d; length = %d\n",
+ (u8 *)vb->v4l2_planes[extra_idx].m.userptr,
+ vb->v4l2_planes[extra_idx].bytesused,
+ vb->v4l2_planes[extra_idx].length);
+ }
mutex_lock(&inst->bufq[CAPTURE_PORT].lock);
vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
mutex_unlock(&inst->bufq[CAPTURE_PORT].lock);
@@ -3121,6 +3141,18 @@
case V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP:
ret = HAL_EXTRADATA_MPEG2_SEQDISP;
break;
+ case V4L2_MPEG_VIDC_EXTRADATA_FRAME_QP:
+ ret = HAL_EXTRADATA_FRAME_QP;
+ break;
+ case V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO:
+ ret = HAL_EXTRADATA_FRAME_BITS_INFO;
+ break;
+ case V4L2_MPEG_VIDC_EXTRADATA_LTR:
+ ret = HAL_EXTRADATA_LTR_INFO;
+ break;
+ case V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI:
+ ret = HAL_EXTRADATA_METADATA_MBI;
+ break;
default:
dprintk(VIDC_WARN, "Extradata not found: %d\n", index);
break;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
index e4f920f..06181dd 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.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
@@ -190,6 +190,8 @@
u32 pixelprocess_capabilities;
struct hal_capability_supported scale_x;
struct hal_capability_supported scale_y;
+ struct hal_capability_supported ltr_count;
+ struct hal_capability_supported hier_p;
u32 capability_set;
enum buffer_mode_type buffer_mode[MAX_PORT_NUM];
};
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index 9359823..694335b 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -1536,16 +1536,11 @@
int enable)
{
u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE];
- u32 hw_version;
struct hfi_cmd_sys_set_property_packet *pkt =
(struct hfi_cmd_sys_set_property_packet *) &packet;
- hw_version = venus_hfi_read_register(device, VIDC_WRAPPER_HW_VERSION);
- dprintk(VIDC_DBG, "Venus HW version: 0x%x\n", hw_version);
- if ((hw_version & 0xFFFF0000) != 0x10030000) {
- create_pkt_cmd_sys_idle_indicator(pkt, enable);
- if (venus_hfi_iface_cmdq_write(device, pkt))
- return -ENOTEMPTY;
- }
+ create_pkt_cmd_sys_idle_indicator(pkt, enable);
+ if (venus_hfi_iface_cmdq_write(device, pkt))
+ return -ENOTEMPTY;
return 0;
}
@@ -3119,14 +3114,14 @@
return -EINVAL;
}
ocmem_buffer = device->resources.ocmem.buf;
- if (!ocmem_buffer ||
- ocmem_buffer->len < size) {
+ 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);
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi.h b/drivers/media/platform/msm/vidc/vidc_hfi.h
index 70b93ff0..75f583f 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi.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
@@ -19,6 +19,7 @@
#define HFI_EVENT_SESSION_SEQUENCE_CHANGED (HFI_OX_BASE + 0x3)
#define HFI_EVENT_SESSION_PROPERTY_CHANGED (HFI_OX_BASE + 0x4)
+#define HFI_EVENT_SESSION_LTRUSE_FAILED (HFI_OX_BASE + 0x5)
#define HFI_EVENT_RELEASE_BUFFER_REFERENCE (HFI_OX_BASE + 0x6)
#define HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUFFER_RESOURCES \
@@ -81,9 +82,12 @@
#define HFI_EXTRADATA_CLOSED_CAPTION_UD 0x0000000A
#define HFI_EXTRADATA_AFD_UD 0x0000000B
#define HFI_EXTRADATA_MPEG2_SEQDISP 0x0000000D
+#define HFI_EXTRADATA_FRAME_QP 0x0000000F
+#define HFI_EXTRADATA_FRAME_BITS_INFO 0x00000010
#define HFI_EXTRADATA_MULTISLICE_INFO 0x7F100000
#define HFI_EXTRADATA_NUM_CONCEALED_MB 0x7F100001
#define HFI_EXTRADATA_INDEX 0x7F100002
+#define HFI_EXTRADATA_METADATA_LTR 0x7F100004
#define HFI_EXTRADATA_METADATA_FILLER 0x7FE00002
#define HFI_INDEX_EXTRADATA_INPUT_CROP 0x0700000E
@@ -202,6 +206,10 @@
(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_FRAME_QP_EXTRADATA \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x018)
+#define HFI_PROPERTY_PARAM_VDEC_FRAME_BITS_INFO_EXTRADATA \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x019)
#define HFI_PROPERTY_CONFIG_VDEC_OX_START \
(HFI_DOMAIN_BASE_VDEC + HFI_ARCH_OX_OFFSET + 0x0000)
@@ -218,6 +226,10 @@
(HFI_PROPERTY_PARAM_VENC_OX_START + 0x001)
#define HFI_PROPERTY_PARAM_VENC_H264_IDR_S3D_FRAME_PACKING_NAL \
(HFI_PROPERTY_PARAM_VENC_OX_START + 0x002)
+#define HFI_PROPERTY_PARAM_VENC_LTR_INFO \
+ (HFI_PROPERTY_PARAM_VENC_OX_START + 0x003)
+#define HFI_PROPERTY_PARAM_VENC_MBI_DUMPING \
+ (HFI_PROPERTY_PARAM_VENC_OX_START + 0x005)
#define HFI_PROPERTY_CONFIG_VENC_OX_START \
(HFI_DOMAIN_BASE_VENC + HFI_ARCH_OX_OFFSET + 0x6000)
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index 846171e..c764758 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.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
@@ -97,7 +97,11 @@
HAL_EXTRADATA_NUM_CONCEALED_MB,
HAL_EXTRADATA_METADATA_FILLER,
HAL_EXTRADATA_ASPECT_RATIO,
- HAL_EXTRADATA_MPEG2_SEQDISP
+ HAL_EXTRADATA_MPEG2_SEQDISP,
+ HAL_EXTRADATA_FRAME_QP,
+ HAL_EXTRADATA_FRAME_BITS_INFO,
+ HAL_EXTRADATA_LTR_INFO,
+ HAL_EXTRADATA_METADATA_MBI,
};
enum hal_property {
@@ -177,6 +181,11 @@
HAL_PARAM_BUFFER_ALLOC_MODE,
HAL_PARAM_VDEC_FRAME_ASSEMBLY,
HAL_PARAM_VDEC_CONCEAL_COLOR,
+ HAL_PARAM_VENC_LTRMODE,
+ HAL_CONFIG_VENC_MARKLTRFRAME,
+ HAL_CONFIG_VENC_USELTRFRAME,
+ HAL_CONFIG_VENC_LTRPERIOD,
+ HAL_PARAM_VENC_HIER_P_NUM_FRAMES,
};
enum hal_domain {
@@ -896,6 +905,27 @@
enum buffer_mode_type buffer_mode;
};
+enum ltr_mode {
+ HAL_LTR_MODE_DISABLE,
+ HAL_LTR_MODE_MANUAL,
+ HAL_LTR_MODE_PERIODIC,
+};
+
+struct hal_ltrmode {
+ enum ltr_mode ltrmode;
+ u32 ltrcount;
+ u32 trustmode;
+};
+
+struct hal_ltruse {
+ u32 refltr;
+ u32 useconstrnt;
+ u32 frames;
+};
+
+struct hal_ltrmark {
+ u32 markframe;
+};
/* HAL Response */
enum command_response {
@@ -1032,6 +1062,8 @@
struct hal_capability_supported scale_x;
struct hal_capability_supported scale_y;
struct hal_capability_supported bitrate;
+ struct hal_capability_supported ltr_count;
+ struct hal_capability_supported hier_p;
struct hal_uncompressed_format_supported uncomp_format;
struct hal_interlace_format_supported HAL_format;
struct hal_nal_stream_format_supported nal_stream_format;
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
index 1916e9f..5117266 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.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
@@ -309,7 +309,7 @@
(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_H264_LTRMODE \
+#define HFI_PROPERTY_PARAM_VENC_LTRMODE \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01C)
#define HFI_PROPERTY_PARAM_VENC_VIDEO_FULL_RANGE \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01D)
@@ -338,7 +338,12 @@
(HFI_DOMAIN_BASE_VPE + HFI_ARCH_COMMON_OFFSET + 0x7000)
#define HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER \
(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x008)
-
+#define HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME \
+ (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_LTRPERIOD \
+ (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00C)
#define HFI_PROPERTY_CONFIG_VPE_COMMON_START \
(HFI_DOMAIN_BASE_VPE + HFI_ARCH_COMMON_OFFSET + 0x8000)
#define HFI_PROPERTY_CONFIG_VPE_DEINTERLACE \
@@ -361,6 +366,7 @@
#define HFI_CAPABILITY_BITRATE (HFI_COMMON_BASE + 0x8)
#define HFI_CAPABILITY_BFRAME (HFI_COMMON_BASE + 0x9)
#define HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS (HFI_COMMON_BASE + 0x10)
+#define HFI_CAPABILITY_ENC_LTR_COUNT (HFI_COMMON_BASE + 0x11)
struct hfi_capability_supported {
u32 capability_type;
@@ -531,6 +537,26 @@
u32 layer_id;
};
+#define HFI_LTR_MODE_DISABLE 0x0
+#define HFI_LTR_MODE_MANUAL 0x1
+#define HFI_LTR_MODE_PERIODIC 0x2
+
+struct hfi_ltrmode {
+ u32 ltrmode;
+ u32 ltrcount;
+ u32 trustmode;
+};
+
+struct hfi_ltruse {
+ u32 refltr;
+ u32 useconstrnt;
+ u32 frames;
+};
+
+struct hfi_ltrmark {
+ u32 markframe;
+};
+
struct hfi_frame_size {
u32 buffer_type;
u32 width;
diff --git a/drivers/media/platform/msm/wfd/enc-venus-subdev.c b/drivers/media/platform/msm/wfd/enc-venus-subdev.c
index 7e64714..40d1b2c 100644
--- a/drivers/media/platform/msm/wfd/enc-venus-subdev.c
+++ b/drivers/media/platform/msm/wfd/enc-venus-subdev.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
@@ -14,6 +14,7 @@
#include <linux/bitmap.h>
#include <linux/completion.h>
#include <linux/ion.h>
+#include <linux/jiffies.h>
#include <linux/kthread.h>
#include <linux/list.h>
#include <linux/mutex.h>
@@ -28,6 +29,7 @@
#define BUF_TYPE_OUTPUT V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
#define BUF_TYPE_INPUT V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
+#define TIMEOUT msecs_to_jiffies(100)
static struct ion_client *venc_ion_client;
static long venc_secure(struct v4l2_subdev *sd);
@@ -1027,10 +1029,18 @@
index = next_free_index(&inst->free_output_indices);
mutex_unlock(&inst->lock);
- if (index < 0)
- wait_for_completion(&inst->dq_complete);
- else
+ if (index < 0) {
+ rc = wait_for_completion_timeout(&inst->dq_complete,
+ TIMEOUT);
+ if (!rc) {
+ WFD_MSG_ERR(
+ "Timed out waiting for an output buffer\n");
+ rc = -ETIMEDOUT;
+ goto err_fill_buf;
+ }
+ } else {
break;
+ }
}
buffer = (struct v4l2_buffer) {
@@ -1050,6 +1060,7 @@
mutex_unlock(&inst->lock);
}
+err_fill_buf:
return rc;
}
@@ -1153,10 +1164,18 @@
index = next_free_index(&inst->free_input_indices);
mutex_unlock(&inst->lock);
- if (index < 0)
- wait_for_completion(&inst->dq_complete);
- else
+ if (index < 0) {
+ rc = wait_for_completion_timeout(&inst->dq_complete,
+ TIMEOUT);
+ if (!rc) {
+ WFD_MSG_ERR(
+ "Timed out waiting for an input buffer\n");
+ rc = -ETIMEDOUT;
+ goto err_encode_frame;
+ }
+ } else {
break;
+ }
}
buffer = (struct v4l2_buffer) {
@@ -1176,6 +1195,7 @@
mark_index_busy(&inst->free_input_indices, index);
mutex_unlock(&inst->lock);
}
+err_encode_frame:
return rc;
}
diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c
index 1c42431..5eb359e 100644
--- a/drivers/mfd/wcd9xxx-core.c
+++ b/drivers/mfd/wcd9xxx-core.c
@@ -555,7 +555,6 @@
{WCD9XXX_IRQ_EAR_PA_OCPL_FAULT, false},
{WCD9XXX_IRQ_HPH_L_PA_STARTUP, false},
{WCD9XXX_IRQ_HPH_R_PA_STARTUP, false},
- {WCD9320_IRQ_EAR_PA_STARTUP, false},
{WCD9XXX_IRQ_RESERVED_0, false},
{WCD9XXX_IRQ_RESERVED_1, false},
{WCD9XXX_IRQ_MAD_AUDIO, false},
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index aad7fb3..99f6b5a 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -1,6 +1,6 @@
/*Qualcomm Secure Execution Environment Communicator (QSEECOM) driver
*
- * 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
@@ -2647,7 +2647,7 @@
if (ret) {
pr_err("scm call to generate key failed : %d\n", ret);
__qseecom_disable_clk(CLK_QSEE);
- return ret;
+ return -EFAULT;
}
switch (resp.result) {
@@ -2698,7 +2698,7 @@
if (ret) {
pr_err("scm call to delete key failed : %d\n", ret);
__qseecom_disable_clk(CLK_QSEE);
- return ret;
+ return -EFAULT;
}
switch (resp.result) {
@@ -2706,9 +2706,18 @@
break;
case QSEOS_RESULT_INCOMPLETE:
ret = __qseecom_process_incomplete_cmd(data, &resp);
- if (ret)
+ if (ret) {
pr_err("process_incomplete_cmd FAILED, resp.result %d\n",
resp.result);
+ if (resp.result == QSEOS_RESULT_FAIL_MAX_ATTEMPT) {
+ pr_debug("Max attempts to input password reached.\n");
+ ret = -ERANGE;
+ }
+ }
+ break;
+ case QSEOS_RESULT_FAIL_MAX_ATTEMPT:
+ pr_debug("Max attempts to input password reached.\n");
+ ret = -ERANGE;
break;
case QSEOS_RESULT_FAILURE:
default:
@@ -2746,7 +2755,7 @@
__qseecom_disable_clk(CLK_QSEE);
if (qseecom.qsee.instance != qseecom.ce_drv.instance)
__qseecom_disable_clk(CLK_CE_DRV);
- return ret;
+ return -EFAULT;
}
switch (resp.result) {
@@ -2754,9 +2763,18 @@
break;
case QSEOS_RESULT_INCOMPLETE:
ret = __qseecom_process_incomplete_cmd(data, &resp);
- if (ret)
+ if (ret) {
pr_err("process_incomplete_cmd FAILED, resp.result %d\n",
resp.result);
+ if (resp.result == QSEOS_RESULT_FAIL_MAX_ATTEMPT) {
+ pr_debug("Max attempts to input password reached.\n");
+ ret = -ERANGE;
+ }
+ }
+ break;
+ case QSEOS_RESULT_FAIL_MAX_ATTEMPT:
+ pr_debug("Max attempts to input password reached.\n");
+ ret = -ERANGE;
break;
case QSEOS_RESULT_FAILURE:
default:
@@ -2796,7 +2814,7 @@
__qseecom_disable_clk(CLK_QSEE);
if (qseecom.qsee.instance != qseecom.ce_drv.instance)
__qseecom_disable_clk(CLK_CE_DRV);
- return ret;
+ return -EFAULT;
}
switch (resp.result) {
@@ -2862,7 +2880,7 @@
&generate_key_ireq);
if (ret) {
pr_err("Failed to generate key on storage: %d\n", ret);
- return -EFAULT;
+ return ret;
}
set_key_ireq.qsee_command_id = QSEOS_SET_KEY;
@@ -2885,7 +2903,7 @@
if (ret) {
pr_err("Failed to create key: pipe %d, ce %d: %d\n",
pipe, ce_hw, ret);
- return -EFAULT;
+ return ret;
}
return ret;
@@ -2992,7 +3010,7 @@
&ireq);
if (ret) {
pr_err("Failed to update key info: %d\n", ret);
- return -EFAULT;
+ return ret;
}
return ret;
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index f4493a1..3eedc32 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -2670,7 +2670,8 @@
*/
if ((!req && !(mq->flags & MMC_QUEUE_NEW_REQUEST)) ||
((mq->flags & MMC_QUEUE_URGENT_REQUEST) &&
- !(mq->mqrq_cur->req->cmd_flags & REQ_URGENT))) {
+ !(mq->mqrq_cur->req->cmd_flags &
+ MMC_REQ_NOREINSERT_MASK))) {
if (mmc_card_need_bkops(card))
mmc_start_bkops(card, false);
/* release host only when there are no more requests */
@@ -3270,4 +3271,3 @@
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Multimedia Card (MMC) block device driver");
-
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index cef1a41..fa3dcdc 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -79,7 +79,8 @@
continue; /* fetch again */
} else if ((mq->flags & MMC_QUEUE_URGENT_REQUEST) &&
(mq->mqrq_cur->req &&
- !(mq->mqrq_cur->req->cmd_flags & REQ_URGENT))) {
+ !(mq->mqrq_cur->req->cmd_flags &
+ MMC_REQ_NOREINSERT_MASK))) {
/*
* clean current request when urgent request
* processing in progress and current request is
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 94a13f9..cd94960 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -895,9 +895,8 @@
mmc_post_req(host, host->areq->mrq, 0);
host->areq = NULL;
if (areq) {
- if (!(areq->cmd_flags & (REQ_URGENT
- | REQ_FUA
- | REQ_FLUSH))) {
+ if (!(areq->cmd_flags &
+ MMC_REQ_NOREINSERT_MASK)) {
areq->reinsert_req(areq);
mmc_post_req(host, areq->mrq, 0);
} else {
diff --git a/drivers/net/usb/rmnet_usb_ctrl.c b/drivers/net/usb/rmnet_usb_ctrl.c
index d1f3748..9682b48 100644
--- a/drivers/net/usb/rmnet_usb_ctrl.c
+++ b/drivers/net/usb/rmnet_usb_ctrl.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
@@ -38,10 +38,6 @@
#define ACM_CTRL_RI BIT(2)
#define ACM_CTRL_CD BIT(3)
-/* polling interval for Interrupt ep */
-#define HS_INTERVAL 7
-#define FS_LS_INTERVAL 3
-
/*echo modem_wait > /sys/class/hsicctl/hsicctlx/modem_wait*/
static ssize_t modem_wait_store(struct device *d, struct device_attribute *attr,
const char *buf, size_t n)
@@ -941,9 +937,7 @@
dev->intf->cur_altsetting->desc.bInterfaceNumber;
dev->in_ctlreq->wLength = cpu_to_le16(DEFAULT_READ_URB_LENGTH);
- interval = max((int)int_in->desc.bInterval,
- (udev->speed == USB_SPEED_HIGH) ? HS_INTERVAL
- : FS_LS_INTERVAL);
+ interval = int_in->desc.bInterval;
usb_fill_int_urb(dev->inturb, udev,
dev->int_pipe,
diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c
index d75c8a3..38c70a3 100644
--- a/drivers/net/wireless/wcnss/wcnss_wlan.c
+++ b/drivers/net/wireless/wcnss/wcnss_wlan.c
@@ -750,8 +750,29 @@
#ifdef CONFIG_WCNSS_REGISTER_DUMP_ON_BITE
void wcnss_log_debug_regs_on_bite(void)
{
- if (wcnss_hardware_type() == WCNSS_PRONTO_HW)
- wcnss_pronto_log_debug_regs();
+ struct platform_device *pdev = wcnss_get_platform_device();
+ struct clk *measure;
+ struct clk *wcnss_debug_mux;
+ unsigned long clk_rate;
+
+ if (wcnss_hardware_type() != WCNSS_PRONTO_HW)
+ return;
+
+ measure = clk_get(&pdev->dev, "measure");
+ wcnss_debug_mux = clk_get(&pdev->dev, "wcnss_debug");
+
+ if (!IS_ERR(measure) && !IS_ERR(wcnss_debug_mux)) {
+ if (clk_set_parent(measure, wcnss_debug_mux))
+ return;
+
+ clk_rate = clk_get_rate(measure);
+ pr_debug("wcnss: clock frequency is: %luHz\n", clk_rate);
+
+ if (clk_rate)
+ wcnss_pronto_log_debug_regs();
+ else
+ pr_err("clock frequency is zero, cannot access PMU or other registers\n");
+ }
}
#endif
diff --git a/drivers/nfc/nfc-nci.c b/drivers/nfc/nfc-nci.c
index b808f97..c6192ed 100644
--- a/drivers/nfc/nfc-nci.c
+++ b/drivers/nfc/nfc-nci.c
@@ -30,8 +30,10 @@
struct qca199x_platform_data {
unsigned int irq_gpio;
+ unsigned int irq_gpio_clk_req;
+ unsigned int clk_req_irq_num;
unsigned int dis_gpio;
- unsigned int ven_gpio;
+ unsigned int clkreq_gpio;
unsigned int reg;
const char *clk_src_name;
unsigned int clk_src_gpio;
@@ -52,32 +54,48 @@
#define MAX_PACKET_SIZE (PACKET_HEADER_SIZE_NCI + 255)
#define MAX_QCA_REG (116)
/* will timeout in approx. 100ms as 10us steps */
+#define NFC_RF_CLK_FREQ (19200000)
#define NTF_TIMEOUT (10000)
#define CORE_RESET_RSP_GID (0x60)
#define CORE_RESET_OID (0x00)
#define CORE_RST_NTF_LENGTH (0x02)
+static void clk_req_update(struct work_struct *work);
struct qca199x_dev {
wait_queue_head_t read_wq;
- struct mutex read_mutex;
- struct i2c_client *client;
- struct miscdevice qca199x_device;
- unsigned int irq_gpio;
- unsigned int dis_gpio;
- unsigned int ven_gpio;
- bool irq_enabled;
- bool sent_first_nci_write;
- spinlock_t irq_enabled_lock;
- unsigned int count_irq;
+ struct mutex read_mutex;
+ struct i2c_client *client;
+ struct miscdevice qca199x_device;
+ /* NFC_IRQ new NCI data available */
+ unsigned int irq_gpio;
+ /* CLK_REQ IRQ to signal the state has changed */
+ unsigned int irq_gpio_clk_req;
+ /* Actual IRQ no. assigned to CLK_REQ */
+ unsigned int clk_req_irq_num;
+ unsigned int dis_gpio;
+ unsigned int clkreq_gpio;
+ /* NFC_IRQ state */
+ bool irq_enabled;
+ bool sent_first_nci_write;
+ spinlock_t irq_enabled_lock;
+ unsigned int count_irq;
+ /* CLK_REQ IRQ state */
+ bool irq_enabled_clk_req;
+ spinlock_t irq_enabled_lock_clk_req;
+ unsigned int count_irq_clk_req;
enum nfcc_state state;
- unsigned int clk_src_gpio;
- const char *clk_src_name;
- struct clk *s_clk;
- bool clk_run;
+ /* CLK control */
+ unsigned int clk_src_gpio;
+ const char *clk_src_name;
+ struct clk *s_clk;
+ bool clk_run;
+ struct work_struct msm_clock_controll_work;
+ struct workqueue_struct *my_wq;
};
static int nfc_i2c_write(struct i2c_client *client, u8 *buf, int len);
+static int nfcc_hw_check(struct i2c_client *client, unsigned short curr_addr);
static int nfcc_initialise(struct i2c_client *client, unsigned short curr_addr);
static int qca199x_clock_select(struct qca199x_dev *qca199x_dev);
static int qca199x_clock_deselect(struct qca199x_dev *qca199x_dev);
@@ -92,9 +110,10 @@
/*
* FTM-RAW-I2C RD/WR MODE
*/
-static struct devicemode device_mode;
-static int ftm_raw_write_mode;
-static int ftm_werr_code;
+static struct devicemode device_mode;
+static int ftm_raw_write_mode;
+static int ftm_werr_code;
+
static void qca199x_init_stat(struct qca199x_dev *qca199x_dev)
{
@@ -156,6 +175,95 @@
return mask;
}
+/* Handlers for CLK_REQ */
+static void qca199x_disable_irq_clk_req(struct qca199x_dev *qca199x_dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&qca199x_dev->irq_enabled_lock_clk_req, flags);
+ if (qca199x_dev->irq_enabled_clk_req) {
+ disable_irq_nosync(qca199x_dev->clk_req_irq_num);
+ qca199x_dev->irq_enabled_clk_req = false;
+ }
+ spin_unlock_irqrestore(&qca199x_dev->irq_enabled_lock_clk_req, flags);
+}
+
+
+static void qca199x_enable_irq_clk_req(struct qca199x_dev *qca199x_dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&qca199x_dev->irq_enabled_lock_clk_req, flags);
+ if (!qca199x_dev->irq_enabled_clk_req) {
+ qca199x_dev->irq_enabled_clk_req = true;
+ enable_irq(qca199x_dev->clk_req_irq_num);
+ }
+ spin_unlock_irqrestore(&qca199x_dev->irq_enabled_lock_clk_req, flags);
+}
+
+
+static irqreturn_t qca199x_dev_irq_handler_clk_req(int irq, void *dev_id)
+{
+ struct qca199x_dev *qca199x_dev = dev_id;
+ unsigned long flags;
+
+ spin_lock_irqsave(&qca199x_dev->irq_enabled_lock_clk_req, flags);
+ qca199x_dev->count_irq_clk_req++;
+ spin_unlock_irqrestore(&qca199x_dev->irq_enabled_lock_clk_req, flags);
+
+ queue_work(qca199x_dev->my_wq, &qca199x_dev->msm_clock_controll_work);
+
+ return IRQ_HANDLED;
+}
+
+
+static struct gpiomux_setting nfc_clk_on = {
+ .func = GPIOMUX_FUNC_2,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+static struct gpiomux_setting nfc_clk_on_suspend = {
+ .func = GPIOMUX_FUNC_2,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+};
+static struct gpiomux_setting nfc_clk_off = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+};
+
+static void clk_req_update(struct work_struct *work)
+{
+ struct i2c_client *client;
+ struct qca199x_dev *qca199x_dev;
+ int gpio_clk_req_level = 0;
+
+ qca199x_dev = container_of(work, struct qca199x_dev,
+ msm_clock_controll_work);
+ client = qca199x_dev->client;
+
+ /* Read status level of CLK_REQ from NFC Controller, QCA199_x */
+ gpio_clk_req_level = gpio_get_value(qca199x_dev->irq_gpio_clk_req);
+ if (gpio_clk_req_level == 1) {
+ if (qca199x_dev->clk_run == false) {
+ msm_gpiomux_write(qca199x_dev->clk_src_gpio,
+ GPIOMUX_ACTIVE, &nfc_clk_on, NULL);
+ msm_gpiomux_write(qca199x_dev->clk_src_gpio,
+ GPIOMUX_SUSPENDED, &nfc_clk_on_suspend, NULL);
+ qca199x_dev->clk_run = true;
+ }
+ } else{
+ if (qca199x_dev->clk_run == true) {
+ msm_gpiomux_write(qca199x_dev->clk_src_gpio,
+ GPIOMUX_ACTIVE, &nfc_clk_off, NULL);
+ msm_gpiomux_write(qca199x_dev->clk_src_gpio,
+ GPIOMUX_SUSPENDED, &nfc_clk_off, NULL);
+ qca199x_dev->clk_run = false;
+ }
+ }
+}
+
/*
* ONLY for FTM-RAW-I2C Mode
* Required to instigate a read, which comes from DT layer. This means we need
@@ -396,7 +504,14 @@
filp->private_data = qca199x_dev;
qca199x_init_stat(qca199x_dev);
+ /* Enable interrupts from NFCC NFC_INT new NCI data available */
qca199x_enable_irq(qca199x_dev);
+
+ if ((!strcmp(qca199x_dev->clk_src_name, "GPCLK")) ||
+ (!strcmp(qca199x_dev->clk_src_name, "GPCLK2"))) {
+ /* Enable interrupts from NFCC CLK_REQ */
+ qca199x_enable_irq_clk_req(qca199x_dev);
+ }
dev_dbg(&qca199x_dev->client->dev,
"%d,%d\n", imajor(inode), iminor(inode));
return ret;
@@ -759,6 +874,33 @@
return r;
}
+/* Check for availability of qca199x_ NFC controller hardware */
+static int nfcc_hw_check(struct i2c_client *client, unsigned short curr_addr)
+{
+ int r = 0;
+ unsigned char buf = 0;
+
+ client->addr = curr_addr;
+ /* Set-up Addr 0. No data written */
+ r = i2c_master_send(client, &buf, 1);
+ if (r < 0)
+ goto err_presence_check;
+ buf = 0;
+ /* Read back from Addr 0 */
+ r = i2c_master_recv(client, &buf, 1);
+ if (r < 0)
+ goto err_presence_check;
+
+ r = 0;
+ return r;
+
+err_presence_check:
+ r = -ENXIO;
+ dev_err(&client->dev,
+ "nfc-nci nfcc_presence check - no NFCC available\n");
+ return r;
+}
+/* Initialise qca199x_ NFC controller hardware */
static int nfcc_initialise(struct i2c_client *client, unsigned short curr_addr)
{
int r = 0;
@@ -949,6 +1091,14 @@
goto err_invalid_dis_gpio;
}
if (qca199x_dev->clk_run == false) {
+ /* Set clock rate */
+ if ((!strcmp(qca199x_dev->clk_src_name, "GPCLK")) ||
+ (!strcmp(qca199x_dev->clk_src_name, "GPCLK2"))) {
+ r = clk_set_rate(qca199x_dev->s_clk, NFC_RF_CLK_FREQ);
+ if (r)
+ goto err_invalid_clk;
+ }
+
r = clk_prepare_enable(qca199x_dev->s_clk);
if (r)
goto err_invalid_clk;
@@ -990,10 +1140,6 @@
if (r)
return -EINVAL;
- r = of_property_read_u32(np, "qcom,clk-gpio", &pdata->ven_gpio);
- if (r)
- return -EINVAL;
-
pdata->dis_gpio = of_get_named_gpio(np, "qcom,dis-gpio", 0);
if ((!gpio_is_valid(pdata->dis_gpio)))
return -EINVAL;
@@ -1004,11 +1150,24 @@
r = of_property_read_string(np, "qcom,clk-src", &pdata->clk_src_name);
- if ((!strcmp(pdata->clk_src_name, "GPCLK")) ||
- (!strcmp(pdata->clk_src_name, "GPCLK2")))
- pdata->clk_src_gpio = of_get_named_gpio(np,
- "qcom,clk-en-gpio", 0);
+ if (strcmp(pdata->clk_src_name, "GPCLK2")) {
+ r = of_property_read_u32(np, "qcom,clk-gpio",
+ &pdata->clkreq_gpio);
+ if (r)
+ return -EINVAL;
+ }
+ if ((!strcmp(pdata->clk_src_name, "GPCLK")) ||
+ (!strcmp(pdata->clk_src_name, "GPCLK2"))) {
+ pdata->clk_src_gpio = of_get_named_gpio(np,
+ "qcom,clk-src-gpio", 0);
+ if ((!gpio_is_valid(pdata->clk_src_gpio)))
+ return -EINVAL;
+ pdata->irq_gpio_clk_req = of_get_named_gpio(np,
+ "qcom,clk-req-gpio", 0);
+ if ((!gpio_is_valid(pdata->irq_gpio_clk_req)))
+ return -EINVAL;
+ }
if (r)
return -EINVAL;
return r;
@@ -1057,40 +1216,23 @@
return -ENOMEM;
}
qca199x_dev->client = client;
- if (gpio_is_valid(platform_data->irq_gpio)) {
- r = gpio_request(platform_data->irq_gpio, "nfc_irq_gpio");
- if (r) {
- dev_err(&client->dev, "unable to request gpio [%d]\n",
- platform_data->irq_gpio);
- goto err_free_dev;
- }
- r = gpio_direction_input(platform_data->irq_gpio);
- if (r) {
- dev_err(&client->dev,
- "unable to set direction for gpio [%d]\n",
- platform_data->irq_gpio);
- goto err_irq;
- }
- gpio_to_irq(0);
- irqn = gpio_to_irq(platform_data->irq_gpio);
- if (irqn < 0) {
- r = irqn;
- goto err_irq;
- }
- client->irq = irqn;
+ /*
+ * To be efficient we need to test whether nfcc hardware is physically
+ * present before attempting further hardware initialisation.
+ * For this we need to be sure the device is in ULPM state by
+ * setting disable line low early on.
+ *
+ */
- } else {
- dev_err(&client->dev, "irq gpio not provided\n");
- goto err_free_dev;
- }
+
if (gpio_is_valid(platform_data->dis_gpio)) {
r = gpio_request(platform_data->dis_gpio, "nfc_reset_gpio");
if (r) {
dev_err(&client->dev,
"NFC: unable to request gpio [%d]\n",
platform_data->dis_gpio);
- goto err_irq;
+ goto err_free_dev;
}
r = gpio_direction_output(platform_data->dis_gpio, 1);
if (r) {
@@ -1101,7 +1243,75 @@
}
} else {
dev_err(&client->dev, "dis gpio not provided\n");
- goto err_irq;
+ goto err_free_dev;
+ }
+
+ /* Put device in ULPM */
+ gpio_set_value(platform_data->dis_gpio, 0);
+ r = nfcc_hw_check(client, platform_data->reg);
+ if (r) {
+ /* We don't think there is hardware but just in case HPD */
+ gpio_set_value(platform_data->dis_gpio, 1);
+ goto err_dis_gpio;
+ }
+
+ if (gpio_is_valid(platform_data->irq_gpio)) {
+ r = gpio_request(platform_data->irq_gpio, "nfc_irq_gpio");
+ if (r) {
+ dev_err(&client->dev, "unable to request gpio [%d]\n",
+ platform_data->irq_gpio);
+ goto err_dis_gpio;
+ }
+ r = gpio_direction_input(platform_data->irq_gpio);
+ if (r) {
+
+ dev_err(&client->dev,
+ "unable to set direction for gpio [%d]\n",
+ platform_data->irq_gpio);
+ goto err_irq;
+ }
+ irqn = gpio_to_irq(platform_data->irq_gpio);
+ if (irqn < 0) {
+ r = irqn;
+ goto err_irq;
+ }
+ client->irq = irqn;
+
+ } else {
+ dev_err(&client->dev, "irq gpio not provided\n");
+ goto err_dis_gpio;
+ }
+ /* Interrupt from NFCC CLK_REQ to handle REF_CLK
+ o/p gating/selection */
+ if ((!strcmp(platform_data->clk_src_name, "GPCLK")) ||
+ (!strcmp(platform_data->clk_src_name, "GPCLK2"))) {
+ if (gpio_is_valid(platform_data->irq_gpio_clk_req)) {
+ r = gpio_request(platform_data->irq_gpio_clk_req,
+ "nfc_irq_gpio_clk_en");
+ if (r) {
+ dev_err(&client->dev, "unable to request CLK_EN gpio [%d]\n",
+ platform_data->irq_gpio_clk_req);
+ goto err_irq;
+ }
+ r = gpio_direction_input(
+ platform_data->irq_gpio_clk_req);
+ if (r) {
+ dev_err(&client->dev,
+ "unable to set direction for CLK_EN gpio [%d]\n",
+ platform_data->irq_gpio_clk_req);
+ goto err_irq_clk;
+ }
+ gpio_to_irq(0);
+ irqn = gpio_to_irq(platform_data->irq_gpio_clk_req);
+ if (irqn < 0) {
+ r = irqn;
+ goto err_irq_clk;
+ }
+ platform_data->clk_req_irq_num = irqn;
+ } else {
+ dev_err(&client->dev, "irq CLK_EN gpio not provided\n");
+ goto err_irq;
+ }
}
/* Get the clock source name and gpio from from Device Tree */
qca199x_dev->clk_src_name = platform_data->clk_src_name;
@@ -1112,37 +1322,49 @@
if (r == -1)
goto err_clk;
else
- goto err_dis_gpio;
+ goto err_irq_clk;
}
- platform_data->ven_gpio = of_get_named_gpio(node,
- "qcom,clk-gpio", 0);
- if (gpio_is_valid(platform_data->ven_gpio)) {
- r = gpio_request(platform_data->ven_gpio, "nfc_ven_gpio");
- if (r) {
- dev_err(&client->dev, "unable to request gpio [%d]\n",
- platform_data->ven_gpio);
- goto err_ven_gpio;
+ 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");
+ if (r) {
+ dev_err(&client->dev, "unable to request gpio [%d]\n",
+ platform_data->clkreq_gpio);
+ goto err_clkreq_gpio;
+ }
+ r = gpio_direction_input(platform_data->clkreq_gpio);
+ if (r) {
+ dev_err(&client->dev,
+ "unable to set direction for gpio [%d]\n",
+ platform_data->clkreq_gpio);
+ goto err_clkreq_gpio;
+ }
+ } else {
+ dev_err(&client->dev, "clkreq gpio not provided\n");
+ goto err_clk;
}
- r = gpio_direction_input(platform_data->ven_gpio);
- if (r) {
- dev_err(&client->dev,
- "unable to set direction for gpio [%d]\n",
- platform_data->ven_gpio);
- goto err_ven_gpio;
- }
- } else {
- dev_err(&client->dev, "ven gpio not provided\n");
- goto err_clk;
+ qca199x_dev->clkreq_gpio = platform_data->clkreq_gpio;
}
qca199x_dev->dis_gpio = platform_data->dis_gpio;
qca199x_dev->irq_gpio = platform_data->irq_gpio;
- qca199x_dev->ven_gpio = platform_data->ven_gpio;
+ if ((!strcmp(platform_data->clk_src_name, "GPCLK")) ||
+ (!strcmp(platform_data->clk_src_name, "GPCLK2"))) {
+ qca199x_dev->irq_gpio_clk_req =
+ platform_data->irq_gpio_clk_req;
+ qca199x_dev->clk_req_irq_num =
+ platform_data->clk_req_irq_num;
+ }
/* init mutex and queues */
init_waitqueue_head(&qca199x_dev->read_wq);
mutex_init(&qca199x_dev->read_mutex);
spin_lock_init(&qca199x_dev->irq_enabled_lock);
+ spin_lock_init(&qca199x_dev->irq_enabled_lock_clk_req);
qca199x_dev->qca199x_device.minor = MISC_DYNAMIC_MINOR;
qca199x_dev->qca199x_device.name = "nfc-nci";
@@ -1154,6 +1376,27 @@
goto err_misc_register;
}
+
+ /*
+ * Reboot the NFCC now that all resources are ready
+ *
+ * The NFCC takes time to transition between power states.
+ * We wait 20uS for the NFCC to shutdown. (HPD)
+ * We wait 100uS for the NFCC to boot into ULPM.
+ */
+ gpio_set_value(platform_data->dis_gpio, 1);/* HPD */
+ msleep(20);
+ gpio_set_value(platform_data->dis_gpio, 0);/* ULPM */
+ msleep(100);
+
+
+ /* Here we perform a second presence check. */
+ r = nfcc_hw_check(client, platform_data->reg);
+ if (r) {
+ /* We don't think there is hardware but just in case HPD */
+ gpio_set_value(platform_data->dis_gpio, 1);
+ goto err_nfcc_not_present;
+ }
regulators.regulator = regulator_get(&client->dev, regulators.name);
if (IS_ERR(regulators.regulator)) {
r = PTR_ERR(regulators.regulator);
@@ -1172,6 +1415,7 @@
* for reading. It is cleared when all data has been read.
*/
device_mode.handle_flavour = UNSOLICITED_MODE;
+ /* NFC_INT IRQ */
qca199x_dev->irq_enabled = true;
r = request_irq(client->irq, qca199x_dev_irq_handler,
IRQF_TRIGGER_RISING, client->name, qca199x_dev);
@@ -1180,6 +1424,31 @@
goto err_request_irq_failed;
}
qca199x_disable_irq(qca199x_dev);
+ /* CLK_REQ IRQ */
+ if ((!strcmp(platform_data->clk_src_name, "GPCLK")) ||
+ (!strcmp(platform_data->clk_src_name, "GPCLK2"))) {
+ r = request_irq(qca199x_dev->clk_req_irq_num,
+ qca199x_dev_irq_handler_clk_req,
+ (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING),
+ client->name, qca199x_dev);
+ if (r) {
+ dev_err(&client->dev,
+ "nfc-nci probe: request_irq failed. irq no = %d\n, main irq = %d",
+ qca199x_dev->clk_req_irq_num, client->irq);
+ goto err_request_irq_failed;
+ }
+ qca199x_dev->irq_enabled_clk_req = true;
+ qca199x_disable_irq_clk_req(qca199x_dev);
+
+
+ qca199x_dev->my_wq =
+ create_singlethread_workqueue("qca1990x_CLK_REQ_queue");
+ if (!qca199x_dev->my_wq)
+ goto err_create_workq;
+
+ INIT_WORK(&qca199x_dev->msm_clock_controll_work,
+ clk_req_update);
+ }
i2c_set_clientdata(client, qca199x_dev);
gpio_set_value(platform_data->dis_gpio, 1);
dev_dbg(&client->dev,
@@ -1187,28 +1456,33 @@
__func__);
return 0;
+err_create_workq:
+ dev_err(&client->dev,
+ "nfc-nci probe: %s, work_queue creation failure\n",
+ __func__);
+ free_irq(client->irq, qca199x_dev);
+err_nfcc_not_present:
err_request_irq_failed:
misc_deregister(&qca199x_dev->qca199x_device);
err_misc_register:
mutex_destroy(&qca199x_dev->read_mutex);
-err_ven_gpio:
- gpio_free(platform_data->ven_gpio);
+err_clkreq_gpio:
+ if (strcmp(platform_data->clk_src_name, "GPCLK2"))
+ gpio_free(platform_data->clkreq_gpio);
err_clk:
qca199x_clock_deselect(qca199x_dev);
-err_dis_gpio:
- r = gpio_direction_input(platform_data->dis_gpio);
- if (r)
- dev_err(&client->dev, "nfc-nci probe: Unable to set direction\n");
+err_irq_clk:
if ((!strcmp(platform_data->clk_src_name, "GPCLK")) ||
- (!strcmp(platform_data->clk_src_name, "GPCLK2"))) {
- r = gpio_direction_input(platform_data->clk_src_gpio);
+ (!strcmp(platform_data->clk_src_name, "GPCLK2"))) {
+ r = gpio_direction_input(platform_data->irq_gpio_clk_req);
if (r)
dev_err(&client->dev, "nfc-nci probe: Unable to set direction\n");
- gpio_free(platform_data->clk_src_gpio);
+ gpio_free(platform_data->irq_gpio_clk_req);
}
- gpio_free(platform_data->dis_gpio);
err_irq:
gpio_free(platform_data->irq_gpio);
+err_dis_gpio:
+ gpio_free(platform_data->dis_gpio);
err_free_dev:
kfree(qca199x_dev);
return r;
@@ -1223,8 +1497,13 @@
misc_deregister(&qca199x_dev->qca199x_device);
mutex_destroy(&qca199x_dev->read_mutex);
gpio_free(qca199x_dev->irq_gpio);
+ if ((!strcmp(qca199x_dev->clk_src_name, "GPCLK")) ||
+ (!strcmp(qca199x_dev->clk_src_name, "GPCLK2"))) {
+ gpio_free(qca199x_dev->irq_gpio_clk_req);
+ }
gpio_free(qca199x_dev->dis_gpio);
- gpio_free(qca199x_dev->ven_gpio);
+ if (strcmp(qca199x_dev->clk_src_name, "GPCLK2"))
+ gpio_free(qca199x_dev->clkreq_gpio);
kfree(qca199x_dev);
return 0;
diff --git a/drivers/platform/msm/ipa/teth_bridge.c b/drivers/platform/msm/ipa/teth_bridge.c
index 79ec0c0..d08ac64 100644
--- a/drivers/platform/msm/ipa/teth_bridge.c
+++ b/drivers/platform/msm/ipa/teth_bridge.c
@@ -22,7 +22,6 @@
#include <linux/mutex.h>
#include <linux/skbuff.h>
#include <linux/types.h>
-#include <mach/bam_dmux.h>
#include <mach/ipa.h>
#include <mach/sps.h>
#include "ipa_i.h"
diff --git a/drivers/platform/msm/sps/sps_bam.c b/drivers/platform/msm/sps/sps_bam.c
index bd4328a..ad5e241 100644
--- a/drivers/platform/msm/sps/sps_bam.c
+++ b/drivers/platform/msm/sps/sps_bam.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
@@ -169,6 +169,7 @@
list_for_each_entry(pipe, &dev->pipes_q, list) {
/* Check this pipe's bit in the source mask */
if (BAM_PIPE_IS_ASSIGNED(pipe)
+ && (!pipe->disconnecting)
&& (source & pipe->pipe_index_mask)) {
/* This pipe has an interrupt pending */
pipe_handler(dev, pipe);
@@ -585,6 +586,7 @@
pipe->mode = -1;
pipe->num_descs = 0;
pipe->desc_size = 0;
+ pipe->disconnecting = false;
memset(&pipe->sys, 0, sizeof(pipe->sys));
INIT_LIST_HEAD(&pipe->sys.events_q);
}
diff --git a/drivers/platform/msm/sps/sps_bam.h b/drivers/platform/msm/sps/sps_bam.h
index da5dafd..a20156b 100644
--- a/drivers/platform/msm/sps/sps_bam.h
+++ b/drivers/platform/msm/sps/sps_bam.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
@@ -185,6 +185,7 @@
/* System mode control */
struct sps_bam_sys_mode sys;
+ bool disconnecting;
};
/* BAM device descriptor */
diff --git a/drivers/platform/msm/sps/sps_rm.c b/drivers/platform/msm/sps/sps_rm.c
index 7d7e1a6..d8c7a4d 100644
--- a/drivers/platform/msm/sps/sps_rm.c
+++ b/drivers/platform/msm/sps/sps_rm.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
@@ -556,11 +556,8 @@
{
struct sps_connection *map = (void *)pipe->map;
struct sps_connect *cfg = &pipe->connect;
- struct sps_bam *bam = pipe->bam;
- unsigned long flags;
mutex_lock(&sps_rm->lock);
- spin_lock_irqsave(&bam->isr_lock, flags);
/* Free this connection */
if (cfg->mode == SPS_MODE_SRC)
@@ -574,7 +571,6 @@
sps_rm_remove_ref(map);
- spin_unlock_irqrestore(&bam->isr_lock, flags);
mutex_unlock(&sps_rm->lock);
return 0;
@@ -800,8 +796,9 @@
synchronize_irq(bam->props.irq);
spin_lock_irqsave(&bam->isr_lock, flags);
- result = sps_bam_pipe_disconnect(pipe->bam, pipe_index);
+ pipe->disconnecting = true;
spin_unlock_irqrestore(&bam->isr_lock, flags);
+ result = sps_bam_pipe_disconnect(pipe->bam, pipe_index);
if (result) {
SPS_ERR("sps:Failed to disconnect BAM 0x%x pipe %d",
pipe->bam->props.phys_addr,
diff --git a/drivers/power/qpnp-bms.c b/drivers/power/qpnp-bms.c
index 2fcab32..2559ff9 100644
--- a/drivers/power/qpnp-bms.c
+++ b/drivers/power/qpnp-bms.c
@@ -407,6 +407,14 @@
}
}
+static void disable_bms_irq_nosync(struct bms_irq *irq)
+{
+ if (!__test_and_set_bit(0, &irq->disabled)) {
+ disable_irq_nosync(irq->irq);
+ pr_debug("disabled irq %d\n", irq->irq);
+ }
+}
+
#define HOLD_OREG_DATA BIT(0)
static int lock_output_data(struct qpnp_bms_chip *chip)
{
@@ -3562,7 +3570,7 @@
struct qpnp_bms_chip *chip = _chip;
pr_debug("sw_cc_thr irq triggered\n");
- disable_bms_irq(&chip->sw_cc_thr_irq);
+ disable_bms_irq_nosync(&chip->sw_cc_thr_irq);
bms_stay_awake(&chip->soc_wake_source);
schedule_work(&chip->recalc_work);
return IRQ_HANDLED;
@@ -4285,6 +4293,12 @@
goto error_setup;
}
+ rc = bms_request_irqs(chip);
+ if (rc) {
+ pr_err("error requesting bms irqs, rc = %d\n", rc);
+ goto error_setup;
+ }
+
battery_insertion_check(chip);
batfet_status_check(chip);
battery_status_check(chip);
@@ -4318,12 +4332,6 @@
goto unregister_dc;
}
- rc = bms_request_irqs(chip);
- if (rc) {
- pr_err("error requesting bms irqs, rc = %d\n", rc);
- goto unregister_dc;
- }
-
pr_info("probe success: soc =%d vbatt = %d ocv = %d r_sense_uohm = %u warm_reset = %d\n",
get_prop_bms_capacity(chip), vbatt, chip->last_ocv_uv,
chip->r_sense_uohm, warm_reset);
diff --git a/drivers/power/qpnp-charger.c b/drivers/power/qpnp-charger.c
index 1223fe0..40c5568 100644
--- a/drivers/power/qpnp-charger.c
+++ b/drivers/power/qpnp-charger.c
@@ -113,6 +113,7 @@
#define USB_OCP_THR 0x52
#define USB_OCP_CLR 0x53
#define BAT_IF_TEMP_STATUS 0x09
+#define BOOST_ILIM 0x78
#define REG_OFFSET_PERP_SUBTYPE 0x05
@@ -217,6 +218,7 @@
struct qpnp_chg_irq {
int irq;
unsigned long disabled;
+ unsigned long wake_enable;
};
struct qpnp_chg_regulator {
@@ -364,6 +366,7 @@
struct work_struct soc_check_work;
struct delayed_work aicl_check_work;
struct work_struct insertion_ocv_work;
+ struct work_struct ocp_clear_work;
struct qpnp_chg_regulator otg_vreg;
struct qpnp_chg_regulator boost_vreg;
struct qpnp_chg_regulator batfet_vreg;
@@ -531,6 +534,24 @@
}
}
+static void
+qpnp_chg_irq_wake_enable(struct qpnp_chg_irq *irq)
+{
+ if (!__test_and_set_bit(0, &irq->wake_enable)) {
+ pr_debug("number = %d\n", irq->irq);
+ enable_irq_wake(irq->irq);
+ }
+}
+
+static void
+qpnp_chg_irq_wake_disable(struct qpnp_chg_irq *irq)
+{
+ if (__test_and_clear_bit(0, &irq->wake_enable)) {
+ pr_debug("number = %d\n", irq->irq);
+ disable_irq_wake(irq->irq);
+ }
+}
+
#define USB_OTG_EN_BIT BIT(0)
static int
qpnp_chg_is_otg_en_set(struct qpnp_chg_chip *chip)
@@ -1215,10 +1236,36 @@
qpnp_chg_usb_usb_ocp_irq_handler(int irq, void *_chip)
{
struct qpnp_chg_chip *chip = _chip;
- int rc;
pr_debug("usb-ocp triggered\n");
+ schedule_work(&chip->ocp_clear_work);
+
+ return IRQ_HANDLED;
+}
+
+#define BOOST_ILIMIT_MIN 0x07
+#define BOOST_ILIMIT_DEF 0x02
+#define BOOST_ILIMT_MASK 0xFF
+static void
+qpnp_chg_ocp_clear_work(struct work_struct *work)
+{
+ int rc;
+ u8 usb_sts;
+ struct qpnp_chg_chip *chip = container_of(work,
+ struct qpnp_chg_chip, ocp_clear_work);
+
+ if (chip->type == SMBBP) {
+ rc = qpnp_chg_masked_write(chip,
+ chip->boost_base + BOOST_ILIM,
+ BOOST_ILIMT_MASK,
+ BOOST_ILIMIT_MIN, 1);
+ if (rc) {
+ pr_err("Failed to turn configure ilim rc = %d\n", rc);
+ return;
+ }
+ }
+
rc = qpnp_chg_masked_write(chip,
chip->usb_chgpth_base + USB_OCP_CLR,
OCP_CLR_BIT,
@@ -1234,7 +1281,29 @@
if (rc)
pr_err("Failed to turn off usb ovp rc = %d\n", rc);
- return IRQ_HANDLED;
+ if (chip->type == SMBBP) {
+ /* Wait for OCP circuitry to be powered up */
+ msleep(100);
+ rc = qpnp_chg_read(chip, &usb_sts,
+ INT_RT_STS(chip->usb_chgpth_base), 1);
+ if (rc) {
+ pr_err("failed to read interrupt sts %d\n", rc);
+ return;
+ }
+
+ if (usb_sts & COARSE_DET_USB_IRQ) {
+ rc = qpnp_chg_masked_write(chip,
+ chip->boost_base + BOOST_ILIM,
+ BOOST_ILIMT_MASK,
+ BOOST_ILIMIT_DEF, 1);
+ if (rc) {
+ pr_err("Failed to set ilim rc = %d\n", rc);
+ return;
+ }
+ } else {
+ pr_warn_ratelimited("USB short to GND detected!\n");
+ }
+ }
}
#define QPNP_CHG_VDDMAX_MIN 3400
@@ -1678,6 +1747,8 @@
u8 chgr_sts;
int rc;
+ qpnp_chg_irq_wake_disable(&chip->chg_fastchg);
+
rc = qpnp_chg_read(chip, &chgr_sts, INT_RT_STS(chip->chgr_base), 1);
if (rc)
pr_err("failed to read interrupt sts %d\n", rc);
@@ -1781,6 +1852,17 @@
if (!qpnp_chg_is_otg_en_set(chip))
return 0;
+ if (chip->type == SMBBP) {
+ rc = qpnp_chg_masked_write(chip,
+ chip->boost_base + BOOST_ILIM,
+ BOOST_ILIMT_MASK,
+ BOOST_ILIMIT_DEF, 1);
+ if (rc) {
+ pr_err("Failed to set ilim rc = %d\n", rc);
+ return rc;
+ }
+ }
+
/* enable usb ovp fet */
rc = qpnp_chg_masked_write(chip,
chip->usb_chgpth_base + CHGR_USB_USB_OTG_CTL,
@@ -1804,11 +1886,23 @@
switch_usb_to_host_mode(struct qpnp_chg_chip *chip)
{
int rc;
+ u8 usb_sts;
pr_debug("switch to host mode\n");
if (qpnp_chg_is_otg_en_set(chip))
return 0;
+ if (chip->type == SMBBP) {
+ rc = qpnp_chg_masked_write(chip,
+ chip->boost_base + BOOST_ILIM,
+ BOOST_ILIMT_MASK,
+ BOOST_ILIMIT_MIN, 1);
+ if (rc) {
+ pr_err("Failed to turn configure ilim rc = %d\n", rc);
+ return rc;
+ }
+ }
+
if (!qpnp_chg_is_dc_chg_plugged_in(chip)) {
rc = qpnp_chg_force_run_on_batt(chip, 1);
if (rc) {
@@ -1827,6 +1921,30 @@
return rc;
}
+ if (chip->type == SMBBP) {
+ /* Wait for OCP circuitry to be powered up */
+ msleep(100);
+ rc = qpnp_chg_read(chip, &usb_sts,
+ INT_RT_STS(chip->usb_chgpth_base), 1);
+ if (rc) {
+ pr_err("failed to read interrupt sts %d\n", rc);
+ return rc;
+ }
+
+ if (usb_sts & COARSE_DET_USB_IRQ) {
+ rc = qpnp_chg_masked_write(chip,
+ chip->boost_base + BOOST_ILIM,
+ BOOST_ILIMT_MASK,
+ BOOST_ILIMIT_DEF, 1);
+ if (rc) {
+ pr_err("Failed to set ilim rc = %d\n", rc);
+ return rc;
+ }
+ } else {
+ pr_warn_ratelimited("USB short to GND detected!\n");
+ }
+ }
+
return 0;
}
@@ -2148,6 +2266,7 @@
&& soc <= chip->soc_resume_limit) {
pr_debug("resuming charging at %d%% soc\n", soc);
chip->resuming_charging = true;
+ qpnp_chg_irq_wake_enable(&chip->chg_fastchg);
qpnp_chg_set_appropriate_vbatdet(chip);
qpnp_chg_charge_en(chip, !chip->charging_disabled);
}
@@ -3848,10 +3967,10 @@
return rc;
}
- enable_irq_wake(chip->chg_trklchg.irq);
- enable_irq_wake(chip->chg_failed.irq);
+ qpnp_chg_irq_wake_enable(&chip->chg_trklchg);
+ qpnp_chg_irq_wake_enable(&chip->chg_failed);
qpnp_chg_disable_irq(&chip->chg_vbatdet_lo);
- enable_irq_wake(chip->chg_vbatdet_lo.irq);
+ qpnp_chg_irq_wake_enable(&chip->chg_vbatdet_lo);
break;
case SMBB_BAT_IF_SUBTYPE:
@@ -3874,7 +3993,7 @@
return rc;
}
- enable_irq_wake(chip->batt_pres.irq);
+ qpnp_chg_irq_wake_enable(&chip->batt_pres);
chip->batt_temp_ok.irq = spmi_get_irq_byname(spmi,
spmi_resource, "bat-temp-ok");
@@ -3893,7 +4012,7 @@
}
qpnp_chg_bat_if_batt_temp_irq_handler(0, chip);
- enable_irq_wake(chip->batt_temp_ok.irq);
+ qpnp_chg_irq_wake_enable(&chip->batt_temp_ok);
break;
case SMBB_BUCK_SUBTYPE:
@@ -3975,11 +4094,11 @@
return rc;
}
- enable_irq_wake(chip->usb_ocp.irq);
+ qpnp_chg_irq_wake_enable(&chip->usb_ocp);
}
- enable_irq_wake(chip->usbin_valid.irq);
- enable_irq_wake(chip->chg_gone.irq);
+ qpnp_chg_irq_wake_enable(&chip->usbin_valid);
+ qpnp_chg_irq_wake_enable(&chip->chg_gone);
break;
case SMBB_DC_CHGPTH_SUBTYPE:
chip->dcin_valid.irq = spmi_get_irq_byname(spmi,
@@ -3998,7 +4117,7 @@
return rc;
}
- enable_irq_wake(chip->dcin_valid.irq);
+ qpnp_chg_irq_wake_enable(&chip->dcin_valid);
break;
}
}
@@ -4527,6 +4646,8 @@
INIT_WORK(&chip->reduce_power_stage_work,
qpnp_chg_reduce_power_stage_work);
mutex_init(&chip->batfet_vreg_lock);
+ INIT_WORK(&chip->ocp_clear_work,
+ qpnp_chg_ocp_clear_work);
INIT_WORK(&chip->batfet_lcl_work,
qpnp_chg_batfet_lcl_work);
INIT_WORK(&chip->insertion_ocv_work,
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index fe5af3a..e9994c2 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -102,6 +102,17 @@
This driver supports the voltage regulators of DA9052-BC and
DA9053-AA/Bx PMIC.
+config REGULATOR_FAN53555
+ tristate "Fairchild FAN53555 Regulator"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ This driver supports Fairchild FAN53555 Digitally Programmable
+ TinyBuck Regulator. The FAN53555 is a step-down switching voltage
+ regulator that delivers a digitally programmable output from an
+ input voltage supply of 2.5V to 5.5V. The output voltage is
+ programmed through an I2C interface.
+
config REGULATOR_ANATOP
tristate "Freescale i.MX on-chip ANATOP LDO regulators"
depends on MFD_ANATOP
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index eb07c97..d96e793 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -20,6 +20,7 @@
obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o
obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o
+obj-$(CONFIG_REGULATOR_FAN53555) += fan53555.o
obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o
obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
obj-$(CONFIG_REGULATOR_LP3972) += lp3972.o
diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c
new file mode 100644
index 0000000..6986a52
--- /dev/null
+++ b/drivers/regulator/fan53555.c
@@ -0,0 +1,658 @@
+/*
+ * FAN53555 Fairchild Digitally Programmable TinyBuck Regulator Driver.
+ *
+ * Supported Part Numbers:
+ * FAN53555UC00X/01X/03X/04X/05X
+ *
+ * Copyright (c) 2012 Marvell Technology Ltd.
+ * Yunfan Zhang <yfzhang@marvell.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/module.h>
+#include <linux/param.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/regulator/fan53555.h>
+
+/* Voltage setting */
+#define FAN53555_VSEL0 0x00
+#define FAN53555_VSEL1 0x01
+/* Control register */
+#define FAN53555_CONTROL 0x02
+/* IC Type */
+#define FAN53555_ID1 0x03
+/* IC mask version */
+#define FAN53555_ID2 0x04
+/* Monitor register */
+#define FAN53555_MONITOR 0x05
+
+/* VSEL bit definitions */
+#define VSEL_BUCK_EN (1 << 7)
+#define VSEL_MODE (1 << 6)
+#define VSEL_NSEL_MASK 0x3F
+#define VSEL_FULL_MASK 0xFF
+/* Chip ID and Verison */
+#define DIE_ID 0x0F /* ID1 */
+#define DIE_REV 0x0F /* ID2 */
+#define DIE_13_REV 0x0F /* DIE Revsion ID of 13 option */
+
+/* Control bit definitions */
+#define CTL_OUTPUT_DISCHG (1 << 7)
+#define CTL_SLEW_MASK (0x7 << 4)
+#define CTL_SLEW_SHIFT 4
+#define CTL_RESET (1 << 2)
+
+#define FAN53555_NVOLTAGES 64 /* Numbers of voltages */
+
+/* IC Type */
+enum {
+ FAN53555_CHIP_ID_00 = 0,
+ FAN53555_CHIP_ID_01,
+ FAN53555_CHIP_ID_02,
+ FAN53555_CHIP_ID_03,
+ FAN53555_CHIP_ID_04,
+ FAN53555_CHIP_ID_05,
+};
+
+static const int slew_rate_plan[] = {
+ 64000,
+ 32000,
+ 16000,
+ 8000,
+ 4000,
+ 2000,
+ 1000,
+ 500
+};
+
+struct fan53555_device_info {
+ struct regmap *regmap;
+ struct device *dev;
+ struct regulator_desc desc;
+ struct regulator_dev *rdev;
+ struct regulator_init_data *regulator;
+ /* IC Type and Rev */
+ int chip_id;
+ int chip_rev;
+ /* Voltage setting register */
+ unsigned int vol_reg;
+ unsigned int sleep_reg;
+ /* Voltage range and step(linear) */
+ unsigned int vsel_min;
+ unsigned int vsel_step;
+ /* Voltage slew rate limiting */
+ unsigned int slew_rate;
+ /* Sleep voltage cache */
+ unsigned int sleep_vol_cache;
+
+ bool disable_suspend;
+};
+
+static int fan53555_get_voltage(struct regulator_dev *rdev)
+{
+ struct fan53555_device_info *di = rdev_get_drvdata(rdev);
+ unsigned int val;
+ int rc;
+
+ rc = regmap_read(di->regmap, di->vol_reg, &val);
+ if (rc) {
+ dev_err(di->dev, "Unable to get voltage rc(%d)", rc);
+ return rc;
+ }
+
+ return ((val & VSEL_NSEL_MASK) * di->vsel_step) +
+ di->vsel_min;
+}
+
+static int fan53555_set_voltage(struct regulator_dev *rdev,
+ int min_uv, int max_uv, unsigned *selector)
+{
+ struct fan53555_device_info *di = rdev_get_drvdata(rdev);
+ int rc, set_val, cur_uv, new_uv;
+
+ set_val = DIV_ROUND_UP(min_uv - di->vsel_min, di->vsel_step);
+ new_uv = (set_val * di->vsel_step) + di->vsel_min;
+
+ if (new_uv > max_uv || max_uv < di->vsel_min) {
+ dev_err(di->dev, "Unable to set voltage (%d %d)\n",
+ min_uv, max_uv);
+ }
+
+ cur_uv = fan53555_get_voltage(rdev);
+ if (cur_uv < 0)
+ return cur_uv;
+
+ rc = regmap_update_bits(di->regmap, di->vol_reg, VSEL_NSEL_MASK,
+ set_val);
+ if (rc) {
+ dev_err(di->dev, "Unable to set voltage (%d %d)\n",
+ min_uv, max_uv);
+ } else {
+ udelay(DIV_ROUND_UP(abs(new_uv - cur_uv),
+ slew_rate_plan[di->slew_rate]));
+ *selector = set_val;
+ }
+
+ return rc;
+}
+
+static int fan53555_list_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ struct fan53555_device_info *di = rdev_get_drvdata(rdev);
+
+ if (selector >= di->desc.n_voltages)
+ return 0;
+
+ return selector * di->vsel_step + di->vsel_min;
+}
+
+static int fan53555_set_suspend_voltage(struct regulator_dev *rdev, int uV)
+{
+ struct fan53555_device_info *di = rdev_get_drvdata(rdev);
+ int ret, val;
+
+ if (di->sleep_vol_cache == uV)
+ return 0;
+ ret = fan53555_set_voltage(rdev, uV, uV, &val);
+ if (ret < 0)
+ return -EINVAL;
+ ret = regmap_update_bits(di->regmap, di->sleep_reg,
+ VSEL_NSEL_MASK, val);
+ if (ret < 0)
+ return -EINVAL;
+ /* Cache the sleep voltage setting.
+ * Might not be the real voltage which is rounded */
+ di->sleep_vol_cache = uV;
+
+ return 0;
+}
+
+static int fan53555_enable(struct regulator_dev *rdev)
+{
+ struct fan53555_device_info *di = rdev_get_drvdata(rdev);
+ int ret;
+
+ ret = regmap_update_bits(di->regmap, di->vol_reg,
+ VSEL_BUCK_EN, VSEL_BUCK_EN);
+ if (ret)
+ dev_err(di->dev, "Unable to enable regulator, ret = %d\n",
+ ret);
+ return ret;
+}
+
+static int fan53555_disable(struct regulator_dev *rdev)
+{
+ struct fan53555_device_info *di = rdev_get_drvdata(rdev);
+ int ret;
+
+ ret = regmap_update_bits(di->regmap, di->vol_reg,
+ VSEL_BUCK_EN, 0);
+ if (ret)
+ dev_err(di->dev, "Unable to set disable regulator, ret = %d\n",
+ ret);
+ return ret;
+}
+
+static int fan53555_is_enabled(struct regulator_dev *rdev)
+{
+ struct fan53555_device_info *di = rdev_get_drvdata(rdev);
+ int ret;
+ u32 val;
+
+ ret = regmap_read(di->regmap, di->vol_reg, &val);
+ if (ret) {
+ dev_err(di->dev, "Unable to get regulator status, ret = %d\n",
+ ret);
+ return ret;
+ } else {
+ return val & VSEL_BUCK_EN;
+ }
+}
+
+static int fan53555_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ struct fan53555_device_info *di = rdev_get_drvdata(rdev);
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ regmap_update_bits(di->regmap, di->vol_reg,
+ VSEL_MODE, VSEL_MODE);
+ break;
+ case REGULATOR_MODE_NORMAL:
+ regmap_update_bits(di->regmap, di->vol_reg, VSEL_MODE, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static unsigned int fan53555_get_mode(struct regulator_dev *rdev)
+{
+ struct fan53555_device_info *di = rdev_get_drvdata(rdev);
+ unsigned int val;
+ int ret = 0;
+
+ ret = regmap_read(di->regmap, di->vol_reg, &val);
+ if (ret < 0)
+ return ret;
+ if (val & VSEL_MODE)
+ return REGULATOR_MODE_FAST;
+ else
+ return REGULATOR_MODE_NORMAL;
+}
+
+static struct regulator_ops fan53555_regulator_ops = {
+ .set_voltage = fan53555_set_voltage,
+ .get_voltage = fan53555_get_voltage,
+ .list_voltage = fan53555_list_voltage,
+ .set_suspend_voltage = fan53555_set_suspend_voltage,
+ .enable = fan53555_enable,
+ .disable = fan53555_disable,
+ .is_enabled = fan53555_is_enabled,
+ .set_mode = fan53555_set_mode,
+ .get_mode = fan53555_get_mode,
+};
+
+static struct regulator_ops fan53555_regulator_disable_suspend_ops = {
+ .set_voltage = fan53555_set_voltage,
+ .get_voltage = fan53555_get_voltage,
+ .list_voltage = fan53555_list_voltage,
+ .enable = fan53555_enable,
+ .disable = fan53555_disable,
+ .is_enabled = fan53555_is_enabled,
+ .set_mode = fan53555_set_mode,
+ .get_mode = fan53555_get_mode,
+};
+
+/* For 00,01,03,05 options:
+ * VOUT = 0.60V + NSELx * 10mV, from 0.60 to 1.23V.
+ * For 04 option:
+ * VOUT = 0.603V + NSELx * 12.826mV, from 0.603 to 1.411V.
+ * For 13 option:
+ * 13 option, its DIE ID is 0x00 and DIE_REV is 0x0F.
+ * VOUT = 0.80V + NSELx * 10mV, from 0.80 to 1.43V.
+ * */
+static int fan53555_device_setup(struct fan53555_device_info *di,
+ struct fan53555_platform_data *pdata)
+{
+ unsigned int reg, data, mask;
+
+ /* Setup voltage control register */
+ switch (pdata->sleep_vsel_id) {
+ case FAN53555_VSEL_ID_0:
+ di->sleep_reg = FAN53555_VSEL0;
+ di->vol_reg = FAN53555_VSEL1;
+ break;
+ case FAN53555_VSEL_ID_1:
+ di->sleep_reg = FAN53555_VSEL1;
+ di->vol_reg = FAN53555_VSEL0;
+ break;
+ default:
+ dev_err(di->dev, "Invalid VSEL ID!\n");
+ return -EINVAL;
+ }
+ /* Init voltage range and step */
+ switch (di->chip_id) {
+ case FAN53555_CHIP_ID_00:
+ if (di->chip_rev == DIE_13_REV) {
+ di->vsel_min = 800000;
+ di->vsel_step = 10000;
+ break;
+ }
+ case FAN53555_CHIP_ID_01:
+ case FAN53555_CHIP_ID_03:
+ case FAN53555_CHIP_ID_05:
+ di->vsel_min = 600000;
+ di->vsel_step = 10000;
+ break;
+ case FAN53555_CHIP_ID_04:
+ di->vsel_min = 603000;
+ di->vsel_step = 12826;
+ break;
+ default:
+ dev_err(di->dev,
+ "Chip ID[%d]\n not supported!\n", di->chip_id);
+ return -EINVAL;
+ }
+ /* Init slew rate */
+ if (pdata->slew_rate & 0x7)
+ di->slew_rate = pdata->slew_rate;
+ else
+ di->slew_rate = FAN53555_SLEW_RATE_64MV;
+ reg = FAN53555_CONTROL;
+ data = di->slew_rate << CTL_SLEW_SHIFT;
+ mask = CTL_SLEW_MASK;
+ return regmap_update_bits(di->regmap, reg, mask, data);
+}
+
+static int fan53555_regulator_register(struct fan53555_device_info *di,
+ struct i2c_client *client)
+{
+ struct regulator_desc *rdesc = &di->desc;
+
+ rdesc->name = "fan53555-reg";
+ if (di->disable_suspend)
+ rdesc->ops = &fan53555_regulator_disable_suspend_ops;
+ else
+ rdesc->ops = &fan53555_regulator_ops;
+ rdesc->type = REGULATOR_VOLTAGE;
+ rdesc->n_voltages = FAN53555_NVOLTAGES;
+ rdesc->owner = THIS_MODULE;
+
+ di->rdev = regulator_register(&di->desc, di->dev,
+ di->regulator, di, client->dev.of_node);
+ return PTR_RET(di->rdev);
+
+}
+
+static struct regmap_config fan53555_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int fan53555_parse_backup_reg(struct i2c_client *client, u32 *sleep_sel)
+{
+ int rc = -EINVAL;
+
+ rc = of_property_read_u32(client->dev.of_node, "fairchild,backup-vsel",
+ sleep_sel);
+ if (rc) {
+ dev_err(&client->dev, "fairchild,backup-vsel property missing\n");
+ } else {
+ switch (*sleep_sel) {
+ case FAN53555_VSEL_ID_0:
+ case FAN53555_VSEL_ID_1:
+ break;
+ default:
+ dev_err(&client->dev, "Invalid VSEL ID!\n");
+ rc = -EINVAL;
+ }
+ }
+
+ return rc;
+}
+
+static u32 fan53555_get_slew_rate_reg_value(struct i2c_client *client,
+ u32 slew_rate)
+{
+ u32 index;
+
+ for (index = 0; index < ARRAY_SIZE(slew_rate_plan); index++)
+ if (slew_rate == slew_rate_plan[index])
+ break;
+
+ if (index == ARRAY_SIZE(slew_rate_plan)) {
+ dev_err(&client->dev, "invalid slew rate.\n");
+ index = FAN53555_SLEW_RATE_8MV;
+ }
+
+ return index;
+}
+
+static struct fan53555_platform_data *
+ fan53555_get_of_platform_data(struct i2c_client *client)
+{
+ struct fan53555_platform_data *pdata = NULL;
+ struct regulator_init_data *init_data;
+ u32 sleep_sel, slew_rate;
+ int rc;
+
+ init_data = of_get_regulator_init_data(&client->dev,
+ client->dev.of_node);
+ if (!init_data) {
+ dev_err(&client->dev, "regulator init data is missing\n");
+ return pdata;
+ }
+
+ rc = of_property_read_u32(client->dev.of_node, "regulator-ramp-delay",
+ &slew_rate);
+ if (rc)
+ slew_rate = slew_rate_plan[FAN53555_SLEW_RATE_8MV];
+
+ if (fan53555_parse_backup_reg(client, &sleep_sel))
+ return pdata;
+
+ pdata = devm_kzalloc(&client->dev,
+ sizeof(struct fan53555_platform_data), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&client->dev,
+ "fan53555_platform_data allocation failed.\n");
+ return pdata;
+ }
+
+ init_data->constraints.input_uV = init_data->constraints.max_uV;
+ init_data->constraints.valid_ops_mask |=
+ REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_MODE;
+ init_data->constraints.valid_modes_mask =
+ REGULATOR_MODE_NORMAL |
+ REGULATOR_MODE_FAST;
+ init_data->constraints.initial_mode = REGULATOR_MODE_NORMAL;
+
+ pdata->regulator = init_data;
+ pdata->slew_rate = fan53555_get_slew_rate_reg_value(client,
+ slew_rate);
+ pdata->sleep_vsel_id = sleep_sel;
+
+ return pdata;
+}
+
+static int fan53555_restore_working_reg(struct device_node *node,
+ struct fan53555_device_info *di)
+{
+ int ret;
+ u32 val;
+
+ /* Restore register from back up register */
+ ret = regmap_read(di->regmap, di->sleep_reg, &val);
+ if (ret < 0) {
+ dev_err(di->dev,
+ "Failed to get backup data from reg %d, ret = %d\n",
+ di->sleep_reg, ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(di->regmap,
+ di->vol_reg, VSEL_FULL_MASK, val);
+ if (ret < 0) {
+ dev_err(di->dev,
+ "Failed to update working reg %d, ret = %d\n",
+ di->vol_reg, ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int fan53555_of_init(struct device_node *node,
+ struct fan53555_device_info *di)
+{
+ int ret, gpio;
+ enum of_gpio_flags flags;
+
+ if (of_property_read_bool(node, "fairchild,restore-reg")) {
+ ret = fan53555_restore_working_reg(node, di);
+ if (ret)
+ return ret;
+ }
+
+ if (of_find_property(node, "fairchild,vsel-gpio", NULL)) {
+ gpio = of_get_named_gpio_flags(node, "fairchild,vsel-gpio", 0,
+ &flags);
+
+ if (!gpio_is_valid(gpio)) {
+ if (gpio != -EPROBE_DEFER)
+ dev_err(di->dev, "Could not get vsel, ret = %d\n",
+ gpio);
+ return gpio;
+ }
+
+ ret = devm_gpio_request(di->dev, gpio, "fan53555_vsel");
+ if (ret) {
+ dev_err(di->dev, "Failed to obtain gpio %d ret = %d\n",
+ gpio, ret);
+ return ret;
+ }
+
+ ret = gpio_direction_output(gpio, flags & OF_GPIO_ACTIVE_LOW ?
+ 0 : 1);
+ if (ret) {
+ dev_err(di->dev,
+ "Failed to set GPIO %d to: %s, ret = %d",
+ gpio, flags & OF_GPIO_ACTIVE_LOW ?
+ "GPIO_LOW" : "GPIO_HIGH", ret);
+ return ret;
+ }
+ }
+
+ di->disable_suspend = of_property_read_bool(node,
+ "fairchild,disable-suspend");
+
+ return 0;
+}
+
+static int __devinit fan53555_regulator_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct fan53555_device_info *di;
+ struct fan53555_platform_data *pdata;
+ unsigned int val;
+ int ret;
+
+ if (client->dev.of_node)
+ pdata = fan53555_get_of_platform_data(client);
+ else
+ pdata = client->dev.platform_data;
+
+ if (!pdata || !pdata->regulator) {
+ dev_err(&client->dev, "Platform data not found!\n");
+ return -ENODEV;
+ }
+
+ di = devm_kzalloc(&client->dev, sizeof(struct fan53555_device_info),
+ GFP_KERNEL);
+ if (!di) {
+ dev_err(&client->dev, "Failed to allocate device info data!\n");
+ return -ENOMEM;
+ }
+ di->regmap = devm_regmap_init_i2c(client, &fan53555_regmap_config);
+ if (IS_ERR(di->regmap)) {
+ dev_err(&client->dev, "Failed to allocate regmap!\n");
+ return PTR_ERR(di->regmap);
+ }
+ di->dev = &client->dev;
+ di->regulator = pdata->regulator;
+ i2c_set_clientdata(client, di);
+ /* Get chip ID */
+ ret = regmap_read(di->regmap, FAN53555_ID1, &val);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to get chip ID!\n");
+ return -ENODEV;
+ }
+ di->chip_id = val & DIE_ID;
+ /* Get chip revision */
+ ret = regmap_read(di->regmap, FAN53555_ID2, &val);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to get chip Rev!\n");
+ return -ENODEV;
+ }
+ di->chip_rev = val & DIE_REV;
+ dev_info(&client->dev, "FAN53555 Option[%d] Rev[%d] Detected!\n",
+ di->chip_id, di->chip_rev);
+ /* Device init */
+ ret = fan53555_device_setup(di, pdata);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to setup device!\n");
+ return ret;
+ }
+
+ /* Set up from device tree */
+ if (client->dev.of_node) {
+ ret = fan53555_of_init(client->dev.of_node, di);
+ if (ret)
+ return ret;
+ }
+
+ ret = fan53555_regulator_register(di, client);
+ if (ret < 0)
+ dev_err(&client->dev, "Failed to register regulator!\n");
+
+ return ret;
+
+}
+
+static int __devexit fan53555_regulator_remove(struct i2c_client *client)
+{
+ struct fan53555_device_info *di = i2c_get_clientdata(client);
+
+ regulator_unregister(di->rdev);
+ return 0;
+}
+
+static struct of_device_id fan53555_match_table[] = {
+ { .compatible = "fairchild,fan53555-regulator",},
+ {},
+};
+MODULE_DEVICE_TABLE(of, fan53555_match_table);
+
+static const struct i2c_device_id fan53555_id[] = {
+ {"fan53555", -1},
+ { },
+};
+
+static struct i2c_driver fan53555_regulator_driver = {
+ .driver = {
+ .name = "fan53555-regulator",
+ .owner = THIS_MODULE,
+ .of_match_table = fan53555_match_table,
+ },
+ .probe = fan53555_regulator_probe,
+ .remove = __devexit_p(fan53555_regulator_remove),
+ .id_table = fan53555_id,
+};
+
+/**
+ * fan53555_regulator_init() - initialized fan53555 regulator driver
+ * This function registers the fan53555 regulator platform driver.
+ *
+ * Returns 0 on success or errno on failure.
+ */
+int __init fan53555_regulator_init(void)
+{
+ static bool initialized;
+
+ if (initialized)
+ return 0;
+ else
+ initialized = true;
+
+ return i2c_add_driver(&fan53555_regulator_driver);
+}
+EXPORT_SYMBOL(fan53555_regulator_init);
+module_init(fan53555_regulator_init);
+
+static void __exit fan53555_regulator_exit(void)
+{
+ i2c_del_driver(&fan53555_regulator_driver);
+}
+module_exit(fan53555_regulator_exit);
+
+MODULE_AUTHOR("Yunfan Zhang <yfzhang@marvell.com>");
+MODULE_DESCRIPTION("FAN53555 regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/onsemi-ncp6335d.c b/drivers/regulator/onsemi-ncp6335d.c
index 329ac46..3d4dd04 100644
--- a/drivers/regulator/onsemi-ncp6335d.c
+++ b/drivers/regulator/onsemi-ncp6335d.c
@@ -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
@@ -12,12 +12,20 @@
#include <linux/module.h>
#include <linux/err.h>
+#include <linux/gpio.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/i2c.h>
+#include <linux/log2.h>
#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
#include <linux/regmap.h>
#include <linux/regulator/onsemi-ncp6335d.h>
+#include <linux/string.h>
+#include <mach/gpiomux.h>
/* registers */
#define REG_NCP6335D_PID 0x03
@@ -30,6 +38,7 @@
/* constraints */
#define NCP6335D_MIN_VOLTAGE_UV 600000
#define NCP6335D_STEP_VOLTAGE_UV 6250
+#define NCP6335D_VOLTAGE_STEPS 128
#define NCP6335D_MIN_SLEW_NS 166
#define NCP6335D_MAX_SLEW_NS 1333
@@ -51,9 +60,15 @@
struct regmap *regmap;
struct device *dev;
unsigned int vsel_reg;
+ unsigned int vsel_backup_reg;
unsigned int mode_bit;
int curr_voltage;
int slew_rate;
+
+ unsigned int step_size;
+ unsigned int min_voltage;
+ unsigned int min_slew_ns;
+ unsigned int max_slew_ns;
};
static void dump_registers(struct ncp6335d_info *dd,
@@ -71,8 +86,8 @@
u8 val;
int delay;
- val = abs(prev_uV - new_uV) / NCP6335D_STEP_VOLTAGE_UV;
- delay = (val * dd->slew_rate / 1000) + 1;
+ val = abs(prev_uV - new_uV) / dd->step_size;
+ delay = ((val * dd->slew_rate) / 1000) + 1;
dev_dbg(dd->dev, "Slew Delay = %d\n", delay);
@@ -120,8 +135,8 @@
dev_err(dd->dev, "Unable to get volatge rc(%d)", rc);
return rc;
}
- dd->curr_voltage = ((val & NCP6335D_VOUT_SEL_MASK) *
- NCP6335D_STEP_VOLTAGE_UV) + NCP6335D_MIN_VOLTAGE_UV;
+ dd->curr_voltage = ((val & NCP6335D_VOUT_SEL_MASK) * dd->step_size) +
+ dd->min_voltage;
dump_registers(dd, dd->vsel_reg, __func__);
@@ -134,10 +149,8 @@
int rc, set_val, new_uV;
struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
- set_val = DIV_ROUND_UP(min_uV - NCP6335D_MIN_VOLTAGE_UV,
- NCP6335D_STEP_VOLTAGE_UV);
- new_uV = (set_val * NCP6335D_STEP_VOLTAGE_UV) +
- NCP6335D_MIN_VOLTAGE_UV;
+ set_val = DIV_ROUND_UP(min_uV - dd->min_voltage, dd->step_size);
+ new_uV = (set_val * dd->step_size) + dd->min_voltage;
if (new_uV > max_uV) {
dev_err(dd->dev, "Unable to set volatge (%d %d)\n",
min_uV, max_uV);
@@ -159,6 +172,17 @@
return rc;
}
+static int ncp6335d_list_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+ if (selector >= NCP6335D_VOLTAGE_STEPS)
+ return 0;
+
+ return selector * dd->step_size + dd->min_voltage;
+}
+
static int ncp6335d_set_mode(struct regulator_dev *rdev,
unsigned int mode)
{
@@ -213,6 +237,7 @@
static struct regulator_ops ncp6335d_ops = {
.set_voltage = ncp6335d_set_voltage,
.get_voltage = ncp6335d_get_voltage,
+ .list_voltage = ncp6335d_list_voltage,
.enable = ncp6335d_enable,
.disable = ncp6335d_disable,
.set_mode = ncp6335d_set_mode,
@@ -222,11 +247,98 @@
static struct regulator_desc rdesc = {
.name = "ncp6335d",
.owner = THIS_MODULE,
- .n_voltages = 128,
+ .n_voltages = NCP6335D_VOLTAGE_STEPS,
.ops = &ncp6335d_ops,
};
-static int __devinit ncp6335d_init(struct ncp6335d_info *dd,
+static int ncp6335d_restore_working_reg(struct device_node *node,
+ struct ncp6335d_info *dd)
+{
+ int ret;
+ unsigned int val;
+
+ /* Restore register from back up register */
+ ret = regmap_read(dd->regmap, dd->vsel_backup_reg, &val);
+ if (ret < 0) {
+ dev_err(dd->dev, "Failed to get backup data from reg %d, ret = %d\n",
+ dd->vsel_backup_reg, ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(dd->regmap, dd->vsel_reg,
+ NCP6335D_VOUT_SEL_MASK, val);
+ if (ret < 0) {
+ dev_err(dd->dev, "Failed to update working reg %d, ret = %d\n",
+ dd->vsel_reg, ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int ncp6335d_parse_gpio(struct device_node *node,
+ struct ncp6335d_info *dd)
+{
+ int ret = 0, gpio;
+ enum of_gpio_flags flags;
+
+ if (!of_find_property(node, "onnn,vsel-gpio", NULL))
+ return ret;
+
+ /* Get GPIO connected to vsel and set its output */
+ gpio = of_get_named_gpio_flags(node,
+ "onnn,vsel-gpio", 0, &flags);
+ if (!gpio_is_valid(gpio)) {
+ if (gpio != -EPROBE_DEFER)
+ dev_err(dd->dev, "Could not get vsel, ret = %d\n",
+ gpio);
+ return gpio;
+ }
+
+ ret = devm_gpio_request(dd->dev, gpio, "ncp6335d_vsel");
+ if (ret) {
+ dev_err(dd->dev, "Failed to obtain gpio %d ret = %d\n",
+ gpio, ret);
+ return ret;
+ }
+
+ ret = gpio_direction_output(gpio, flags & OF_GPIO_ACTIVE_LOW ? 0 : 1);
+ if (ret) {
+ dev_err(dd->dev, "Failed to set GPIO %d to: %s, ret = %d",
+ gpio, flags & OF_GPIO_ACTIVE_LOW ?
+ "GPIO_LOW" : "GPIO_HIGH", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int ncp6335d_parse_tlmm(struct device_node *node,
+ struct ncp6335d_info *dd)
+{
+ int val, ret = 0;
+ u32 tmp[2];
+
+ if (!of_find_property(node, "onnn,tlmm-config", NULL))
+ return ret;
+
+ ret = of_property_read_u32_array(node, "onnn,tlmm-config", tmp, 2);
+ if (ret) {
+ dev_err(dd->dev, "onnn,tlmm-config is misconfigured, ret = %d",
+ ret);
+ return ret;
+ }
+
+ val = msm_tlmm_misc_reg_read(TLMM_SPARE_REG);
+ val &= ~tmp[0];
+ val |= tmp[1] & tmp[0];
+ msm_tlmm_misc_reg_write(TLMM_SPARE_REG, val);
+
+ return ret;
+}
+
+static int __devinit ncp6335d_init(struct i2c_client *client,
+ struct ncp6335d_info *dd,
const struct ncp6335d_platform_data *pdata)
{
int rc;
@@ -235,10 +347,12 @@
switch (pdata->default_vsel) {
case NCP6335D_VSEL0:
dd->vsel_reg = REG_NCP6335D_PROGVSEL0;
+ dd->vsel_backup_reg = REG_NCP6335D_PROGVSEL1;
dd->mode_bit = NCP6335D_PWM_MODE0;
break;
case NCP6335D_VSEL1:
dd->vsel_reg = REG_NCP6335D_PROGVSEL1;
+ dd->vsel_backup_reg = REG_NCP6335D_PROGVSEL0;
dd->mode_bit = NCP6335D_PWM_MODE1;
break;
default:
@@ -246,6 +360,20 @@
return -EINVAL;
}
+ if (of_property_read_bool(client->dev.of_node, "onnn,restore-reg")) {
+ rc = ncp6335d_restore_working_reg(client->dev.of_node, dd);
+ if (rc)
+ return rc;
+ }
+
+ rc = ncp6335d_parse_gpio(client->dev.of_node, dd);
+ if (rc)
+ return rc;
+
+ rc = ncp6335d_parse_tlmm(client->dev.of_node, dd);
+ if (rc)
+ return rc;
+
/* get the current programmed voltage */
rc = regmap_read(dd->regmap, dd->vsel_reg, &val);
if (rc) {
@@ -253,7 +381,7 @@
return rc;
}
dd->curr_voltage = ((val & NCP6335D_VOUT_SEL_MASK) *
- NCP6335D_STEP_VOLTAGE_UV) + NCP6335D_MIN_VOLTAGE_UV;
+ dd->step_size) + dd->min_voltage;
/* set discharge */
rc = regmap_update_bits(dd->regmap, REG_NCP6335D_PGOOD,
@@ -266,15 +394,15 @@
}
/* set slew rate */
- if (pdata->slew_rate_ns < NCP6335D_MIN_SLEW_NS ||
- pdata->slew_rate_ns > NCP6335D_MAX_SLEW_NS) {
+ if (pdata->slew_rate_ns < dd->min_slew_ns ||
+ pdata->slew_rate_ns > dd->max_slew_ns) {
dev_err(dd->dev, "Invalid slew rate %d\n", pdata->slew_rate_ns);
return -EINVAL;
}
- val = DIV_ROUND_UP(pdata->slew_rate_ns - NCP6335D_MIN_SLEW_NS,
- NCP6335D_MIN_SLEW_NS);
- val >>= 1;
- dd->slew_rate = val * NCP6335D_MIN_SLEW_NS;
+
+ dd->slew_rate = pdata->slew_rate_ns;
+ val = DIV_ROUND_UP(pdata->slew_rate_ns, dd->min_slew_ns);
+ val = ilog2(val);
rc = regmap_update_bits(dd->regmap, REG_NCP6335D_TIMING,
NCP6335D_SLEW_MASK, val << NCP6335D_SLEW_SHIFT);
@@ -301,6 +429,116 @@
.val_bits = 8,
};
+static int ncp6335d_parse_dt(struct i2c_client *client,
+ struct ncp6335d_info *dd)
+{
+ int rc;
+
+ rc = of_property_read_u32(client->dev.of_node,
+ "onnn,step-size", &dd->step_size);
+ if (rc < 0) {
+ dev_err(&client->dev, "step size missing: rc = %d.\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(client->dev.of_node,
+ "onnn,min-slew-ns", &dd->min_slew_ns);
+ if (rc < 0) {
+ dev_err(&client->dev, "min slew us missing: rc = %d.\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(client->dev.of_node,
+ "onnn,max-slew-ns", &dd->max_slew_ns);
+ if (rc < 0) {
+ dev_err(&client->dev, "max slew us missing: rc = %d.\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(client->dev.of_node,
+ "onnn,min-setpoint", &dd->min_voltage);
+ if (rc < 0) {
+ dev_err(&client->dev, "min set point missing: rc = %d.\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static struct ncp6335d_platform_data *
+ ncp6335d_get_of_platform_data(struct i2c_client *client)
+{
+ struct ncp6335d_platform_data *pdata = NULL;
+ struct regulator_init_data *init_data;
+ const char *mode_name;
+ int rc;
+
+ init_data = of_get_regulator_init_data(&client->dev,
+ client->dev.of_node);
+ if (!init_data) {
+ dev_err(&client->dev, "regulator init data is missing\n");
+ return pdata;
+ }
+
+ pdata = devm_kzalloc(&client->dev,
+ sizeof(struct ncp6335d_platform_data), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&client->dev, "ncp6335d_platform_data allocation failed.\n");
+ return pdata;
+ }
+
+ rc = of_property_read_u32(client->dev.of_node,
+ "onnn,vsel", &pdata->default_vsel);
+ if (rc < 0) {
+ dev_err(&client->dev, "onnn,vsel property missing: rc = %d.\n",
+ rc);
+ return NULL;
+ }
+
+ rc = of_property_read_u32(client->dev.of_node,
+ "onnn,slew-ns", &pdata->slew_rate_ns);
+ if (rc < 0) {
+ dev_err(&client->dev, "onnn,slew-ns property missing: rc = %d.\n",
+ rc);
+ return NULL;
+ }
+
+ pdata->discharge_enable = of_property_read_bool(client->dev.of_node,
+ "onnn,discharge-enable");
+
+ pdata->sleep_enable = of_property_read_bool(client->dev.of_node,
+ "onnn,sleep-enable");
+
+ pdata->init_data = init_data;
+
+ init_data->constraints.input_uV = init_data->constraints.max_uV;
+ init_data->constraints.valid_ops_mask =
+ REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS |
+ REGULATOR_CHANGE_MODE;
+ init_data->constraints.valid_modes_mask =
+ REGULATOR_MODE_NORMAL |
+ REGULATOR_MODE_FAST;
+
+ rc = of_property_read_string(client->dev.of_node, "onnn,mode",
+ &mode_name);
+ if (!rc) {
+ if (strcmp("pwm", mode_name) == 0) {
+ init_data->constraints.initial_mode =
+ REGULATOR_MODE_FAST;
+ } else if (strcmp("auto", mode_name) == 0) {
+ init_data->constraints.initial_mode =
+ REGULATOR_MODE_NORMAL;
+ } else {
+ dev_err(&client->dev, "onnn,mode, unknown regulator mode: %s\n",
+ mode_name);
+ return NULL;
+ }
+ }
+
+ return pdata;
+}
+
static int __devinit ncp6335d_regulator_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -309,7 +547,11 @@
struct ncp6335d_info *dd;
const struct ncp6335d_platform_data *pdata;
- pdata = client->dev.platform_data;
+ if (client->dev.of_node)
+ pdata = ncp6335d_get_of_platform_data(client);
+ else
+ pdata = client->dev.platform_data;
+
if (!pdata) {
dev_err(&client->dev, "Platform data not specified\n");
return -EINVAL;
@@ -321,6 +563,17 @@
return -ENOMEM;
}
+ if (client->dev.of_node) {
+ rc = ncp6335d_parse_dt(client, dd);
+ if (rc)
+ return rc;
+ } else {
+ dd->step_size = NCP6335D_STEP_VOLTAGE_UV;
+ dd->min_voltage = NCP6335D_MIN_VOLTAGE_UV;
+ dd->min_slew_ns = NCP6335D_MIN_SLEW_NS;
+ dd->max_slew_ns = NCP6335D_MAX_SLEW_NS;
+ }
+
dd->regmap = devm_regmap_init_i2c(client, &ncp6335d_regmap_config);
if (IS_ERR(dd->regmap)) {
dev_err(&client->dev, "Error allocating regmap\n");
@@ -339,17 +592,19 @@
dd->dev = &client->dev;
i2c_set_clientdata(client, dd);
- rc = ncp6335d_init(dd, pdata);
+ rc = ncp6335d_init(client, dd, pdata);
if (rc) {
dev_err(&client->dev, "Unable to intialize the regulator\n");
return -EINVAL;
}
- dd->regulator = regulator_register(&rdesc, &client->dev,
- dd->init_data, dd, NULL);
+ dd->regulator = regulator_register(&rdesc, &client->dev, dd->init_data,
+ dd, client->dev.of_node);
+
if (IS_ERR(dd->regulator)) {
dev_err(&client->dev, "Unable to register regulator rc(%ld)",
PTR_ERR(dd->regulator));
+
return PTR_ERR(dd->regulator);
}
@@ -365,6 +620,12 @@
return 0;
}
+static struct of_device_id ncp6335d_match_table[] = {
+ { .compatible = "onnn,ncp6335d-regulator", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ncp6335d_match_table);
+
static const struct i2c_device_id ncp6335d_id[] = {
{"ncp6335d", -1},
{ },
@@ -373,15 +634,32 @@
static struct i2c_driver ncp6335d_regulator_driver = {
.driver = {
.name = "ncp6335d-regulator",
+ .owner = THIS_MODULE,
+ .of_match_table = ncp6335d_match_table,
},
.probe = ncp6335d_regulator_probe,
.remove = __devexit_p(ncp6335d_regulator_remove),
.id_table = ncp6335d_id,
};
-static int __init ncp6335d_regulator_init(void)
+
+/**
+ * ncp6335d_regulator_init() - initialized ncp6335d regulator driver
+ * This function registers the ncp6335d regulator platform driver.
+ *
+ * Returns 0 on success or errno on failure.
+ */
+int __init ncp6335d_regulator_init(void)
{
+ static bool initialized;
+
+ if (initialized)
+ return 0;
+ else
+ initialized = true;
+
return i2c_add_driver(&ncp6335d_regulator_driver);
}
+EXPORT_SYMBOL(ncp6335d_regulator_init);
subsys_initcall(ncp6335d_regulator_init);
static void __exit ncp6335d_regulator_exit(void)
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index dc728eb..e65c8cf 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -276,12 +276,15 @@
* Returning 0 on the disconnections and
* removals will ensure consistent state of channels,
* ports with the HW
+ * Remote requests to remove channel/port will be
+ * returned from the path where they wait on
+ * acknowledgement from ADSP
*/
if ((txn->mt == SLIM_MSG_MT_DEST_REFERRED_USER) &&
((mc == SLIM_USR_MC_CHAN_CTRL ||
mc == SLIM_USR_MC_DISCONNECT_PORT ||
mc == SLIM_USR_MC_RECONFIG_NOW)))
- return 0;
+ return -EREMOTEIO;
if ((txn->mt == SLIM_MSG_MT_CORE) &&
((mc == SLIM_MSG_MC_DISCONNECT_PORT ||
mc == SLIM_MSG_MC_NEXT_REMOVE_CHANNEL ||
@@ -299,7 +302,7 @@
return -EREMOTEIO;
timeout = wait_for_completion_timeout(&dev->ctrl_up,
HZ);
- if (!timeout)
+ if (!timeout && dev->state == MSM_CTRL_DOWN)
return -ETIMEDOUT;
}
msm_slim_get_ctrl(dev);
@@ -504,10 +507,17 @@
ret = -ETIMEDOUT;
else
ret = txn->ec;
+ } else if (ret == -EREMOTEIO &&
+ (txn->mc == SLIM_USR_MC_CHAN_CTRL ||
+ txn->mc == SLIM_USR_MC_DISCONNECT_PORT)) {
+ /* HW restarting, channel/port removal should succeed */
+ return 0;
}
+
if (ret) {
pr_err("master msg:0x%x,tid:%d ret:%d", txn->mc,
txn->tid, ret);
+ WARN(1, "timeout during xfer and wait");
mutex_lock(&ctrl->m_ctrl);
ctrl->txnt[txn->tid] = NULL;
mutex_unlock(&ctrl->m_ctrl);
diff --git a/drivers/slimbus/slimbus.c b/drivers/slimbus/slimbus.c
index f6594c8..96fe2a8 100644
--- a/drivers/slimbus/slimbus.c
+++ b/drivers/slimbus/slimbus.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
@@ -313,14 +313,12 @@
sbdrv->device_down(sbdev);
return;
}
- if (sbdev->notified)
+ if (sbdev->notified || !sbdrv)
return;
ret = slim_get_logical_addr(sbdev, sbdev->e_addr, 6, &laddr);
if (!ret) {
- if (sbdrv)
- sbdev->notified = true;
- if (sbdrv->device_up)
- sbdrv->device_up(sbdev);
+ sbdev->notified = true;
+ sbdrv->device_up(sbdev);
}
}
diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c
index 45400cb..39e81fa 100644
--- a/drivers/spi/spi_qsd.c
+++ b/drivers/spi/spi_qsd.c
@@ -1167,10 +1167,8 @@
if ((!dd->read_buf || op & SPI_OP_MAX_INPUT_DONE_FLAG) &&
(!dd->write_buf || op & SPI_OP_MAX_OUTPUT_DONE_FLAG)) {
msm_spi_ack_transfer(dd);
- if (dd->rx_unaligned_len == 0) {
if (atomic_inc_return(&dd->rx_irq_called) == 1)
return IRQ_HANDLED;
- }
msm_spi_complete(dd);
return IRQ_HANDLED;
}
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index 739696d..21863e8 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -70,16 +70,19 @@
list_for_each_entry_safe(pos, var, &sensor_info_list, sensor_list) {
if (pos->sensor_id == sensor_id)
- break;
+ return pos;
}
- return pos;
+ return NULL;
}
int sensor_get_id(char *name)
{
struct sensor_info *pos, *var;
+ if (!name)
+ return -ENODEV;
+
list_for_each_entry_safe(pos, var, &sensor_info_list, sensor_list) {
if (!strcmp(pos->tz->type, name))
return pos->sensor_id;
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index ecfacc0..066419f 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -551,6 +551,25 @@
writel_relaxed(value, uport->membase + offset);
}
+static int sps_rx_disconnect(struct sps_pipe *sps_pipe_handler)
+{
+ struct sps_connect config;
+ int ret;
+
+ ret = sps_get_config(sps_pipe_handler, &config);
+ if (ret) {
+ pr_err("%s: sps_get_config() failed ret %d\n", __func__, ret);
+ return ret;
+ }
+ config.options |= SPS_O_POLL;
+ ret = sps_set_config(sps_pipe_handler, &config);
+ if (ret) {
+ pr_err("%s: sps_set_config() failed ret %d\n", __func__, ret);
+ return ret;
+ }
+ return sps_disconnect(sps_pipe_handler);
+}
+
static void hex_dump_ipc(char *prefix, char *string, int size)
{
char linebuf[512];
@@ -1191,7 +1210,7 @@
ret = wait_event_timeout(msm_uport->rx.wait,
msm_uport->rx_bam_inprogress == false,
RX_FLUSH_COMPLETE_TIMEOUT);
- ret = sps_disconnect(sps_pipe_handle);
+ ret = sps_rx_disconnect(sps_pipe_handle);
if (ret)
MSM_HS_ERR("%s(): sps_disconnect failed\n",
__func__);
@@ -1275,7 +1294,7 @@
struct sps_pipe *sps_pipe_handle = rx->prod.pipe_handle;
int ret = 0;
- ret = sps_disconnect(sps_pipe_handle);
+ ret = sps_rx_disconnect(sps_pipe_handle);
if (ret)
MSM_HS_ERR("%s(): sps_disconnect failed\n", __func__);
@@ -2529,6 +2548,8 @@
/* Initialize the tx */
tx->tx_ready_int_en = 0;
tx->dma_in_flight = 0;
+ 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;
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index a2580fc..a192398 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -794,9 +794,9 @@
.release = single_release,
};
-static unsigned int ep_addr_rxdbg_mask;
+static unsigned int ep_addr_rxdbg_mask = 1;
module_param(ep_addr_rxdbg_mask, uint, S_IRUGO | S_IWUSR);
-static unsigned int ep_addr_txdbg_mask;
+static unsigned int ep_addr_txdbg_mask = 1;
module_param(ep_addr_txdbg_mask, uint, S_IRUGO | S_IWUSR);
/* Maximum debug message length */
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 0cfd515..c07f18c 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -291,6 +291,7 @@
struct dwc3_ep *dep = to_dwc3_ep(ep);
struct dwc3 *dwc = dep->dwc;
+ dbg_event(dep->number, "EP0STAL", value);
dwc3_ep0_stall_and_restart(dwc);
return 0;
@@ -752,8 +753,10 @@
dwc->delayed_status = true;
out:
- if (ret < 0)
+ if (ret < 0) {
+ dbg_event(0x0, "ERRSTAL", ret);
dwc3_ep0_stall_and_restart(dwc);
+ }
}
bool zlp_required;
@@ -819,7 +822,7 @@
if ((epnum & 1) && ur->actual < ur->length) {
/* for some reason we did not get everything out */
-
+ dbg_event(epnum, "INDATSTAL", 0);
dwc3_ep0_stall_and_restart(dwc);
} else {
/*
@@ -855,6 +858,7 @@
if (ret < 0) {
dev_dbg(dwc->dev, "Invalid Test #%d\n",
dwc->test_mode_nr);
+ dbg_event(0x00, "INVALTEST", ret);
dwc3_ep0_stall_and_restart(dwc);
return;
}
@@ -1034,6 +1038,7 @@
dev_vdbg(dwc->dev, "Wrong direction for Data phase\n");
dwc3_ep0_end_control_data(dwc, dep);
+ dbg_event(epnum, "WRONGDR", 0);
dwc3_ep0_stall_and_restart(dwc);
return;
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 7439c45..4be032a 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -2204,6 +2204,7 @@
return;
}
+ dbg_event(dep->number, "XFRCOMP", 0);
dwc3_endpoint_transfer_complete(dwc, dep, event, 1);
break;
case DWC3_DEPEVT_XFERINPROGRESS:
@@ -2213,9 +2214,11 @@
return;
}
+ dbg_event(dep->number, "XFRPROG", 0);
dwc3_endpoint_transfer_complete(dwc, dep, event, 0);
break;
case DWC3_DEPEVT_XFERNOTREADY:
+ dbg_event(dep->number, "XFRNRDY", 0);
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
dwc3_gadget_start_isoc(dwc, dep, event);
} else {
diff --git a/drivers/usb/gadget/f_mbim.c b/drivers/usb/gadget/f_mbim.c
index 15f20ca..88a7bde 100644
--- a/drivers/usb/gadget/f_mbim.c
+++ b/drivers/usb/gadget/f_mbim.c
@@ -1710,8 +1710,10 @@
return -EIO;
}
+ spin_lock(&dev->lock);
while (list_empty(&dev->cpkt_req_q)) {
pr_debug("Requests list is empty. Wait.\n");
+ spin_unlock(&dev->lock);
ret = wait_event_interruptible(dev->read_wq,
!list_empty(&dev->cpkt_req_q));
if (ret < 0) {
@@ -1720,11 +1722,13 @@
return -ERESTARTSYS;
}
pr_debug("Received request packet\n");
+ spin_lock(&dev->lock);
}
cpkt = list_first_entry(&dev->cpkt_req_q, struct ctrl_pkt,
list);
if (cpkt->len > count) {
+ spin_unlock(&dev->lock);
mbim_unlock(&dev->read_excl);
pr_err("cpkt size too big:%d > buf size:%d\n",
cpkt->len, count);
@@ -1734,6 +1738,7 @@
pr_debug("cpkt size:%d\n", cpkt->len);
list_del(&cpkt->list);
+ spin_unlock(&dev->lock);
mbim_unlock(&dev->read_excl);
ret = copy_to_user(buf, cpkt->buf, cpkt->len);
diff --git a/drivers/usb/gadget/f_qc_rndis.c b/drivers/usb/gadget/f_qc_rndis.c
index 1af076b..69f4b1c 100644
--- a/drivers/usb/gadget/f_qc_rndis.c
+++ b/drivers/usb/gadget/f_qc_rndis.c
@@ -347,7 +347,7 @@
};
static struct usb_descriptor_header *eth_qc_ss_function[] = {
- (struct usb_descriptor_header *) &rndis_iad_descriptor,
+ (struct usb_descriptor_header *) &rndis_qc_iad_descriptor,
/* control interface matches ACM, not Ethernet */
(struct usb_descriptor_header *) &rndis_qc_control_intf,
diff --git a/drivers/usb/misc/mdm_ctrl_bridge.c b/drivers/usb/misc/mdm_ctrl_bridge.c
index 75fce2f..262b6c2 100644
--- a/drivers/usb/misc/mdm_ctrl_bridge.c
+++ b/drivers/usb/misc/mdm_ctrl_bridge.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
@@ -26,10 +26,6 @@
#include <asm/unaligned.h>
#include <mach/usb_bridge.h>
-/* polling interval for Interrupt ep */
-#define HS_INTERVAL 7
-#define FS_LS_INTERVAL 3
-
#define ACM_CTRL_DTR (1 << 0)
#define DEFAULT_READ_URB_LENGTH 4096
@@ -719,8 +715,7 @@
goto free_inturb;
}
- interval =
- (udev->speed == USB_SPEED_HIGH) ? HS_INTERVAL : FS_LS_INTERVAL;
+ interval = int_in->desc.bInterval;
usb_fill_int_urb(dev->inturb, udev, dev->int_pipe,
dev->intbuf, wMaxPacketSize,
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 8287ad7..47b8e59 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2013, Linux Foundation. All rights reserved.
+/* Copyright (c) 2009-2014, 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
@@ -2551,6 +2551,8 @@
bool work = 0, srp_reqd, dcp;
pm_runtime_resume(otg->phy->dev);
+ if (motg->pm_done)
+ pm_runtime_get_sync(otg->phy->dev);
pr_debug("%s work\n", otg_state_string(otg->phy->state));
switch (otg->phy->state) {
case OTG_STATE_UNDEFINED:
@@ -2696,6 +2698,7 @@
*/
pm_runtime_mark_last_busy(otg->phy->dev);
pm_runtime_autosuspend(otg->phy->dev);
+ motg->pm_done = 1;
}
break;
case OTG_STATE_B_SRP_INIT:
@@ -4842,6 +4845,7 @@
dev_dbg(dev, "OTG runtime resume\n");
pm_runtime_get_noresume(dev);
+ motg->pm_done = 0;
return msm_otg_resume(motg);
}
#endif
@@ -4869,6 +4873,7 @@
dev_dbg(dev, "OTG PM resume\n");
+ motg->pm_done = 0;
atomic_set(&motg->pm_suspended, 0);
if (motg->async_int || motg->sm_work_pending) {
pm_runtime_get_noresume(dev);
diff --git a/drivers/video/msm/mdss/dsi_host_v2.c b/drivers/video/msm/mdss/dsi_host_v2.c
index 53b6fc6..ca7f199 100644
--- a/drivers/video/msm/mdss/dsi_host_v2.c
+++ b/drivers/video/msm/mdss/dsi_host_v2.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
@@ -524,8 +524,11 @@
if (readl_poll_timeout((ctrl_base + DSI_STATUS),
status,
((status & 0x02) == 0),
- DSI_POLL_SLEEP_US, DSI_POLL_TIMEOUT_US))
+ DSI_POLL_SLEEP_US, DSI_POLL_TIMEOUT_US)) {
pr_err("%s: DSI status=%x failed\n", __func__, status);
+ pr_err("%s: Doing sw reset\n", __func__);
+ msm_dsi_sw_reset();
+ }
/* Check for x_HS_FIFO_EMPTY */
if (readl_poll_timeout((ctrl_base + DSI_FIFO_STATUS),
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.c b/drivers/video/msm/mdss/mdp3_ctrl.c
index 8b390ca..e4d8d0f 100644
--- a/drivers/video/msm/mdss/mdp3_ctrl.c
+++ b/drivers/video/msm/mdss/mdp3_ctrl.c
@@ -426,10 +426,10 @@
return format;
}
-static int mdp3_ctrl_get_pack_pattern(struct msm_fb_data_type *mfd)
+static int mdp3_ctrl_get_pack_pattern(u32 imgType)
{
int packPattern = MDP3_DMA_OUTPUT_PACK_PATTERN_RGB;
- if (mfd->fb_imgType == MDP_RGBA_8888)
+ if (imgType == MDP_RGBA_8888)
packPattern = MDP3_DMA_OUTPUT_PACK_PATTERN_BGR;
return packPattern;
}
@@ -530,7 +530,7 @@
outputConfig.out_sel = mdp3_ctrl_get_intf_type(mfd);
outputConfig.bit_mask_polarity = 0;
outputConfig.color_components_flip = 0;
- outputConfig.pack_pattern = mdp3_ctrl_get_pack_pattern(mfd);
+ outputConfig.pack_pattern = mdp3_ctrl_get_pack_pattern(mfd->fb_imgType);
outputConfig.pack_align = MDP3_DMA_OUTPUT_PACK_ALIGN_LSB;
outputConfig.color_comp_out_bits = (MDP3_DMA_OUTPUT_COMP_BITS_8 << 4) |
(MDP3_DMA_OUTPUT_COMP_BITS_8 << 2)|
@@ -895,6 +895,8 @@
dma->source_config.format != format) {
dma->source_config.format = format;
dma->source_config.stride = stride;
+ dma->output_config.pack_pattern =
+ mdp3_ctrl_get_pack_pattern(req->src.format);
mdp3_clk_enable(1, 0);
mdp3_session->dma->dma_config_source(dma);
mdp3_clk_enable(0, 0);
@@ -924,6 +926,8 @@
struct mdp3_dma *dma = mdp3_session->dma;
dma->source_config.format = format;
dma->source_config.stride = fix->line_length;
+ dma->output_config.pack_pattern =
+ mdp3_ctrl_get_pack_pattern(mfd->fb_imgType);
mdp3_clk_enable(1, 0);
mdp3_session->dma->dma_config_source(dma);
mdp3_clk_enable(0, 0);
diff --git a/drivers/video/msm/mdss/mdp3_dma.c b/drivers/video/msm/mdss/mdp3_dma.c
index 36d5cf1..800c4b3 100644
--- a/drivers/video/msm/mdss/mdp3_dma.c
+++ b/drivers/video/msm/mdss/mdp3_dma.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
@@ -339,6 +339,8 @@
dma_p_cfg_reg = MDP3_REG_READ(MDP3_REG_DMA_P_CONFIG);
dma_p_cfg_reg &= ~MDP3_DMA_IBUF_FORMAT_MASK;
dma_p_cfg_reg |= source_config->format << 25;
+ dma_p_cfg_reg &= ~MDP3_DMA_PACK_PATTERN_MASK;
+ dma_p_cfg_reg |= dma->output_config.pack_pattern << 8;
dma_p_size = source_config->width | (source_config->height << 16);
diff --git a/drivers/video/msm/mdss/mdp3_hwio.h b/drivers/video/msm/mdss/mdp3_hwio.h
index 39690ef..c40ee47 100644
--- a/drivers/video/msm/mdss/mdp3_hwio.h
+++ b/drivers/video/msm/mdss/mdp3_hwio.h
@@ -120,6 +120,7 @@
/*DMA MASK*/
#define MDP3_DMA_IBUF_FORMAT_MASK 0x06000000
+#define MDP3_DMA_PACK_PATTERN_MASK 0x00003f00
/*MISR*/
#define MDP3_REG_MODE_CLK 0x000D0000
diff --git a/drivers/video/msm/mdss/mdp3_ppp.c b/drivers/video/msm/mdss/mdp3_ppp.c
index 270c096..8cc29da8 100644
--- a/drivers/video/msm/mdss/mdp3_ppp.c
+++ b/drivers/video/msm/mdss/mdp3_ppp.c
@@ -99,6 +99,7 @@
struct timer_list free_bw_timer;
struct work_struct free_bw_work;
bool bw_on;
+ bool bw_optimal;
};
static struct ppp_status *ppp_stat;
@@ -335,21 +336,54 @@
mdp3_irq_disable(MDP3_PPP_DONE);
}
+int mdp3_ppp_vote_update(struct msm_fb_data_type *mfd)
+{
+ struct mdss_panel_info *panel_info = mfd->panel_info;
+ uint64_t req_bw = 0, ab = 0, ib = 0;
+ int rate = 0;
+ int rc = 0;
+ if (!ppp_stat->bw_on)
+ pr_err("%s: PPP vote update in wrong state\n", __func__);
+
+ rate = MDP_BLIT_CLK_RATE;
+ req_bw = panel_info->xres * panel_info->yres *
+ panel_info->mipi.frame_rate *
+ MDP_PPP_MAX_BPP *
+ MDP_PPP_DYNAMIC_FACTOR *
+ MDP_PPP_MAX_READ_WRITE;
+ ib = (req_bw * 3) / 2;
+
+ if (ppp_stat->bw_optimal)
+ ab = ib / 2;
+ else
+ ab = req_bw;
+ rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_PPP, ab, ib);
+ if (rc < 0) {
+ pr_err("%s: scale_set_quota failed\n", __func__);
+ return rc;
+ }
+ return rc;
+}
+
int mdp3_ppp_turnon(struct msm_fb_data_type *mfd, int on_off)
{
struct mdss_panel_info *panel_info = mfd->panel_info;
- uint64_t ab = 0, ib = 0;
+ uint64_t req_bw = 0, ab = 0, ib = 0;
int rate = 0;
int rc;
if (on_off) {
rate = MDP_BLIT_CLK_RATE;
- ab = panel_info->xres * panel_info->yres *
+ req_bw = panel_info->xres * panel_info->yres *
panel_info->mipi.frame_rate *
MDP_PPP_MAX_BPP *
MDP_PPP_DYNAMIC_FACTOR *
MDP_PPP_MAX_READ_WRITE;
- ib = (ab * 3) / 2;
+ ib = (req_bw * 3) / 2;
+ if (ppp_stat->bw_optimal)
+ ab = ib / 2;
+ else
+ ab = req_bw;
}
mdp3_clk_set_rate(MDP3_CLK_CORE, rate, MDP3_CLIENT_PPP);
rc = mdp3_clk_enable(on_off, 0);
@@ -367,6 +401,13 @@
return 0;
}
+bool mdp3_optimal_bw(int req_cnt)
+{
+ if (req_cnt == 1 && ppp_stat->req_q.count == 1)
+ return true;
+ return false;
+}
+
void mdp3_start_ppp(struct ppp_blit_op *blit_op)
{
/* Wait for the pipe to clear */
@@ -1030,6 +1071,7 @@
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);
@@ -1063,6 +1105,10 @@
if (ppp_stat->wait_for_pop)
complete(&ppp_stat->pop_q_comp);
mutex_unlock(&ppp_stat->req_mutex);
+ if (req && (ppp_stat->bw_optimal != mdp3_optimal_bw(req->count))) {
+ ppp_stat->bw_optimal = !ppp_stat->bw_optimal;
+ mdp3_ppp_vote_update(mfd);
+ }
}
mod_timer(&ppp_stat->free_bw_timer, jiffies +
msecs_to_jiffies(MDP_RELEASE_BW_TIMEOUT));
diff --git a/drivers/video/msm/mdss/mdss.h b/drivers/video/msm/mdss/mdss.h
index 53292f8..ada1281 100644
--- a/drivers/video/msm/mdss/mdss.h
+++ b/drivers/video/msm/mdss/mdss.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
@@ -79,6 +79,21 @@
spinlock_t lock;
};
+struct mdss_fudge_factor {
+ u32 numer;
+ u32 denom;
+};
+
+struct mdss_prefill_data {
+ u32 ot_bytes;
+ u32 y_buf_bytes;
+ u32 y_scaler_lines_bilinear;
+ u32 y_scaler_lines_caf;
+ u32 post_scaler_pixels;
+ u32 pp_pixels;
+ u32 fbc_lines;
+};
+
struct mdss_data_type {
u32 mdp_rev;
struct clk *mdp_clk[MDSS_MAX_CLK];
@@ -104,6 +119,7 @@
u8 has_wfd_blk;
u8 has_wb_ad;
+ u32 rotator_ot_limit;
u32 mdp_irq_mask;
u32 mdp_hist_irq_mask;
@@ -114,8 +130,8 @@
unsigned long min_mdp_clk;
u32 res_init;
- u32 bus_hdl;
+ u32 highest_bank_bit;
u32 smp_mb_cnt;
u32 smp_mb_size;
u32 smp_mb_per_pipe;
@@ -125,6 +141,15 @@
u32 max_bw_low;
u32 max_bw_high;
+ u32 axi_port_cnt;
+ u32 curr_bw_uc_idx;
+ u32 bus_hdl;
+ struct msm_bus_scale_pdata *bus_scale_table;
+
+ struct mdss_fudge_factor ab_factor;
+ struct mdss_fudge_factor ib_factor;
+ struct mdss_fudge_factor clk_factor;
+
struct mdss_hw_settings *hw_settings;
struct mdss_mdp_pipe *vig_pipes;
@@ -162,11 +187,11 @@
struct early_suspend early_suspend;
struct mdss_debug_inf debug_inf;
- int current_bus_idx;
bool mixer_switched;
struct mdss_panel_cfg pan_cfg;
int handoff_pending;
+ struct mdss_prefill_data prefill_data;
};
extern struct mdss_data_type *mdss_res;
diff --git a/drivers/video/msm/mdss/mdss_dsi.c b/drivers/video/msm/mdss/mdss_dsi.c
index 54c8a06..44cc4a2 100644
--- a/drivers/video/msm/mdss/mdss_dsi.c
+++ b/drivers/video/msm/mdss/mdss_dsi.c
@@ -642,33 +642,58 @@
if (new_fps !=
ctrl_pdata->panel_data.panel_info.mipi.frame_rate) {
- rc = mdss_dsi_clk_div_config
- (&ctrl_pdata->panel_data.panel_info, new_fps);
- if (rc) {
- pr_err("%s: unable to initialize the clk dividers\n",
- __func__);
- return rc;
- }
- ctrl_pdata->pclk_rate =
- ctrl_pdata->panel_data.panel_info.mipi.dsi_pclk_rate;
- ctrl_pdata->byte_clk_rate =
- ctrl_pdata->panel_data.panel_info.clk_rate / 8;
-
if (pdata->panel_info.dfps_update
- == DFPS_IMMEDIATE_CLK_UPDATE_MODE) {
- dsi_ctrl = MIPI_INP((ctrl_pdata->ctrl_base) +
- 0x0004);
- ctrl_pdata->panel_data.panel_info.mipi.frame_rate =
- new_fps;
- dsi_ctrl &= ~0x2;
- 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);
- dsi_ctrl |= 0x2;
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004,
- dsi_ctrl);
+ == DFPS_IMMEDIATE_PORCH_UPDATE_MODE) {
+ u32 hsync_period, vsync_period;
+ u32 new_dsi_v_total, current_dsi_v_total;
+ vsync_period =
+ mdss_panel_get_vtotal(&pdata->panel_info);
+ hsync_period =
+ mdss_panel_get_htotal(&pdata->panel_info);
+ current_dsi_v_total =
+ MIPI_INP((ctrl_pdata->ctrl_base) + 0x2C);
+ new_dsi_v_total =
+ ((vsync_period - 1) << 16) | (hsync_period - 1);
+ MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x2C,
+ (current_dsi_v_total | 0x8000000));
+ if (new_dsi_v_total & 0x8000000) {
+ MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x2C,
+ new_dsi_v_total);
+ } else {
+ MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x2C,
+ (new_dsi_v_total | 0x8000000));
+ MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x2C,
+ (new_dsi_v_total & 0x7ffffff));
+ }
+ pdata->panel_info.mipi.frame_rate = new_fps;
+ } else {
+ rc = mdss_dsi_clk_div_config
+ (&ctrl_pdata->panel_data.panel_info, new_fps);
+ if (rc) {
+ pr_err("%s: unable to initialize the clk dividers\n",
+ __func__);
+ return rc;
+ }
+ ctrl_pdata->pclk_rate =
+ pdata->panel_info.mipi.dsi_pclk_rate;
+ ctrl_pdata->byte_clk_rate =
+ pdata->panel_info.clk_rate / 8;
+
+ if (pdata->panel_info.dfps_update
+ == DFPS_IMMEDIATE_CLK_UPDATE_MODE) {
+ dsi_ctrl = MIPI_INP((ctrl_pdata->ctrl_base) +
+ 0x0004);
+ pdata->panel_info.mipi.frame_rate = new_fps;
+ dsi_ctrl &= ~0x2;
+ 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);
+ dsi_ctrl |= 0x2;
+ MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004,
+ dsi_ctrl);
+ }
}
} else {
pr_debug("%s: Panel is already at this FPS\n", __func__);
@@ -1196,6 +1221,12 @@
DFPS_IMMEDIATE_CLK_UPDATE_MODE;
pr_debug("%s: dfps mode: Immediate clk\n",
__func__);
+ } else if (!strcmp(data,
+ "dfps_immediate_porch_mode")) {
+ pinfo->dfps_update =
+ DFPS_IMMEDIATE_PORCH_UPDATE_MODE;
+ pr_debug("%s: dfps mode: Immediate porch\n",
+ __func__);
} else {
pr_debug("%s: dfps to default mode\n",
__func__);
diff --git a/drivers/video/msm/mdss/mdss_dsi_panel.c b/drivers/video/msm/mdss/mdss_dsi_panel.c
index 6e44099..76e6d1b 100644
--- a/drivers/video/msm/mdss/mdss_dsi_panel.c
+++ b/drivers/video/msm/mdss/mdss_dsi_panel.c
@@ -815,11 +815,11 @@
pinfo->mipi.insert_dcs_cmd =
(!rc ? tmp : 1);
rc = of_property_read_u32(np,
- "qcom,mdss-dsi-te-v-sync-continue-lines", &tmp);
+ "qcom,mdss-dsi-wr-mem-continue", &tmp);
pinfo->mipi.wr_mem_continue =
(!rc ? tmp : 0x3c);
rc = of_property_read_u32(np,
- "qcom,mdss-dsi-te-v-sync-rd-ptr-irq-line", &tmp);
+ "qcom,mdss-dsi-wr-mem-start", &tmp);
pinfo->mipi.wr_mem_start =
(!rc ? tmp : 0x2c);
rc = of_property_read_u32(np,
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c
index dc69927..41aaf61 100644
--- a/drivers/video/msm/mdss/mdss_fb.c
+++ b/drivers/video/msm/mdss/mdss_fb.c
@@ -447,17 +447,27 @@
mfd->mdp_sync_pt_data.timeline =
sw_sync_timeline_create(timeline_name);
if (mfd->mdp_sync_pt_data.timeline == NULL) {
- pr_err("%s: cannot create time line", __func__);
+ pr_err("cannot create release fence time line\n");
return -ENOMEM;
}
mfd->mdp_sync_pt_data.notifier.notifier_call =
__mdss_fb_sync_buf_done_callback;
}
- if ((mfd->panel.type == WRITEBACK_PANEL) ||
- (mfd->panel.type == MIPI_CMD_PANEL))
+
+ switch (mfd->panel.type) {
+ case WRITEBACK_PANEL:
mfd->mdp_sync_pt_data.threshold = 1;
- else
+ mfd->mdp_sync_pt_data.retire_threshold = 0;
+ break;
+ case MIPI_CMD_PANEL:
+ mfd->mdp_sync_pt_data.threshold = 1;
+ mfd->mdp_sync_pt_data.retire_threshold = 1;
+ break;
+ default:
mfd->mdp_sync_pt_data.threshold = 2;
+ mfd->mdp_sync_pt_data.retire_threshold = 0;
+ break;
+ }
if (mfd->splash_logo_enabled) {
mfd->splash_thread = kthread_run(mdss_fb_splash_thread, mfd,
@@ -2004,42 +2014,37 @@
}
/**
- * mdss_fb_sync_get_rel_fence() - get release fence from sync pt timeline
- * @sync_pt_data: Sync pt structure holding timeline and fence info.
+ * mdss_fb_sync_get_fence() - get fence from timeline
+ * @timeline: Timeline to create the fence on
+ * @fence_name: Name of the fence that will be created for debugging
+ * @val: Timeline value at which the fence will be signaled
*
- * Function returns a release fence on the timeline associated with the
- * sync pt struct given and it's associated information. The release fence
- * created can be used to signal when buffers provided will be released.
+ * Function returns a fence on the timeline given with the name provided.
+ * The fence created will be signaled when the timeline is advanced.
*/
-static struct sync_fence *__mdss_fb_sync_get_rel_fence(
- struct msm_sync_pt_data *sync_pt_data)
+struct sync_fence *mdss_fb_sync_get_fence(struct sw_sync_timeline *timeline,
+ const char *fence_name, int val)
{
- struct sync_pt *rel_sync_pt;
- struct sync_fence *rel_fence;
- int val;
+ struct sync_pt *sync_pt;
+ struct sync_fence *fence;
- val = sync_pt_data->timeline_value + sync_pt_data->threshold +
- atomic_read(&sync_pt_data->commit_cnt);
+ pr_debug("%s: buf sync fence timeline=%d\n", fence_name, val);
- pr_debug("%s: buf sync rel fence timeline=%d\n",
- sync_pt_data->fence_name, val);
-
- rel_sync_pt = sw_sync_pt_create(sync_pt_data->timeline, val);
- if (rel_sync_pt == NULL) {
- pr_err("%s: cannot create sync point\n",
- sync_pt_data->fence_name);
+ sync_pt = sw_sync_pt_create(timeline, val);
+ if (sync_pt == NULL) {
+ pr_err("%s: cannot create sync point\n", fence_name);
return NULL;
}
/* create fence */
- rel_fence = sync_fence_create(sync_pt_data->fence_name, rel_sync_pt);
- if (rel_fence == NULL) {
- sync_pt_free(rel_sync_pt);
- pr_err("%s: cannot create fence\n", sync_pt_data->fence_name);
+ fence = sync_fence_create(fence_name, sync_pt);
+ if (fence == NULL) {
+ sync_pt_free(sync_pt);
+ pr_err("%s: cannot create fence\n", fence_name);
return NULL;
}
- return rel_fence;
+ return fence;
}
static int mdss_fb_handle_buf_sync_ioctl(struct msm_sync_pt_data *sync_pt_data,
@@ -2047,8 +2052,10 @@
{
int i, ret = 0;
int acq_fen_fd[MDP_MAX_FENCE_FD];
- struct sync_fence *fence, *rel_fence;
+ struct sync_fence *fence, *rel_fence, *retire_fence;
int rel_fen_fd;
+ int retire_fen_fd;
+ int val;
if ((buf_sync->acq_fen_fd_cnt > MDP_MAX_FENCE_FD) ||
(sync_pt_data->timeline == NULL))
@@ -2085,7 +2092,12 @@
if (ret)
goto buf_sync_err_1;
- rel_fence = __mdss_fb_sync_get_rel_fence(sync_pt_data);
+ val = sync_pt_data->timeline_value + sync_pt_data->threshold +
+ atomic_read(&sync_pt_data->commit_cnt);
+
+ /* Set release fence */
+ rel_fence = mdss_fb_sync_get_fence(sync_pt_data->timeline,
+ sync_pt_data->fence_name, val);
if (IS_ERR_OR_NULL(rel_fence)) {
pr_err("%s: unable to retrieve release fence\n",
sync_pt_data->fence_name);
@@ -2109,6 +2121,50 @@
pr_err("%s: copy_to_user failed\n", sync_pt_data->fence_name);
goto buf_sync_err_3;
}
+
+ if (!(buf_sync->flags & MDP_BUF_SYNC_FLAG_RETIRE_FENCE))
+ goto skip_retire_fence;
+
+ if (sync_pt_data->get_retire_fence)
+ retire_fence = sync_pt_data->get_retire_fence(sync_pt_data);
+ else
+ retire_fence = NULL;
+
+ if (IS_ERR_OR_NULL(retire_fence)) {
+ val += sync_pt_data->retire_threshold;
+ retire_fence = mdss_fb_sync_get_fence(
+ sync_pt_data->timeline, "mdp-retire", val);
+ }
+
+ if (IS_ERR_OR_NULL(retire_fence)) {
+ pr_err("%s: unable to retrieve retire fence\n",
+ sync_pt_data->fence_name);
+ ret = retire_fence ? PTR_ERR(rel_fence) : -ENOMEM;
+ goto buf_sync_err_3;
+ }
+ retire_fen_fd = get_unused_fd_flags(0);
+
+ if (retire_fen_fd < 0) {
+ pr_err("%s: get_unused_fd_flags failed for retire fence\n",
+ sync_pt_data->fence_name);
+ ret = -EIO;
+ sync_fence_put(retire_fence);
+ goto buf_sync_err_3;
+ }
+
+ sync_fence_install(retire_fence, retire_fen_fd);
+
+ ret = copy_to_user(buf_sync->retire_fen_fd, &retire_fen_fd,
+ sizeof(int));
+ if (ret) {
+ pr_err("%s: copy_to_user failed for retire fence\n",
+ sync_pt_data->fence_name);
+ put_unused_fd(retire_fen_fd);
+ sync_fence_put(retire_fence);
+ goto buf_sync_err_3;
+ }
+
+skip_retire_fence:
mutex_unlock(&sync_pt_data->sync_mutex);
if (buf_sync->flags & MDP_BUF_SYNC_FLAG_WAIT)
diff --git a/drivers/video/msm/mdss/mdss_fb.h b/drivers/video/msm/mdss/mdss_fb.h
index 9e44e32..2e024c9 100644
--- a/drivers/video/msm/mdss/mdss_fb.h
+++ b/drivers/video/msm/mdss/mdss_fb.h
@@ -96,13 +96,16 @@
struct sw_sync_timeline *timeline;
int timeline_value;
u32 threshold;
-
+ u32 retire_threshold;
atomic_t commit_cnt;
bool flushed;
bool async_wait_fences;
struct mutex sync_mutex;
struct notifier_block notifier;
+
+ struct sync_fence *(*get_retire_fence)
+ (struct msm_sync_pt_data *sync_pt_data);
};
struct msm_fb_data_type;
@@ -246,6 +249,8 @@
void mdss_fb_update_backlight(struct msm_fb_data_type *mfd);
void mdss_fb_wait_for_fence(struct msm_sync_pt_data *sync_pt_data);
void mdss_fb_signal_timeline(struct msm_sync_pt_data *sync_pt_data);
+struct sync_fence *mdss_fb_sync_get_fence(struct sw_sync_timeline *timeline,
+ const char *fence_name, int val);
int mdss_fb_register_mdp_instance(struct msm_mdp_interface *mdp);
int mdss_fb_dcm(struct msm_fb_data_type *mfd, int req_state);
int mdss_fb_suspres_panel(struct device *dev, void *data);
diff --git a/drivers/video/msm/mdss/mdss_hdmi_edid.c b/drivers/video/msm/mdss/mdss_hdmi_edid.c
index a45876b..d0b89a3 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_edid.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_edid.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
@@ -1120,7 +1120,7 @@
u8 i = 0, offset = 0, std_blk = 0;
u32 video_format = HDMI_VFRMT_640x480p60_4_3;
u32 has480p = false;
- u8 len;
+ u8 len = 0;
int rc;
const u8 *edid_blk0 = NULL;
const u8 *edid_blk1 = NULL;
@@ -1140,7 +1140,7 @@
hdmi_edid_find_block(data_buf+0x80, DBC_START_OFFSET,
VIDEO_DATA_BLOCK, &len) : NULL;
- if (svd == NULL || len == 0 || len > MAX_DATA_BLOCK_SIZE) {
+ if (num_of_cea_blocks && (len == 0 || len > MAX_DATA_BLOCK_SIZE)) {
DEV_DBG("%s: No/Invalid Video Data Block\n",
__func__);
return;
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.c b/drivers/video/msm/mdss/mdss_hdmi_tx.c
index 426855c..79afdca 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.c
@@ -120,6 +120,7 @@
static inline void hdmi_tx_set_audio_switch_node(struct hdmi_tx_ctrl *hdmi_ctrl,
int val, bool force);
static int hdmi_tx_audio_setup(struct hdmi_tx_ctrl *hdmi_ctrl);
+static void hdmi_tx_en_encryption(struct hdmi_tx_ctrl *hdmi_ctrl, u32 on);
struct mdss_hw hdmi_tx_hw = {
.hw_ndx = MDSS_HW_HDMI,
@@ -916,11 +917,11 @@
switch (status) {
case HDCP_STATE_AUTHENTICATED:
if (hdmi_ctrl->hpd_state) {
- /* Clear AV Mute */
- rc = hdmi_tx_config_avmute(hdmi_ctrl, 0);
- if (rc)
- DEV_ERR("%s: Failed to clear av mute. rc=%d\n",
- __func__, rc);
+ if (hdmi_ctrl->pdata.primary)
+ hdmi_tx_en_encryption(hdmi_ctrl, true);
+ else
+ /* Clear AV Mute */
+ rc = hdmi_tx_config_avmute(hdmi_ctrl, 0);
hdmi_tx_set_audio_switch_node(hdmi_ctrl, 1, false);
}
break;
@@ -928,11 +929,11 @@
hdmi_tx_set_audio_switch_node(hdmi_ctrl, 0, false);
if (hdmi_ctrl->hpd_state) {
- /* Set AV Mute */
- rc = hdmi_tx_config_avmute(hdmi_ctrl, 1);
- if (rc)
- DEV_ERR("%s: Failed to set av mute. rc=%d\n",
- __func__, rc);
+ if (hdmi_ctrl->pdata.primary)
+ hdmi_tx_en_encryption(hdmi_ctrl, false);
+ else
+ /* Set AV Mute */
+ rc = hdmi_tx_config_avmute(hdmi_ctrl, 1);
DEV_DBG("%s: Reauthenticating\n", __func__);
rc = hdmi_hdcp_reauthenticate(
@@ -1646,6 +1647,28 @@
DSS_REG_W(io, HDMI_GEN_PKT_CTRL, packet_control);
} /* hdmi_tx_set_spd_infoframe */
+static void hdmi_tx_en_encryption(struct hdmi_tx_ctrl *hdmi_ctrl, u32 on)
+{
+ u32 reg_val;
+ struct dss_io_data *io = NULL;
+
+ if (!hdmi_ctrl->hdcp_feature_on || !hdmi_ctrl->present_hdcp)
+ return;
+
+ io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
+
+ mutex_lock(&hdmi_ctrl->mutex);
+ reg_val = DSS_REG_R_ND(io, HDMI_CTRL);
+
+ if (on)
+ reg_val |= BIT(2);
+ else
+ reg_val &= ~BIT(2);
+ DSS_REG_W(io, HDMI_CTRL, reg_val);
+
+ mutex_unlock(&hdmi_ctrl->mutex);
+} /* hdmi_tx_en_encryption */
+
static void hdmi_tx_set_mode(struct hdmi_tx_ctrl *hdmi_ctrl, u32 power_on)
{
struct dss_io_data *io = NULL;
@@ -1662,12 +1685,14 @@
return;
}
+ mutex_lock(&hdmi_ctrl->mutex);
if (power_on) {
/* Enable the block */
reg_val |= BIT(0);
/* HDMI Encryption, if HDCP is enabled */
- if (hdmi_ctrl->hdcp_feature_on && hdmi_ctrl->present_hdcp)
+ if (hdmi_ctrl->hdcp_feature_on &&
+ hdmi_ctrl->present_hdcp && !hdmi_ctrl->pdata.primary)
reg_val |= BIT(2);
/* Set transmission mode to DVI based in EDID info */
@@ -1677,6 +1702,7 @@
}
DSS_REG_W(io, HDMI_CTRL, reg_val);
+ mutex_unlock(&hdmi_ctrl->mutex);
DEV_DBG("HDMI Core: %s, HDMI_CTRL=0x%08x\n",
power_on ? "Enable" : "Disable", reg_val);
@@ -2642,6 +2668,11 @@
hdmi_ctrl->panel_power_on = true;
mutex_unlock(&hdmi_ctrl->mutex);
+ if (hdmi_ctrl->pdata.primary) {
+ if (hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_DDC_PM, true))
+ DEV_ERR("%s: Failed to enable ddc power\n", __func__);
+ }
+
hdmi_cec_config(hdmi_ctrl->feature_data[HDMI_TX_FEAT_CEC]);
if (hdmi_ctrl->hpd_state) {
@@ -3109,10 +3140,10 @@
case MDSS_EVENT_PANEL_ON:
if (hdmi_ctrl->hdcp_feature_on && hdmi_ctrl->present_hdcp) {
/* Set AV Mute before starting authentication */
- rc = hdmi_tx_config_avmute(hdmi_ctrl, 1);
- if (rc)
- DEV_ERR("%s: Failed to set av mute. rc=%d\n",
- __func__, rc);
+ if (hdmi_ctrl->pdata.primary)
+ hdmi_tx_en_encryption(hdmi_ctrl, false);
+ else
+ rc = hdmi_tx_config_avmute(hdmi_ctrl, 1);
DEV_DBG("%s: Starting HDCP authentication\n", __func__);
rc = hdmi_hdcp_authenticate(
diff --git a/drivers/video/msm/mdss/mdss_mdp.c b/drivers/video/msm/mdss/mdss_mdp.c
index 8fcde84..088472f 100644
--- a/drivers/video/msm/mdss/mdss_mdp.c
+++ b/drivers/video/msm/mdss/mdss_mdp.c
@@ -1,7 +1,7 @@
/*
* MDSS MDP Interface (used by framebuffer core)
*
- * Copyright (c) 2007-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2007-2014, The Linux Foundation. All rights reserved.
* Copyright (C) 2007 Google Incorporated
*
* This software is licensed under the terms of the GNU General Public
@@ -82,33 +82,12 @@
static DEFINE_MUTEX(bus_bw_lock);
static DEFINE_MUTEX(mdp_iommu_lock);
-#define MDP_BUS_VECTOR_ENTRY(ab_val, ib_val) \
- { \
- .src = MSM_BUS_MASTER_MDP_PORT0, \
- .dst = MSM_BUS_SLAVE_EBI_CH0, \
- .ab = (ab_val), \
- .ib = (ib_val), \
- }
-
-static struct msm_bus_vectors mdp_bus_vectors[] = {
- MDP_BUS_VECTOR_ENTRY(0, 0),
- MDP_BUS_VECTOR_ENTRY(SZ_128M, SZ_256M),
- MDP_BUS_VECTOR_ENTRY(SZ_256M, SZ_512M),
-};
-static struct msm_bus_paths mdp_bus_usecases[ARRAY_SIZE(mdp_bus_vectors)];
-
static struct mdss_panel_intf pan_types[] = {
{"dsi", MDSS_PANEL_INTF_DSI},
{"edp", MDSS_PANEL_INTF_EDP},
{"hdmi", MDSS_PANEL_INTF_HDMI},
};
-static struct msm_bus_scale_pdata mdp_bus_scale_table = {
- .usecase = mdp_bus_usecases,
- .num_usecases = ARRAY_SIZE(mdp_bus_usecases),
- .name = "mdss_mdp",
-};
-
struct mdss_iommu_map_type mdss_iommu_map[MDSS_IOMMU_MAX_DOMAIN] = {
[MDSS_IOMMU_DOMAIN_UNSECURE] = {
.client_name = "mdp_ns",
@@ -154,8 +133,10 @@
static int mdss_mdp_parse_dt_prop_len(struct platform_device *pdev,
char *prop_name);
static int mdss_mdp_parse_dt_smp(struct platform_device *pdev);
+static int mdss_mdp_parse_dt_prefill(struct platform_device *pdev);
static int mdss_mdp_parse_dt_misc(struct platform_device *pdev);
static int mdss_mdp_parse_dt_ad_cfg(struct platform_device *pdev);
+static int mdss_mdp_parse_dt_bus_scale(struct platform_device *pdev);
u32 mdss_mdp_fb_stride(u32 fb_index, u32 xres, int bpp)
{
@@ -332,23 +313,17 @@
static int mdss_mdp_bus_scale_register(struct mdss_data_type *mdata)
{
if (!mdata->bus_hdl) {
- struct msm_bus_scale_pdata *bus_pdata = &mdp_bus_scale_table;
- int i;
-
- for (i = 0; i < bus_pdata->num_usecases; i++) {
- mdp_bus_usecases[i].num_paths = 1;
- mdp_bus_usecases[i].vectors = &mdp_bus_vectors[i];
- }
-
- mdata->bus_hdl = msm_bus_scale_register_client(bus_pdata);
- if (!mdata->bus_hdl) {
- pr_err("not able to get bus scale\n");
- return -ENOMEM;
+ mdata->bus_hdl =
+ msm_bus_scale_register_client(mdata->bus_scale_table);
+ if (IS_ERR_VALUE(mdata->bus_hdl)) {
+ pr_err("bus_client register failed\n");
+ return -EINVAL;
}
pr_debug("register bus_hdl=%x\n", mdata->bus_hdl);
}
- return 0;
+
+ return mdss_mdp_bus_scale_set_quota(AB_QUOTA, IB_QUOTA);
}
static void mdss_mdp_bus_scale_unregister(struct mdss_data_type *mdata)
@@ -361,7 +336,7 @@
int mdss_mdp_bus_scale_set_quota(u64 ab_quota, u64 ib_quota)
{
- int bus_idx;
+ int new_uc_idx;
if (mdss_res->bus_hdl < 1) {
pr_err("invalid bus handle %d\n", mdss_res->bus_hdl);
@@ -369,32 +344,50 @@
}
if ((ab_quota | ib_quota) == 0) {
- bus_idx = 0;
+ new_uc_idx = 0;
} else {
- int num_cases = mdp_bus_scale_table.num_usecases;
+ int i;
struct msm_bus_vectors *vect = NULL;
+ struct msm_bus_scale_pdata *bw_table =
+ mdss_res->bus_scale_table;
+ unsigned long size;
- bus_idx = (mdss_res->current_bus_idx % (num_cases - 1)) + 1;
-
- vect = mdp_bus_scale_table.usecase[mdss_res->current_bus_idx].
- vectors;
-
- /* avoid performing updates for small changes */
- if ((ALIGN(ab_quota, SZ_64M) == ALIGN(vect->ab, SZ_64M)) &&
- (ALIGN(ib_quota, SZ_64M) == ALIGN(vect->ib, SZ_64M))) {
- pr_debug("skip bus scaling, no change in vectors\n");
- return 0;
+ if (!bw_table || !mdss_res->axi_port_cnt) {
+ pr_err("invalid input\n");
+ return -EINVAL;
}
- vect = mdp_bus_scale_table.usecase[bus_idx].vectors;
- vect->ab = ab_quota;
- vect->ib = ib_quota;
+ size = SZ_64M / mdss_res->axi_port_cnt;
- pr_debug("bus scale idx=%d ab=%llu ib=%llu\n", bus_idx,
- vect->ab, vect->ib);
+ ab_quota = div_u64(ab_quota, mdss_res->axi_port_cnt);
+ ib_quota = div_u64(ib_quota, mdss_res->axi_port_cnt);
+
+ new_uc_idx = (mdss_res->curr_bw_uc_idx %
+ (bw_table->num_usecases - 1)) + 1;
+
+ for (i = 0; i < mdss_res->axi_port_cnt; i++) {
+ vect = &bw_table->usecase[mdss_res->curr_bw_uc_idx].
+ vectors[i];
+
+ /* avoid performing updates for small changes */
+ if ((ALIGN(ab_quota, size) == ALIGN(vect->ab, size)) &&
+ (ALIGN(ib_quota, size) == ALIGN(vect->ib, size))) {
+ pr_debug("skip bus scaling, no changes\n");
+ return 0;
+ }
+
+ vect = &bw_table->usecase[new_uc_idx].vectors[i];
+ vect->ab = ab_quota;
+ vect->ib = ib_quota;
+
+ pr_debug("uc_idx=%d path_idx=%d ab=%llu ib=%llu\n",
+ new_uc_idx, i, vect->ab, vect->ib);
+ }
}
- mdss_res->current_bus_idx = bus_idx;
- return msm_bus_scale_client_update_request(mdss_res->bus_hdl, bus_idx);
+ mdss_res->curr_bw_uc_idx = new_uc_idx;
+
+ return msm_bus_scale_client_update_request(mdss_res->bus_hdl,
+ new_uc_idx);
}
static inline u32 mdss_mdp_irq_mask(u32 intr_type, u32 intf_num)
@@ -654,7 +647,7 @@
} else {
pm_runtime_get_sync(&mdata->pdev->dev);
msm_bus_scale_client_update_request(
- mdata->bus_hdl, mdata->current_bus_idx);
+ mdata->bus_hdl, mdata->curr_bw_uc_idx);
if (!mdata->handoff_pending)
mdss_iommu_attach(mdata);
}
@@ -1098,6 +1091,8 @@
SPRINT(" bwc");
if (mdata->has_decimation)
SPRINT(" decimation");
+ if (mdata->highest_bank_bit)
+ SPRINT(" tile_format");
SPRINT("\n");
return cnt;
@@ -1218,7 +1213,6 @@
pr_err("unable to register bus scaling\n");
goto probe_done;
}
- mdss_mdp_bus_scale_set_quota(AB_QUOTA, IB_QUOTA);
rc = mdss_mdp_debug_init(mdata);
if (rc) {
@@ -1539,6 +1533,12 @@
return rc;
}
+ rc = mdss_mdp_parse_dt_prefill(pdev);
+ if (rc) {
+ pr_err("Error in device tree : prefill\n");
+ return rc;
+ }
+
rc = mdss_mdp_parse_dt_misc(pdev);
if (rc) {
pr_err("Error in device tree : misc\n");
@@ -1558,6 +1558,12 @@
return rc;
}
+ rc = mdss_mdp_parse_dt_bus_scale(pdev);
+ if (rc) {
+ pr_err("Error in device tree : bus scale\n");
+ return rc;
+ }
+
return 0;
}
@@ -2014,6 +2020,84 @@
return rc;
}
+static void mdss_mdp_parse_dt_fudge_factors(struct platform_device *pdev,
+ char *prop_name, struct mdss_fudge_factor *ff)
+{
+ int rc;
+ u32 data[2] = {1, 1};
+
+ rc = mdss_mdp_parse_dt_handler(pdev, prop_name, data, 2);
+ if (rc) {
+ pr_err("err reading %s\n", prop_name);
+ } else {
+ ff->numer = data[0];
+ ff->denom = data[1];
+ }
+}
+
+static int mdss_mdp_parse_dt_prefill(struct platform_device *pdev)
+{
+ struct mdss_data_type *mdata = platform_get_drvdata(pdev);
+ struct mdss_prefill_data *prefill = &mdata->prefill_data;
+ int rc;
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,mdss-prefill-outstanding-buffer-bytes",
+ &prefill->ot_bytes);
+ if (rc) {
+ pr_err("prefill outstanding buffer bytes not specified\n");
+ return rc;
+ }
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,mdss-prefill-y-buffer-bytes", &prefill->y_buf_bytes);
+ if (rc) {
+ pr_err("prefill y buffer bytes not specified\n");
+ return rc;
+ }
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,mdss-prefill-scaler-buffer-lines-bilinear",
+ &prefill->y_scaler_lines_bilinear);
+ if (rc) {
+ pr_err("prefill scaler lines for bilinear not specified\n");
+ return rc;
+ }
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,mdss-prefill-scaler-buffer-lines-caf",
+ &prefill->y_scaler_lines_caf);
+ if (rc) {
+ pr_debug("prefill scaler lines for caf not specified\n");
+ return rc;
+ }
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,mdss-prefill-post-scaler-buffer-pixels",
+ &prefill->post_scaler_pixels);
+ if (rc) {
+ pr_err("prefill post scaler buffer pixels not specified\n");
+ return rc;
+ }
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,mdss-prefill-pingpong-buffer-pixels",
+ &prefill->pp_pixels);
+ if (rc) {
+ pr_err("prefill pingpong buffer lines not specified\n");
+ return rc;
+ }
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,mdss-prefill-fbc-lines", &prefill->fbc_lines);
+ if (rc) {
+ pr_err("prefill FBC lines not specified\n");
+ return rc;
+ }
+
+ return 0;
+}
+
static int mdss_mdp_parse_dt_misc(struct platform_device *pdev)
{
struct mdss_data_type *mdata = platform_get_drvdata(pdev);
@@ -2025,6 +2109,10 @@
&data);
mdata->rot_block_size = (!rc ? data : 128);
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,mdss-rotator-ot-limit", &data);
+ mdata->rotator_ot_limit = (!rc ? data : 0);
+
mdata->has_bwc = of_property_read_bool(pdev->dev.of_node,
"qcom,mdss-has-bwc");
mdata->has_decimation = of_property_read_bool(pdev->dev.of_node,
@@ -2033,6 +2121,34 @@
"qcom,mdss-has-wfd-blk");
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,
+ "qcom,mdss-highest-bank-bit", &(mdata->highest_bank_bit));
+ if (rc)
+ pr_debug("Could not read optional property: highest bank bit\n");
+
+ /*
+ * 2x factor on AB because bus driver will divide by 2
+ * due to 2x ports to BIMC
+ */
+ mdata->ab_factor.numer = 2;
+ mdata->ab_factor.denom = 1;
+ mdss_mdp_parse_dt_fudge_factors(pdev, "qcom,mdss-ab-factor",
+ &mdata->ab_factor);
+
+ /*
+ * 1.2 factor on ib as default value. This value is
+ * experimentally determined and should be tuned in device
+ * tree.
+ */
+ mdata->ib_factor.numer = 6;
+ mdata->ib_factor.denom = 5;
+ mdss_mdp_parse_dt_fudge_factors(pdev, "qcom,mdss-ib-factor",
+ &mdata->ib_factor);
+
+ mdata->clk_factor.numer = 1;
+ mdata->clk_factor.denom = 1;
+ mdss_mdp_parse_dt_fudge_factors(pdev, "qcom,mdss-clk-factor",
+ &mdata->clk_factor);
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,max-bandwidth-low-kbps", &mdata->max_bw_low);
@@ -2087,6 +2203,31 @@
return rc;
}
+static int mdss_mdp_parse_dt_bus_scale(struct platform_device *pdev)
+{
+ int rc;
+ struct mdss_data_type *mdata = platform_get_drvdata(pdev);
+
+ rc = of_property_read_u32(pdev->dev.of_node, "qcom,msm-bus,num-paths",
+ &mdata->axi_port_cnt);
+ if (rc) {
+ pr_err("Error. qcom,msm-bus,num-paths prop not found.rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ mdata->bus_scale_table = msm_bus_cl_get_pdata(pdev);
+ if (IS_ERR_OR_NULL(mdata->bus_scale_table)) {
+ rc = PTR_ERR(mdata->bus_scale_table);
+ if (!rc)
+ rc = -EINVAL;
+ pr_err("msm_bus_cl_get_pdata failed. rc=%d\n", rc);
+ mdata->bus_scale_table = NULL;
+ }
+
+ return rc;
+}
+
static int mdss_mdp_parse_dt_handler(struct platform_device *pdev,
char *prop_name, u32 *offsets, int len)
{
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index 1c4efb4..9d200b9 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.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
@@ -145,6 +145,14 @@
MDSS_MDP_WB_CTL_TYPE_LINE
};
+struct mdss_mdp_perf_params {
+ u64 bw_overlap;
+ u64 bw_prefill;
+ u32 prefill_bytes;
+ u64 bw_ctl;
+ u32 mdp_clk_rate;
+};
+
struct mdss_mdp_ctl {
u32 num;
char __iomem *base;
@@ -168,11 +176,11 @@
u32 dst_format;
bool is_secure;
- u32 bus_ab_quota;
- u32 bus_ib_quota;
u32 clk_rate;
- u32 perf_changed;
int force_screen_state;
+ struct mdss_mdp_perf_params cur_perf;
+ struct mdss_mdp_perf_params new_perf;
+ int perf_status;
struct mdss_data_type *mdata;
struct msm_fb_data_type *mfd;
@@ -180,6 +188,7 @@
struct mdss_mdp_mixer *mixer_right;
struct mutex lock;
struct mutex *shared_lock;
+ spinlock_t spin_lock;
struct mdss_panel_data *panel_data;
struct mdss_mdp_vsync_handler vsync_handler;
@@ -198,7 +207,8 @@
struct mdss_mdp_vsync_handler *);
int (*remove_vsync_handler) (struct mdss_mdp_ctl *,
struct mdss_mdp_vsync_handler *);
- int (*config_fps_fnc) (struct mdss_mdp_ctl *ctl, int new_fps);
+ int (*config_fps_fnc) (struct mdss_mdp_ctl *ctl,
+ struct mdss_mdp_ctl *sctl, int new_fps);
struct blocking_notifier_head notifier_head;
@@ -237,7 +247,7 @@
u8 unpack_count; /* 0 = 1 component, 1 = 2 component ... */
u8 bpp;
u8 alpha_enable; /* source has alpha */
-
+ u8 tile;
u8 bits[MAX_PLANES];
u8 element[MAX_PLANES];
};
@@ -422,12 +432,11 @@
u32 splash_mem_addr;
u32 splash_mem_size;
u32 sd_enabled;
-};
-struct mdss_mdp_perf_params {
- u32 ib_quota;
- u32 ab_quota;
- u32 mdp_clk_rate;
+ struct sw_sync_timeline *vsync_timeline;
+ struct mdss_mdp_vsync_handler vsync_retire_handler;
+ struct work_struct retire_work;
+ int retire_cnt;
};
/**
@@ -520,7 +529,7 @@
int mdss_mdp_ctl_stop(struct mdss_mdp_ctl *ctl);
int mdss_mdp_ctl_intf_event(struct mdss_mdp_ctl *ctl, int event, void *arg);
int mdss_mdp_perf_calc_pipe(struct mdss_mdp_pipe *pipe,
- struct mdss_mdp_perf_params *perf);
+ struct mdss_mdp_perf_params *perf, struct mdss_mdp_img_rect *roi);
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);
@@ -529,8 +538,13 @@
int mdss_mdp_mixer_handoff(struct mdss_mdp_ctl *ctl, u32 num,
struct mdss_mdp_pipe *pipe);
+
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_release_bw(struct mdss_mdp_ctl *ctl);
+
struct mdss_mdp_mixer *mdss_mdp_wb_mixer_alloc(int rotator);
int mdss_mdp_wb_mixer_destroy(struct mdss_mdp_mixer *mixer);
struct mdss_mdp_mixer *mdss_mdp_mixer_get(struct mdss_mdp_ctl *ctl, int mux);
@@ -598,6 +612,7 @@
void mdss_mdp_pipe_unmap(struct mdss_mdp_pipe *pipe);
struct mdss_mdp_pipe *mdss_mdp_pipe_alloc_dma(struct mdss_mdp_mixer *mixer);
+u32 mdss_mdp_smp_get_size(struct mdss_mdp_pipe *pipe);
int mdss_mdp_smp_reserve(struct mdss_mdp_pipe *pipe);
void mdss_mdp_smp_unreserve(struct mdss_mdp_pipe *pipe);
void mdss_mdp_smp_release(struct mdss_mdp_pipe *pipe);
@@ -632,6 +647,10 @@
void mdss_mdp_intersect_rect(struct mdss_mdp_img_rect *res_rect,
const struct mdss_mdp_img_rect *dst_rect,
const struct mdss_mdp_img_rect *sci_rect);
+void mdss_mdp_crop_rect(struct mdss_mdp_img_rect *src_rect,
+ struct mdss_mdp_img_rect *dst_rect,
+ const struct mdss_mdp_img_rect *sci_rect);
+
int mdss_mdp_wb_kickoff(struct msm_fb_data_type *mfd);
int mdss_mdp_wb_ioctl_handler(struct msm_fb_data_type *mfd, u32 cmd, void *arg);
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index db67773..1a4885c 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -18,30 +18,26 @@
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
+#include <linux/sort.h>
#include "mdss_fb.h"
#include "mdss_mdp.h"
-/* truncate at 1k */
-#define MDSS_MDP_BUS_FACTOR_SHIFT 10
-/* 1.5 bus fudge factor */
-#define MDSS_MDP_BUS_FUDGE_FACTOR_IB(val) (((val) / 2) * 3)
-#define MDSS_MDP_BUS_FUDGE_FACTOR_HIGH_IB(val) (val << 1)
-#define MDSS_MDP_BUS_FUDGE_FACTOR_AB(val) (val << 1)
-#define MDSS_MDP_BUS_FLOOR_BW (1600000000ULL >> MDSS_MDP_BUS_FACTOR_SHIFT)
+static inline u64 fudge_factor(u64 val, u32 numer, u32 denom)
+{
+ u64 result = (val * (u64)numer);
+ do_div(result, denom);
+ return result;
+}
-/* 1.25 clock fudge factor */
-#define MDSS_MDP_CLK_FUDGE_FACTOR(val) (((val) * 5) / 4)
+#define AB_FUDGE_FACTOR(val) fudge_factor((val), \
+ (mdss_res->ab_factor.numer), (mdss_res->ab_factor.denom))
-enum {
- MDSS_MDP_PERF_UPDATE_SKIP,
- MDSS_MDP_PERF_UPDATE_EARLY,
- MDSS_MDP_PERF_UPDATE_LATE,
-};
+#define IB_FUDGE_FACTOR(val) fudge_factor((val), \
+ (mdss_res->ib_factor.numer), (mdss_res->ib_factor.denom))
-#define MDSS_MDP_PERF_UPDATE_CLK BIT(0)
-#define MDSS_MDP_PERF_UPDATE_BUS BIT(1)
-#define MDSS_MDP_PERF_UPDATE_ALL -1
+#define CLK_FUDGE_FACTOR(val) fudge_factor((val), \
+ (mdss_res->clk_factor.numer), (mdss_res->clk_factor.denom))
static DEFINE_MUTEX(mdss_mdp_ctl_lock);
@@ -63,112 +59,243 @@
pinfo->clk_rate;
}
-static u32 __mdss_mdp_ctrl_perf_ovrd_helper(struct mdss_mdp_mixer *mixer,
- u32 *npipe)
+static inline u32 mdss_mdp_clk_fudge_factor(struct mdss_mdp_mixer *mixer,
+ u32 rate)
{
- struct mdss_panel_info *pinfo;
- struct mdss_mdp_pipe *pipe;
- u32 mnum, ovrd = 0;
+ struct mdss_panel_info *pinfo = &mixer->ctl->panel_data->panel_info;
- if (!mixer || !mixer->ctl->panel_data)
- return 0;
+ rate = CLK_FUDGE_FACTOR(rate);
- pinfo = &mixer->ctl->panel_data->panel_info;
- for (mnum = 0; mnum < MDSS_MDP_MAX_STAGE; mnum++) {
- pipe = mixer->stage_pipe[mnum];
- if (pipe && pinfo) {
- *npipe = *npipe + 1;
- if ((pipe->src.w >= pipe->src.h) &&
- (pipe->src.w >= pinfo->xres))
- ovrd = 1;
- }
- }
+ /*
+ * If the panel is video mode and its back porch period is
+ * small, the workaround of increasing mdp clk is needed to
+ * avoid underrun.
+ */
+ if (mixer->ctl->is_video_mode && pinfo &&
+ (pinfo->lcdc.v_back_porch < MDP_MIN_VBP))
+ rate = CLK_FUDGE_FACTOR(rate);
- return ovrd;
+ return rate;
}
-/**
- * mdss_mdp_ctrl_perf_ovrd() - Determines if performance override is needed
- * @mdata: Struct containing references to all MDP5 hardware structures
- * and status info such as interupts, target caps etc.
- * @ab_quota: Arbitrated bandwidth quota
- * @ib_quota: Instantaneous bandwidth quota
- *
- * Function calculates the minimum required MDP and BIMC clocks to avoid MDP
- * underflow during portrait video playback. The calculations are based on the
- * way MDP fetches (bandwidth requirement) and processes data through
- * MDP pipeline (MDP clock requirement) based on frame size and scaling
- * requirements.
- */
-static void __mdss_mdp_ctrl_perf_ovrd(struct mdss_data_type *mdata,
- u64 *ab_quota, u64 *ib_quota)
+struct mdss_mdp_prefill_params {
+ u32 smp_bytes;
+ u32 xres;
+ u32 src_w;
+ u32 dst_w;
+ u32 src_h;
+ u32 dst_h;
+ u32 dst_y;
+ u32 bpp;
+ bool is_yuv;
+ bool is_caf;
+ bool is_fbc;
+ bool is_bwc;
+ bool is_tile;
+ bool is_hflip;
+};
+
+static inline bool mdss_mdp_perf_is_caf(struct mdss_mdp_pipe *pipe)
{
- struct mdss_mdp_ctl *ctl;
- u32 i, npipe = 0, ovrd = 0;
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
- for (i = 0; i < mdata->nctl; i++) {
- ctl = mdata->ctl_off + i;
- if (!ctl->power_on)
- continue;
- ovrd |= __mdss_mdp_ctrl_perf_ovrd_helper(
- ctl->mixer_left, &npipe);
- ovrd |= __mdss_mdp_ctrl_perf_ovrd_helper(
- ctl->mixer_right, &npipe);
- }
+ /*
+ * CAF mode filter is enabled when format is yuv and
+ * upscaling. Post processing had the decision to use CAF
+ * under these conditions.
+ */
+ return ((mdata->mdp_rev >= MDSS_MDP_HW_REV_102) &&
+ pipe->src_fmt->is_yuv && ((pipe->src.h >> pipe->vert_deci) <=
+ pipe->dst.h));
+}
- *ab_quota = MDSS_MDP_BUS_FUDGE_FACTOR_AB(*ab_quota);
- if (npipe > 1)
- *ib_quota = MDSS_MDP_BUS_FUDGE_FACTOR_HIGH_IB(*ib_quota);
- else
- *ib_quota = MDSS_MDP_BUS_FUDGE_FACTOR_IB(*ib_quota);
+static inline u32 mdss_mdp_calc_y_scaler_bytes(struct mdss_mdp_prefill_params
+ *params, struct mdss_prefill_data *prefill)
+{
+ u32 y_scaler_bytes = 0, y_scaler_lines = 0;
- if (ovrd && (*ib_quota < MDSS_MDP_BUS_FLOOR_BW)) {
- *ib_quota = MDSS_MDP_BUS_FLOOR_BW;
- pr_debug("forcing the BIMC clock to 200 MHz : %llu bytes",
- *ib_quota);
+ if (params->is_yuv) {
+ if (params->src_h != params->dst_h) {
+ y_scaler_lines = (params->is_caf) ?
+ prefill->y_scaler_lines_caf :
+ prefill->y_scaler_lines_bilinear;
+ /*
+ * y is src_width, u is src_width/2 and v is
+ * src_width/2, so the total is scaler_lines *
+ * src_w * 2
+ */
+ y_scaler_bytes = y_scaler_lines * params->src_w * 2;
+ }
} else {
- pr_debug("ib quota : %llu bytes", *ib_quota);
- }
-}
-
-static int mdss_mdp_ctl_perf_commit(struct mdss_data_type *mdata, u32 flags)
-{
- struct mdss_mdp_ctl *ctl;
- int cnum;
- unsigned long clk_rate = 0;
- u64 bus_ab_quota = 0, bus_ib_quota = 0;
-
- if (!flags) {
- pr_err("nothing to update\n");
- return -EINVAL;
- }
-
- mutex_lock(&mdss_mdp_ctl_lock);
- for (cnum = 0; cnum < mdata->nctl; cnum++) {
- ctl = mdata->ctl_off + cnum;
- if (ctl->power_on) {
- bus_ab_quota += ctl->bus_ab_quota;
- bus_ib_quota += ctl->bus_ib_quota;
-
- if (ctl->clk_rate > clk_rate)
- clk_rate = ctl->clk_rate;
+ if (params->src_h != params->dst_h) {
+ y_scaler_lines = prefill->y_scaler_lines_bilinear;
+ y_scaler_bytes = y_scaler_lines * params->src_w *
+ params->bpp;
}
}
- if (flags & MDSS_MDP_PERF_UPDATE_BUS) {
- bus_ab_quota = bus_ib_quota;
- __mdss_mdp_ctrl_perf_ovrd(mdata, &bus_ab_quota, &bus_ib_quota);
- bus_ib_quota <<= MDSS_MDP_BUS_FACTOR_SHIFT;
- bus_ab_quota <<= MDSS_MDP_BUS_FACTOR_SHIFT;
- mdss_mdp_bus_scale_set_quota(bus_ab_quota, bus_ib_quota);
- }
- if (flags & MDSS_MDP_PERF_UPDATE_CLK) {
- clk_rate = MDSS_MDP_CLK_FUDGE_FACTOR(clk_rate);
- pr_debug("update clk rate = %lu HZ\n", clk_rate);
- mdss_mdp_set_clk_rate(clk_rate);
- }
- mutex_unlock(&mdss_mdp_ctl_lock);
- return 0;
+ return y_scaler_bytes;
+}
+
+static inline u32 mdss_mdp_calc_latency_buf_bytes(struct mdss_mdp_prefill_params
+ *params, struct mdss_prefill_data *prefill)
+{
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+ u32 latency_lines, latency_buf_bytes;
+
+ if (params->is_yuv) {
+ if (params->is_bwc) {
+ latency_lines = 4;
+ latency_buf_bytes = params->src_w * params->bpp *
+ latency_lines;
+ } else {
+ latency_lines = 2;
+ latency_buf_bytes = ALIGN(params->src_w * params->bpp *
+ latency_lines, mdata->smp_mb_size) * 2;
+ }
+ } else {
+ if (params->is_tile) {
+ latency_lines = 8;
+ latency_buf_bytes = params->src_w * params->bpp *
+ latency_lines;
+ } else if (params->is_bwc) {
+ latency_lines = 4;
+ latency_buf_bytes = params->src_w * params->bpp *
+ latency_lines;
+ } else {
+ latency_lines = 2;
+ latency_buf_bytes = ALIGN(params->src_w * params->bpp *
+ latency_lines, mdata->smp_mb_size);
+ }
+ }
+
+ return latency_buf_bytes;
+}
+
+static inline u32 mdss_mdp_calc_scaling_w_h(u32 val, u32 src_h, u32 dst_h,
+ u32 src_w, u32 dst_w)
+{
+ if (dst_h)
+ val = mult_frac(val, src_h, dst_h);
+ if (dst_w)
+ val = mult_frac(val, src_w, dst_w);
+
+ return val;
+}
+
+static u32 mdss_mdp_perf_calc_pipe_prefill_video(struct mdss_mdp_prefill_params
+ *params)
+{
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+ struct mdss_prefill_data *prefill = &mdata->prefill_data;
+ u32 prefill_bytes;
+ u32 latency_buf_bytes;
+ u32 y_buf_bytes = 0;
+ u32 y_scaler_bytes;
+ u32 pp_bytes = 0, pp_lines = 0;
+ u32 post_scaler_bytes;
+ u32 fbc_bytes = 0;
+
+ prefill_bytes = prefill->ot_bytes;
+
+ latency_buf_bytes = mdss_mdp_calc_latency_buf_bytes(params, prefill);
+ prefill_bytes += latency_buf_bytes;
+ pr_debug("latency_buf_bytes bw_calc=%d actual=%d\n", latency_buf_bytes,
+ params->smp_bytes);
+
+ if (params->is_yuv)
+ y_buf_bytes = prefill->y_buf_bytes;
+
+ y_scaler_bytes = mdss_mdp_calc_y_scaler_bytes(params, prefill);
+
+ prefill_bytes += y_buf_bytes + y_scaler_bytes;
+
+ post_scaler_bytes = prefill->post_scaler_pixels * params->bpp;
+ post_scaler_bytes = mdss_mdp_calc_scaling_w_h(post_scaler_bytes,
+ params->src_h, params->dst_h, params->src_w, params->dst_w);
+ prefill_bytes += post_scaler_bytes;
+
+ if (params->xres)
+ pp_lines = DIV_ROUND_UP(prefill->pp_pixels, params->xres);
+ if (params->xres && params->dst_h && (params->dst_y <= pp_lines))
+ pp_bytes = ((params->src_w * params->bpp * prefill->pp_pixels /
+ params->xres) * params->src_h) / params->dst_h;
+ prefill_bytes += pp_bytes;
+
+ if (params->is_fbc) {
+ fbc_bytes = prefill->fbc_lines * params->bpp;
+ fbc_bytes = mdss_mdp_calc_scaling_w_h(fbc_bytes, params->src_h,
+ params->dst_h, params->src_w, params->dst_w);
+ }
+ prefill_bytes += fbc_bytes;
+
+ pr_debug("ot=%d y_buf=%d pp_lines=%d pp=%d post_sc=%d fbc_bytes=%d\n",
+ prefill->ot_bytes, y_buf_bytes, pp_lines, pp_bytes,
+ post_scaler_bytes, fbc_bytes);
+
+ return prefill_bytes;
+}
+
+static u32 mdss_mdp_perf_calc_pipe_prefill_cmd(struct mdss_mdp_prefill_params
+ *params)
+{
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+ struct mdss_prefill_data *prefill = &mdata->prefill_data;
+ u32 prefill_bytes;
+ u32 ot_bytes = 0;
+ u32 latency_lines, latency_buf_bytes;
+ u32 y_buf_bytes = 0;
+ u32 y_scaler_bytes;
+ u32 fbc_cmd_lines = 0, fbc_cmd_bytes = 0;
+ u32 post_scaler_bytes = 0;
+
+ /* y_scaler_bytes are same for the first or non first line */
+ y_scaler_bytes = mdss_mdp_calc_y_scaler_bytes(params, prefill);
+ prefill_bytes = y_scaler_bytes;
+
+ /* 1st line if fbc is not enabled and 2nd line if fbc is enabled */
+ if (((params->dst_y == 0) && !params->is_fbc) ||
+ ((params->dst_y <= 1) && params->is_fbc)) {
+ if (params->is_bwc || params->is_tile)
+ latency_lines = 4;
+ else if (!params->is_caf && params->is_hflip)
+ latency_lines = 1;
+ else
+ latency_lines = 0;
+ latency_buf_bytes = params->src_w * params->bpp * latency_lines;
+ prefill_bytes += latency_buf_bytes;
+
+ fbc_cmd_lines++;
+ if (params->is_fbc)
+ fbc_cmd_lines++;
+ fbc_cmd_bytes = params->bpp * params->dst_w * fbc_cmd_lines;
+ fbc_cmd_bytes = mdss_mdp_calc_scaling_w_h(fbc_cmd_bytes,
+ params->src_h, params->dst_h, params->src_w,
+ params->dst_w);
+ prefill_bytes += fbc_cmd_bytes;
+ } else {
+ ot_bytes = prefill->ot_bytes;
+ prefill_bytes += ot_bytes;
+
+ latency_buf_bytes = mdss_mdp_calc_latency_buf_bytes(params,
+ prefill);
+ prefill_bytes += latency_buf_bytes;
+
+ if (params->is_yuv)
+ y_buf_bytes = prefill->y_buf_bytes;
+ prefill_bytes += y_buf_bytes;
+
+ post_scaler_bytes = prefill->post_scaler_pixels * params->bpp;
+ post_scaler_bytes = mdss_mdp_calc_scaling_w_h(post_scaler_bytes,
+ params->src_h, params->dst_h, params->src_w,
+ params->dst_w);
+ prefill_bytes += post_scaler_bytes;
+ }
+
+ pr_debug("ot=%d bwc=%d smp=%d y_buf=%d fbc=%d\n", ot_bytes,
+ params->is_bwc, latency_buf_bytes, y_buf_bytes, fbc_cmd_bytes);
+
+ return prefill_bytes;
}
/**
@@ -183,16 +310,22 @@
* (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_perf_params *perf, struct mdss_mdp_img_rect *roi)
{
struct mdss_mdp_mixer *mixer;
int fps = DEFAULT_FRAME_RATE;
- u32 quota, rate, v_total, src_h;
+ u32 quota, rate, v_total, src_h, xres = 0;
+ struct mdss_mdp_img_rect src, dst;
+ bool is_fbc = false;
+ struct mdss_mdp_prefill_params prefill_params;
if (!pipe || !perf || !pipe->mixer)
return -EINVAL;
mixer = pipe->mixer;
+ dst = pipe->dst;
+ src = pipe->src;
+
if (mixer->rotator_mode) {
v_total = pipe->flags & MDP_ROT_90 ? pipe->dst.w : pipe->dst.h;
} else if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF) {
@@ -201,18 +334,31 @@
pinfo = &mixer->ctl->panel_data->panel_info;
fps = mdss_panel_get_framerate(pinfo);
v_total = mdss_panel_get_vtotal(pinfo);
+ xres = pinfo->xres;
+ is_fbc = pinfo->fbc.enabled;
} else {
v_total = mixer->height;
+ xres = mixer->width;
}
+ if (roi)
+ mdss_mdp_crop_rect(&src, &dst, roi);
+
+ pr_debug("v_total=%d, xres=%d fps=%d\n", v_total, xres, fps);
+
/*
* when doing vertical decimation lines will be skipped, hence there is
* no need to account for these lines in MDP clock or request bus
* bandwidth to fetch them.
*/
- src_h = pipe->src.h >> pipe->vert_deci;
+ src_h = src.h >> pipe->vert_deci;
- quota = fps * pipe->src.w * src_h;
+ quota = fps * src.w * src_h;
+
+ pr_debug("src(w,h)(%d,%d) dst(w,h)(%d,%d) dst_y=%d bpp=%d yuv=%d\n",
+ pipe->src.w, src_h, pipe->dst.w, pipe->dst.h, pipe->dst.y,
+ pipe->src_fmt->bpp, pipe->src_fmt->is_yuv);
+
if (pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_420)
/*
* with decimation, chroma is not downsampled, this means we
@@ -225,41 +371,76 @@
else
quota *= pipe->src_fmt->bpp;
- rate = pipe->dst.w;
- if (src_h > pipe->dst.h)
- rate = (rate * src_h) / pipe->dst.h;
+ rate = dst.w;
+ if (src_h > dst.h)
+ rate = (rate * src_h) / dst.h;
rate *= v_total * fps;
if (mixer->rotator_mode) {
rate /= 4; /* block mode fetch at 4 pix/clk */
quota *= 2; /* bus read + write */
- perf->ib_quota = quota;
+ perf->bw_overlap = quota;
} else {
- perf->ib_quota = (quota / pipe->dst.h) * v_total;
+ perf->bw_overlap = (quota / dst.h) * v_total;
}
- perf->ab_quota = quota;
- perf->mdp_clk_rate = rate;
- pr_debug("mixer=%d pnum=%d clk_rate=%u bus ab=%u ib=%u\n",
- mixer->num, pipe->num, rate, perf->ab_quota, perf->ib_quota);
+ perf->mdp_clk_rate = mdss_mdp_clk_fudge_factor(mixer, rate);
+
+ prefill_params.smp_bytes = mdss_mdp_smp_get_size(pipe);
+ prefill_params.xres = xres;
+ prefill_params.src_w = src.w;
+ prefill_params.src_h = src_h;
+ prefill_params.dst_w = dst.w;
+ prefill_params.dst_h = dst.h;
+ prefill_params.dst_y = dst.y;
+ prefill_params.bpp = pipe->src_fmt->bpp;
+ prefill_params.is_yuv = pipe->src_fmt->is_yuv;
+ prefill_params.is_caf = mdss_mdp_perf_is_caf(pipe);
+ prefill_params.is_fbc = is_fbc;
+ prefill_params.is_bwc = pipe->bwc_mode;
+ prefill_params.is_tile = pipe->src_fmt->tile;
+ prefill_params.is_hflip = pipe->flags & MDP_FLIP_LR;
+
+ if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF) {
+ perf->prefill_bytes = (mixer->ctl->is_video_mode) ?
+ mdss_mdp_perf_calc_pipe_prefill_video(&prefill_params) :
+ mdss_mdp_perf_calc_pipe_prefill_cmd(&prefill_params);
+ }
+ else
+ perf->prefill_bytes = 0;
+
+ pr_debug("mixer=%d pnum=%d clk_rate=%u bw_overlap=%llu prefill=%d\n",
+ mixer->num, pipe->num, perf->mdp_clk_rate, perf->bw_overlap,
+ perf->prefill_bytes);
return 0;
}
-static void mdss_mdp_perf_mixer_update(struct mdss_mdp_mixer *mixer,
- u32 *bus_ab_quota, u32 *bus_ib_quota,
- u32 *clk_rate)
+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);
+}
+
+static inline int cmpu32(const void *a, const void *b)
+{
+ return (*(u32 *)a < *(u32 *)b) ? -1 : 0;
+}
+
+static void mdss_mdp_perf_calc_mixer(struct mdss_mdp_mixer *mixer,
+ struct mdss_mdp_perf_params *perf)
{
struct mdss_mdp_pipe *pipe;
struct mdss_panel_info *pinfo = NULL;
int fps = DEFAULT_FRAME_RATE;
- u32 v_total;
+ u32 v_total = 0;
int i;
- u32 max_clk_rate = 0, ab_total = 0, ib_total = 0;
+ u32 max_clk_rate = 0;
+ u64 bw_overlap_max = 0;
+ u64 bw_overlap[MDSS_MDP_MAX_STAGE];
+ u32 v_region[MDSS_MDP_MAX_STAGE * 2];
+ u32 prefill_bytes = 0;
- *bus_ab_quota = 0;
- *bus_ib_quota = 0;
- *clk_rate = 0;
+ memset(perf, 0, sizeof(*perf));
if (!mixer->rotator_mode) {
if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF) {
@@ -272,100 +453,392 @@
} else {
v_total = mixer->height;
}
- *clk_rate = mixer->width * v_total * fps;
- if (pinfo && pinfo->lcdc.v_back_porch < MDP_MIN_VBP)
- *clk_rate = MDSS_MDP_CLK_FUDGE_FACTOR(*clk_rate);
- if (!pinfo) {
- /* perf for bus writeback */
- *bus_ab_quota = fps * mixer->width * mixer->height * 3;
- *bus_ab_quota >>= MDSS_MDP_BUS_FACTOR_SHIFT;
- *bus_ib_quota = *bus_ab_quota;
- }
+ perf->mdp_clk_rate = mixer->width * v_total * fps;
+ perf->mdp_clk_rate =
+ mdss_mdp_clk_fudge_factor(mixer, perf->mdp_clk_rate);
+
+ if (!pinfo) /* perf for bus writeback */
+ perf->bw_overlap =
+ fps * mixer->width * mixer->height * 3;
}
+ memset(bw_overlap, 0, sizeof(u64) * MDSS_MDP_MAX_STAGE);
+ memset(v_region, 0, sizeof(u32) * MDSS_MDP_MAX_STAGE * 2);
+
for (i = 0; i < MDSS_MDP_MAX_STAGE; i++) {
- struct mdss_mdp_perf_params perf;
+ struct mdss_mdp_perf_params tmp;
pipe = mixer->stage_pipe[i];
if (pipe == NULL)
continue;
- if (mdss_mdp_perf_calc_pipe(pipe, &perf))
+ if (mdss_mdp_perf_calc_pipe(pipe, &tmp, &mixer->roi))
continue;
-
- ab_total += perf.ab_quota >> MDSS_MDP_BUS_FACTOR_SHIFT;
- ib_total += perf.ib_quota >> MDSS_MDP_BUS_FACTOR_SHIFT;
- if (perf.mdp_clk_rate > max_clk_rate)
- max_clk_rate = perf.mdp_clk_rate;
+ prefill_bytes += tmp.prefill_bytes;
+ bw_overlap[i] = tmp.bw_overlap;
+ v_region[2*i] = pipe->dst.y;
+ v_region[2*i + 1] = pipe->dst.y + pipe->dst.h;
+ if (tmp.mdp_clk_rate > max_clk_rate)
+ max_clk_rate = tmp.mdp_clk_rate;
}
- *bus_ab_quota += ab_total;
- *bus_ib_quota += ib_total;
- if (max_clk_rate > *clk_rate)
- *clk_rate = max_clk_rate;
+ /*
+ * Sort the v_region array so the total display area can be
+ * divided into individual regions. Check how many pipes fetch
+ * data for each region and sum them up, then the worst case
+ * of all regions is ib request.
+ */
+ sort(v_region, MDSS_MDP_MAX_STAGE * 2, sizeof(u32), cmpu32, NULL);
+ for (i = 1; i < MDSS_MDP_MAX_STAGE * 2; i++) {
+ int j;
+ u64 bw_max_region = 0;
+ u32 y0, y1;
+ 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;
+ y1 = v_region[i];
+ for (j = 0; j < MDSS_MDP_MAX_STAGE; j++) {
+ if (!bw_overlap[j])
+ continue;
+ pipe = mixer->stage_pipe[j];
+ if (mdss_mdp_perf_is_overlap(y0, y1, pipe->dst.y,
+ (pipe->dst.y + pipe->dst.h)))
+ bw_max_region += bw_overlap[j];
+ pr_debug("v[%d](%d,%d)pipe[%d](%d,%d)bw(%llu %llu)\n",
+ i, y0, y1, j, pipe->dst.y,
+ pipe->dst.y + pipe->dst.h, bw_overlap[j],
+ bw_max_region);
+ }
+ bw_overlap_max = max(bw_overlap_max, bw_max_region);
+ }
- pr_debug("final mixer=%d clk_rate=%u bus ab=%u ib=%u\n", mixer->num,
- *clk_rate, *bus_ab_quota, *bus_ib_quota);
+ perf->bw_overlap += bw_overlap_max;
+ perf->prefill_bytes += prefill_bytes;
+
+ if (max_clk_rate > perf->mdp_clk_rate)
+ perf->mdp_clk_rate = max_clk_rate;
+
+ pr_debug("final mixer=%d video=%d clk_rate=%u bw=%llu prefill=%d\n",
+ mixer->num, mixer->ctl->is_video_mode, perf->mdp_clk_rate,
+ perf->bw_overlap, perf->prefill_bytes);
+
}
-static int mdss_mdp_ctl_perf_update(struct mdss_mdp_ctl *ctl)
+static u32 mdss_mdp_get_vbp_factor(struct mdss_mdp_ctl *ctl)
{
- int ret = MDSS_MDP_PERF_UPDATE_SKIP;
- u32 clk_rate, ab_quota, ib_quota;
- u32 max_clk_rate = 0, total_ab_quota = 0, total_ib_quota = 0;
+ u32 fps, v_total, vbp, vbp_fac;
+ struct mdss_panel_info *pinfo;
+
+ if (!ctl || !ctl->panel_data)
+ return 0;
+
+ pinfo = &ctl->panel_data->panel_info;
+ fps = mdss_panel_get_framerate(pinfo);
+ v_total = mdss_panel_get_vtotal(pinfo);
+ vbp = pinfo->lcdc.v_back_porch + pinfo->lcdc.v_pulse_width;
+ vbp_fac = (vbp) ? fps * v_total / vbp : 0;
+ pr_debug("vbp_fac=%d vbp=%d v_total=%d\n", vbp_fac, vbp, v_total);
+
+ return vbp_fac;
+}
+
+static u32 mdss_mdp_get_vbp_factor_max(struct mdss_mdp_ctl *ctl)
+{
+ u32 vbp_max = 0;
+ int i;
+ struct mdss_data_type *mdata;
+
+ if (!ctl || !ctl->mdata)
+ return 0;
+
+ mdata = ctl->mdata;
+ for (i = 0; i < mdata->nctl; i++) {
+ struct mdss_mdp_ctl *ctl = mdata->ctl_off + i;
+ u32 vbp_fac;
+
+ if (ctl->power_on) {
+ vbp_fac = mdss_mdp_get_vbp_factor(ctl);
+ vbp_max = max(vbp_max, vbp_fac);
+ }
+ }
+
+ return vbp_max;
+}
+
+static void mdss_mdp_perf_calc_ctl(struct mdss_mdp_ctl *ctl,
+ struct mdss_mdp_perf_params *perf)
+{
+ struct mdss_mdp_perf_params tmp;
+
+ memset(perf, 0, sizeof(*perf));
if (ctl->mixer_left) {
- mdss_mdp_perf_mixer_update(ctl->mixer_left, &ab_quota,
- &ib_quota, &clk_rate);
- total_ab_quota += ab_quota;
- total_ib_quota += ib_quota;
- max_clk_rate = clk_rate;
+ mdss_mdp_perf_calc_mixer(ctl->mixer_left, &tmp);
+ perf->bw_overlap += tmp.bw_overlap;
+ perf->prefill_bytes += tmp.prefill_bytes;
+ perf->mdp_clk_rate = tmp.mdp_clk_rate;
}
if (ctl->mixer_right) {
- mdss_mdp_perf_mixer_update(ctl->mixer_right, &ab_quota,
- &ib_quota, &clk_rate);
- total_ab_quota += ab_quota;
- total_ib_quota += ib_quota;
- if (clk_rate > max_clk_rate)
- max_clk_rate = clk_rate;
+ mdss_mdp_perf_calc_mixer(ctl->mixer_right, &tmp);
+ perf->bw_overlap += tmp.bw_overlap;
+ perf->prefill_bytes += tmp.prefill_bytes;
+ if (tmp.mdp_clk_rate > perf->mdp_clk_rate)
+ perf->mdp_clk_rate = tmp.mdp_clk_rate;
if (ctl->intf_type) {
- clk_rate = mdss_mdp_get_pclk_rate(ctl);
+ u32 clk_rate = mdss_mdp_get_pclk_rate(ctl);
/* minimum clock rate due to inefficiency in 3dmux */
clk_rate = mult_frac(clk_rate >> 1, 9, 8);
- if (clk_rate > max_clk_rate)
- max_clk_rate = clk_rate;
+ if (clk_rate > perf->mdp_clk_rate)
+ perf->mdp_clk_rate = clk_rate;
}
}
/* request minimum bandwidth to have bus clock on when display is on */
- if (total_ib_quota == 0)
- total_ib_quota = SZ_16M >> MDSS_MDP_BUS_FACTOR_SHIFT;
+ if (perf->bw_overlap == 0)
+ perf->bw_overlap = SZ_16M;
- if (max_clk_rate != ctl->clk_rate) {
- if (max_clk_rate > ctl->clk_rate)
- ret = MDSS_MDP_PERF_UPDATE_EARLY;
- else
- ret = MDSS_MDP_PERF_UPDATE_LATE;
- ctl->clk_rate = max_clk_rate;
- ctl->perf_changed |= MDSS_MDP_PERF_UPDATE_CLK;
+ if (ctl->intf_type != MDSS_MDP_NO_INTF) {
+ u32 vbp_fac = mdss_mdp_get_vbp_factor_max(ctl);
+
+ perf->bw_prefill = perf->prefill_bytes;
+ /*
+ * Prefill bandwidth equals the amount of data (number
+ * of prefill_bytes) divided by the the amount time
+ * available (blanking period). It is equivalent that
+ * prefill bytes times a factor in unit Hz, which is
+ * the reciprocal of time.
+ */
+ perf->bw_prefill *= vbp_fac;
}
- if ((total_ab_quota != ctl->bus_ab_quota) ||
- (total_ib_quota != ctl->bus_ib_quota)) {
- if (ret == MDSS_MDP_PERF_UPDATE_SKIP) {
- if (total_ib_quota >= ctl->bus_ib_quota)
- ret = MDSS_MDP_PERF_UPDATE_EARLY;
- else
- ret = MDSS_MDP_PERF_UPDATE_LATE;
+ perf->bw_ctl = max(perf->bw_prefill, perf->bw_overlap);
+
+ if (ctl->is_video_mode)
+ perf->bw_ctl = IB_FUDGE_FACTOR(perf->bw_ctl);
+
+ 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",
+ perf->bw_overlap, perf->bw_prefill, perf->prefill_bytes);
+}
+
+static bool mdss_mdp_ctl_perf_bw_released(struct mdss_mdp_ctl *ctl)
+{
+ 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;
+}
+
+/**
+ * @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.
+ * @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.
+ */
+void mdss_mdp_ctl_perf_done(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--;
+ if (ctl->perf_status)
+ ctl->perf_status--;
+ }
+ pr_debug("perf_status=%d\n", ctl->perf_status);
+ spin_unlock(&ctl->spin_lock);
+}
+
+static inline void mdss_mdp_ctl_perf_update_bus(struct mdss_mdp_ctl *ctl)
+{
+ u64 bw_sum_of_intfs = 0;
+ u64 bus_ab_quota, bus_ib_quota;
+ struct mdss_data_type *mdata;
+ int i;
+
+ if (!ctl || !ctl->mdata)
+ return;
+
+ mdata = ctl->mdata;
+ for (i = 0; i < mdata->nctl; i++) {
+ struct mdss_mdp_ctl *ctl;
+ ctl = mdata->ctl_off + i;
+ if (ctl->power_on) {
+ bw_sum_of_intfs += ctl->cur_perf.bw_ctl;
+ pr_debug("c=%d bw=%llu\n", ctl->num,
+ ctl->cur_perf.bw_ctl);
}
- ctl->bus_ab_quota = total_ab_quota;
- ctl->bus_ib_quota = total_ib_quota;
- ctl->perf_changed |= MDSS_MDP_PERF_UPDATE_BUS;
+ }
+ bus_ib_quota = bw_sum_of_intfs;
+ bus_ab_quota = AB_FUDGE_FACTOR(bw_sum_of_intfs);
+ 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);
+}
+
+/**
+ * @mdss_mdp_ctl_perf_release_bw() - request zero bandwidth
+ * @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
+ * bandwidth request.
+ */
+void mdss_mdp_ctl_perf_release_bw(struct mdss_mdp_ctl *ctl)
+{
+ unsigned long flags;
+ int need_release = 0;
+ struct mdss_data_type *mdata;
+ int i;
+
+ /* only do this for command panel */
+ if (!ctl || !ctl->mdata || !ctl->panel_data ||
+ (ctl->panel_data->panel_info.type != MIPI_CMD_PANEL))
+ return;
+
+ mutex_lock(&mdss_mdp_ctl_lock);
+ mdata = ctl->mdata;
+ /*
+ * If video interface present, cmd panel bandwidth cannot be
+ * released.
+ */
+ 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;
+ }
}
- return ret;
+ 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);
+}
+
+static void mdss_mdp_ctl_perf_update(struct mdss_mdp_ctl *ctl,
+ int params_changed)
+{
+ struct mdss_mdp_perf_params *new, *old;
+ int update_bus = 0, update_clk = 0;
+ struct mdss_data_type *mdata;
+
+ if (!ctl || !ctl->mdata)
+ return;
+
+ mutex_lock(&mdss_mdp_ctl_lock);
+
+ mdata = ctl->mdata;
+ old = &ctl->cur_perf;
+ new = &ctl->new_perf;
+
+ if (ctl->power_on) {
+ if (params_changed || mdss_mdp_ctl_perf_bw_released(ctl))
+ mdss_mdp_perf_calc_ctl(ctl, new);
+ /*
+ * if params have just changed delay the update until
+ * later once the hw configuration has been flushed to
+ * MDP
+ */
+ if ((params_changed && (new->bw_ctl > old->bw_ctl)) ||
+ (!params_changed && (new->bw_ctl < old->bw_ctl))) {
+ pr_debug("c=%d p=%d new_bw=%llu,old_bw=%llu\n",
+ ctl->num, params_changed, new->bw_ctl,
+ old->bw_ctl);
+ old->bw_ctl = new->bw_ctl;
+ update_bus = 1;
+ }
+
+ if ((params_changed && (new->mdp_clk_rate > old->mdp_clk_rate))
+ || (!params_changed && (new->mdp_clk_rate <
+ old->mdp_clk_rate))) {
+ old->mdp_clk_rate = new->mdp_clk_rate;
+ update_clk = 1;
+ }
+ } else {
+ memset(old, 0, sizeof(old));
+ memset(new, 0, sizeof(new));
+ update_bus = 1;
+ update_clk = 1;
+ }
+
+ if (update_bus)
+ mdss_mdp_ctl_perf_update_bus(ctl);
+
+ if (update_clk) {
+ u32 clk_rate = 0;
+ int i;
+
+ for (i = 0; i < mdata->nctl; i++) {
+ struct mdss_mdp_ctl *ctl;
+ ctl = mdata->ctl_off + i;
+ if (ctl->power_on)
+ clk_rate = max(ctl->cur_perf.mdp_clk_rate,
+ clk_rate);
+ }
+ mdss_mdp_set_clk_rate(clk_rate);
+ pr_debug("update clk rate = %d HZ\n", clk_rate);
+ }
+
+ mutex_unlock(&mdss_mdp_ctl_lock);
}
static struct mdss_mdp_ctl *mdss_mdp_ctl_alloc(struct mdss_data_type *mdata,
@@ -385,6 +858,7 @@
ctl->ref_cnt++;
ctl->mdata = mdata;
mutex_init(&ctl->lock);
+ spin_lock_init(&ctl->spin_lock);
BLOCKING_INIT_NOTIFIER_HEAD(&ctl->notifier_head);
pr_debug("alloc ctl_num=%d\n", ctl->num);
break;
@@ -606,7 +1080,7 @@
mdss_mdp_ctl_free(ctl);
- mdss_mdp_ctl_perf_commit(ctl->mdata, MDSS_MDP_PERF_UPDATE_ALL);
+ mdss_mdp_ctl_perf_update(ctl, 0);
return 0;
}
@@ -1142,9 +1616,7 @@
if (!handoff)
ctl->power_on = true;
- ctl->bus_ab_quota = 0;
- ctl->bus_ib_quota = 0;
- ctl->clk_rate = 0;
+ memset(&ctl->cur_perf, 0, sizeof(ctl->cur_perf));
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
@@ -1233,8 +1705,7 @@
ctl->power_on = false;
ctl->play_cnt = 0;
- ctl->clk_rate = 0;
- mdss_mdp_ctl_perf_commit(ctl->mdata, MDSS_MDP_PERF_UPDATE_ALL);
+ mdss_mdp_ctl_perf_update(ctl, 0);
}
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
@@ -1729,9 +2200,12 @@
int mdss_mdp_ctl_update_fps(struct mdss_mdp_ctl *ctl, int fps)
{
int ret = 0;
+ struct mdss_mdp_ctl *sctl = NULL;
+
+ sctl = mdss_mdp_get_split_ctl(ctl);
if (ctl->config_fps_fnc)
- ret = ctl->config_fps_fnc(ctl, fps);
+ ret = ctl->config_fps_fnc(ctl, sctl, fps);
return ret;
}
@@ -1822,10 +2296,7 @@
if (ctl->wait_fnc)
ret = ctl->wait_fnc(ctl, NULL);
- if (ctl->perf_changed) {
- mdss_mdp_ctl_perf_commit(ctl->mdata, ctl->perf_changed);
- ctl->perf_changed = 0;
- }
+ mdss_mdp_ctl_perf_update(ctl, 0);
mutex_unlock(&ctl->lock);
@@ -1858,7 +2329,6 @@
struct mdss_mdp_ctl *sctl = NULL;
int mixer1_changed, mixer2_changed;
int ret = 0;
- int perf_update = MDSS_MDP_PERF_UPDATE_SKIP;
if (!ctl) {
pr_err("display function not set\n");
@@ -1879,10 +2349,9 @@
mixer2_changed = (ctl->mixer_right && ctl->mixer_right->params_changed);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+
if (mixer1_changed || mixer2_changed
|| ctl->force_screen_state) {
- perf_update = mdss_mdp_ctl_perf_update(ctl);
-
if (ctl->prepare_fnc)
ret = ctl->prepare_fnc(ctl, arg);
if (ret) {
@@ -1890,10 +2359,7 @@
goto done;
}
- if (perf_update == MDSS_MDP_PERF_UPDATE_EARLY) {
- mdss_mdp_ctl_perf_commit(ctl->mdata, ctl->perf_changed);
- ctl->perf_changed = 0;
- }
+ mdss_mdp_ctl_perf_update(ctl, 1);
if (mixer1_changed)
mdss_mdp_mixer_update(ctl->mixer_left);
diff --git a/drivers/video/msm/mdss/mdss_mdp_formats.h b/drivers/video/msm/mdss/mdss_mdp_formats.h
index a2edf90..dcbff88 100644
--- a/drivers/video/msm/mdss/mdss_mdp_formats.h
+++ b/drivers/video/msm/mdss/mdss_mdp_formats.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
@@ -81,6 +81,25 @@
}, \
}
+#define FMT_RGB_8888_TILE(fmt, alpha_en, e0, e1, e2, e3) \
+ { \
+ .format = (fmt), \
+ .fetch_planes = MDSS_MDP_PLANE_INTERLEAVED, \
+ .unpack_tight = 1, \
+ .unpack_align_msb = 0, \
+ .alpha_enable = (alpha_en), \
+ .unpack_count = 4, \
+ .bpp = 4, \
+ .tile = 1, \
+ .element = { (e0), (e1), (e2), (e3) }, \
+ .bits = { \
+ [C3_ALPHA] = COLOR_8BIT, \
+ [C2_R_Cr] = COLOR_8BIT, \
+ [C0_G_Y] = COLOR_8BIT, \
+ [C1_B_Cb] = COLOR_8BIT, \
+ }, \
+ }
+
#define FMT_YUV_COMMON(fmt) \
.format = (fmt), \
.is_yuv = 1, \
@@ -125,6 +144,22 @@
FMT_RGB_8888(MDP_RGBX_8888, 0, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA),
FMT_RGB_8888(MDP_BGRA_8888, 1, C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA),
FMT_RGB_8888(MDP_BGRX_8888, 0, C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA),
+ FMT_RGB_8888_TILE(MDP_RGBA_8888_TILE, 1, C2_R_Cr, C0_G_Y, C1_B_Cb,
+ C3_ALPHA),
+ FMT_RGB_8888_TILE(MDP_ARGB_8888_TILE, 1, C3_ALPHA, C2_R_Cr, C0_G_Y,
+ C1_B_Cb),
+ FMT_RGB_8888_TILE(MDP_ABGR_8888_TILE, 1, C3_ALPHA, C1_B_Cb, C0_G_Y,
+ C2_R_Cr),
+ FMT_RGB_8888_TILE(MDP_BGRA_8888_TILE, 1, C1_B_Cb, C0_G_Y, C2_R_Cr,
+ C3_ALPHA),
+ FMT_RGB_8888_TILE(MDP_RGBX_8888_TILE, 0, C2_R_Cr, C0_G_Y, C1_B_Cb,
+ C3_ALPHA),
+ FMT_RGB_8888_TILE(MDP_XRGB_8888_TILE, 0, C3_ALPHA, C2_R_Cr, C0_G_Y,
+ C1_B_Cb),
+ FMT_RGB_8888_TILE(MDP_XBGR_8888_TILE, 0, C3_ALPHA, C1_B_Cb, C0_G_Y,
+ C2_R_Cr),
+ FMT_RGB_8888_TILE(MDP_BGRX_8888_TILE, 0, C1_B_Cb, C0_G_Y, C2_R_Cr,
+ C3_ALPHA),
FMT_YUV_PSEUDO(MDP_Y_CRCB_H1V1, MDSS_MDP_CHROMA_RGB, C2_R_Cr, C1_B_Cb),
FMT_YUV_PSEUDO(MDP_Y_CBCR_H1V1, MDSS_MDP_CHROMA_RGB, C1_B_Cb, C2_R_Cr),
diff --git a/drivers/video/msm/mdss/mdss_mdp_hwio.h b/drivers/video/msm/mdss/mdss_mdp_hwio.h
index 9948738..bff56d2 100644
--- a/drivers/video/msm/mdss/mdss_mdp_hwio.h
+++ b/drivers/video/msm/mdss/mdss_mdp_hwio.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,8 @@
#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
#define MDSS_REG_HW_INTR_STATUS 0x10
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c b/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c
index ae45587..ce0b757 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c
@@ -247,6 +247,8 @@
return;
}
+ mdss_mdp_ctl_perf_taken(ctl);
+
vsync_time = ktime_get();
ctl->vsync_cnt++;
@@ -308,6 +310,8 @@
return;
}
+ mdss_mdp_ctl_perf_done(ctl);
+
spin_lock(&ctx->clk_lock);
list_for_each_entry(tmp, &ctx->vsync_handlers, list) {
if (tmp->enabled && tmp->cmd_post_flush)
@@ -340,9 +344,12 @@
struct mdss_mdp_cmd_ctx *ctx =
container_of(work, typeof(*ctx), pp_done_work);
- if (ctx->ctl)
+ if (ctx->ctl) {
while (atomic_add_unless(&ctx->pp_done_cnt, -1, 0))
mdss_mdp_ctl_notify(ctx->ctl, MDP_NOTIFY_FRAME_DONE);
+
+ mdss_mdp_ctl_perf_release_bw(ctx->ctl);
+ }
}
static void clk_ctrl_work(struct work_struct *work)
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_video.c b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
index f8c59d7..7304694 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_video.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_video.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
@@ -466,7 +466,63 @@
ctl->underrun_cnt);
}
-static int mdss_mdp_video_config_fps(struct mdss_mdp_ctl *ctl, int new_fps)
+static int mdss_mdp_video_vfp_fps_update(struct mdss_mdp_ctl *ctl, int new_fps)
+{
+ int curr_fps;
+ u32 add_v_lines = 0;
+ u32 current_vsync_period_f0, new_vsync_period_f0;
+ struct mdss_panel_data *pdata;
+ struct mdss_mdp_video_ctx *ctx;
+ u32 vsync_period, hsync_period;
+
+ ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data;
+ if (!ctx) {
+ pr_err("invalid ctx\n");
+ return -ENODEV;
+ }
+
+ pdata = ctl->panel_data;
+ if (pdata == NULL) {
+ pr_err("%s: Invalid panel data\n", __func__);
+ return -EINVAL;
+ }
+
+ vsync_period = mdss_panel_get_vtotal(&pdata->panel_info);
+ hsync_period = mdss_panel_get_htotal(&pdata->panel_info);
+ curr_fps = mdss_panel_get_framerate(&pdata->panel_info);
+
+ if (curr_fps > new_fps) {
+ add_v_lines = mult_frac(vsync_period,
+ (curr_fps - new_fps), new_fps);
+ pdata->panel_info.lcdc.v_front_porch += add_v_lines;
+ } else {
+ add_v_lines = mult_frac(vsync_period,
+ (new_fps - curr_fps), new_fps);
+ pdata->panel_info.lcdc.v_front_porch -= add_v_lines;
+ }
+
+ vsync_period = mdss_panel_get_vtotal(&pdata->panel_info);
+ current_vsync_period_f0 = mdp_video_read(ctx,
+ MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0);
+ new_vsync_period_f0 = (vsync_period * hsync_period);
+
+ mdp_video_write(ctx, MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0,
+ current_vsync_period_f0 | 0x800000);
+ if (new_vsync_period_f0 & 0x800000) {
+ mdp_video_write(ctx, MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0,
+ new_vsync_period_f0);
+ } else {
+ mdp_video_write(ctx, MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0,
+ new_vsync_period_f0 | 0x800000);
+ mdp_video_write(ctx, MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0,
+ new_vsync_period_f0 & 0x7fffff);
+ }
+
+ return 0;
+}
+
+static int mdss_mdp_video_config_fps(struct mdss_mdp_ctl *ctl,
+ struct mdss_mdp_ctl *sctl, int new_fps)
{
struct mdss_mdp_video_ctx *ctx;
struct mdss_panel_data *pdata;
@@ -529,6 +585,40 @@
ctl->force_screen_state = MDSS_SCREEN_DEFAULT;
mdss_mdp_display_commit(ctl, NULL);
mdss_mdp_display_wait4comp(ctl);
+ } else if (pdata->panel_info.dfps_update
+ == DFPS_IMMEDIATE_PORCH_UPDATE_MODE){
+ if (!ctx->timegen_en) {
+ pr_err("TG is OFF. DFPS mode invalid\n");
+ return -EINVAL;
+ }
+
+ video_vsync_irq_enable(ctl, true);
+ INIT_COMPLETION(ctx->vsync_comp);
+ rc = wait_for_completion_timeout(&ctx->vsync_comp,
+ usecs_to_jiffies(VSYNC_TIMEOUT_US));
+ WARN(rc <= 0, "timeout (%d) vsync interrupt on ctl=%d\n",
+ rc, ctl->num);
+ rc = 0;
+ video_vsync_irq_disable(ctl);
+
+ rc = mdss_mdp_video_vfp_fps_update(ctl, new_fps);
+ if (rc < 0) {
+ pr_err("%s: Error during DFPS\n", __func__);
+ return rc;
+ }
+ if (sctl) {
+ rc = mdss_mdp_video_vfp_fps_update(sctl,
+ new_fps);
+ if (rc < 0) {
+ pr_err("%s: DFPS error\n", __func__);
+ return rc;
+ }
+ }
+ rc = mdss_mdp_ctl_intf_event(ctl,
+ MDSS_EVENT_PANEL_UPDATE_FPS,
+ (void *)new_fps);
+ WARN(rc, "intf %d panel fps update error (%d)\n",
+ ctl->intf_num, rc);
} else {
pr_err("intf %d panel, unknown FPS mode\n",
ctl->intf_num);
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c b/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
index ff55c57..27a7707 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
@@ -17,6 +17,9 @@
#include "mdss_mdp_rotator.h"
#include "mdss_panel.h"
+#define VBIF_WR_LIM_CONF 0xC0
+#define MDSS_DEFAULT_OT_SETTING 0x10
+
enum mdss_mdp_writeback_type {
MDSS_MDP_WRITEBACK_TYPE_ROTATOR,
MDSS_MDP_WRITEBACK_TYPE_LINE,
@@ -34,6 +37,9 @@
u32 intr_type;
u32 intf_num;
+ u32 xin_id;
+ u32 wr_lim;
+
u32 opmode;
struct mdss_mdp_format_params *dst_fmt;
u16 width;
@@ -55,26 +61,31 @@
.type = MDSS_MDP_WRITEBACK_TYPE_ROTATOR,
.intr_type = MDSS_MDP_IRQ_WB_ROT_COMP,
.intf_num = 0,
+ .xin_id = 3,
},
{
.type = MDSS_MDP_WRITEBACK_TYPE_ROTATOR,
.intr_type = MDSS_MDP_IRQ_WB_ROT_COMP,
.intf_num = 1,
+ .xin_id = 11,
},
{
.type = MDSS_MDP_WRITEBACK_TYPE_LINE,
.intr_type = MDSS_MDP_IRQ_WB_ROT_COMP,
.intf_num = 0,
+ .xin_id = 3,
},
{
.type = MDSS_MDP_WRITEBACK_TYPE_LINE,
.intr_type = MDSS_MDP_IRQ_WB_ROT_COMP,
.intf_num = 1,
+ .xin_id = 11,
},
{
.type = MDSS_MDP_WRITEBACK_TYPE_WFD,
.intr_type = MDSS_MDP_IRQ_WB_WFD,
.intf_num = 0,
+ .xin_id = 6,
},
};
@@ -438,9 +449,12 @@
{
struct mdss_mdp_writeback_ctx *ctx;
struct mdss_mdp_writeback_arg *wb_args;
- u32 flush_bits;
+ u32 flush_bits, val, off;
int ret;
+ if (!ctl || !ctl->mdata)
+ return -ENODEV;
+
ctx = (struct mdss_mdp_writeback_ctx *) ctl->priv_data;
if (!ctx)
return -ENODEV;
@@ -451,6 +465,18 @@
return -EPERM;
}
+ if (ctl->mdata->rotator_ot_limit) {
+ if (ctx->type == MDSS_MDP_WRITEBACK_TYPE_ROTATOR)
+ ctx->wr_lim = ctl->mdata->rotator_ot_limit;
+ else
+ ctx->wr_lim = MDSS_DEFAULT_OT_SETTING;
+ off = (ctx->xin_id % 4) * 8;
+ val = readl_relaxed(ctl->mdata->vbif_base + VBIF_WR_LIM_CONF);
+ val &= ~(0xFF << off);
+ val |= (ctx->wr_lim) << off;
+ writel_relaxed(val, ctl->mdata->vbif_base + VBIF_WR_LIM_CONF);
+ }
+
wb_args = (struct mdss_mdp_writeback_arg *) arg;
if (!wb_args)
return -ENOENT;
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index 2ba231d..9365be8 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -23,6 +23,7 @@
#include <linux/delay.h>
#include <linux/msm_mdp.h>
#include <linux/memblock.h>
+#include <linux/sw_sync.h>
#include <mach/iommu_domains.h>
#include <mach/event_timer.h>
@@ -261,7 +262,7 @@
int rc;
for (;;) {
- rc = mdss_mdp_perf_calc_pipe(pipe, &perf);
+ rc = mdss_mdp_perf_calc_pipe(pipe, &perf, NULL);
if (!rc && (perf.mdp_clk_rate <= mdata->max_mdp_clk_rate))
break;
@@ -2703,6 +2704,107 @@
return rc;
}
+static void __vsync_retire_handle_vsync(struct mdss_mdp_ctl *ctl, ktime_t t)
+{
+ struct msm_fb_data_type *mfd = ctl->mfd;
+ struct mdss_overlay_private *mdp5_data;
+
+ if (!mfd || !mfd->mdp.private1) {
+ pr_warn("Invalid handle for vsync\n");
+ return;
+ }
+
+ mdp5_data = mfd_to_mdp5_data(mfd);
+ schedule_work(&mdp5_data->retire_work);
+}
+
+static void __vsync_retire_work_handler(struct work_struct *work)
+{
+ struct mdss_overlay_private *mdp5_data =
+ container_of(work, typeof(*mdp5_data), retire_work);
+ struct msm_sync_pt_data *sync_pt_data;
+
+ if (!mdp5_data->ctl || !mdp5_data->ctl->mfd)
+ return;
+
+ if (!mdp5_data->ctl->remove_vsync_handler)
+ return;
+
+ sync_pt_data = &mdp5_data->ctl->mfd->mdp_sync_pt_data;
+ mutex_lock(&sync_pt_data->sync_mutex);
+ if (mdp5_data->retire_cnt > 0) {
+ sw_sync_timeline_inc(mdp5_data->vsync_timeline, 1);
+
+ mdp5_data->retire_cnt--;
+ if (mdp5_data->retire_cnt == 0) {
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ mdp5_data->ctl->remove_vsync_handler(mdp5_data->ctl,
+ &mdp5_data->vsync_retire_handler);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+ }
+ }
+ mutex_unlock(&sync_pt_data->sync_mutex);
+}
+
+static struct sync_fence *
+__vsync_retire_get_fence(struct msm_sync_pt_data *sync_pt_data)
+{
+ struct msm_fb_data_type *mfd;
+ struct mdss_overlay_private *mdp5_data;
+ struct mdss_mdp_ctl *ctl;
+ int rc, value;
+
+ mfd = container_of(sync_pt_data, typeof(*mfd), mdp_sync_pt_data);
+ mdp5_data = mfd_to_mdp5_data(mfd);
+
+ if (!mdp5_data || !mdp5_data->ctl)
+ return ERR_PTR(-ENODEV);
+
+ ctl = mdp5_data->ctl;
+ if (!ctl->add_vsync_handler)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ if (!ctl->power_on) {
+ pr_debug("fb%d vsync pending first update\n", mfd->index);
+ return ERR_PTR(-EPERM);
+ }
+
+ if (mdp5_data->retire_cnt == 0) {
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ rc = ctl->add_vsync_handler(ctl,
+ &mdp5_data->vsync_retire_handler);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+ if (IS_ERR_VALUE(rc))
+ return ERR_PTR(rc);
+ }
+ value = mdp5_data->vsync_timeline->value + 1 + mdp5_data->retire_cnt;
+ mdp5_data->retire_cnt++;
+
+ return mdss_fb_sync_get_fence(mdp5_data->vsync_timeline,
+ "mdp-retire", value);
+}
+
+static int __vsync_retire_setup(struct msm_fb_data_type *mfd)
+{
+ struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+ char name[24];
+
+ snprintf(name, sizeof(name), "mdss_fb%d_retire", mfd->index);
+ mdp5_data->vsync_timeline = sw_sync_timeline_create(name);
+ if (mdp5_data->vsync_timeline == NULL) {
+ pr_err("cannot vsync create time line");
+ return -ENOMEM;
+ }
+ mfd->mdp_sync_pt_data.get_retire_fence = __vsync_retire_get_fence;
+
+ mdp5_data->vsync_retire_handler.vsync_handler =
+ __vsync_retire_handle_vsync;
+ mdp5_data->vsync_retire_handler.cmd_post_flush = false;
+ INIT_WORK(&mdp5_data->retire_work, __vsync_retire_work_handler);
+
+ return 0;
+}
+
int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd)
{
struct device *dev = mfd->fbi->dev;
@@ -2769,6 +2871,12 @@
pr_err("Error dfps sysfs creation ret=%d\n", rc);
goto init_fail;
}
+ } else if (mfd->panel_info->type == MIPI_CMD_PANEL) {
+ rc = __vsync_retire_setup(mfd);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("unable to create vsync timeline\n");
+ goto init_fail;
+ }
}
mfd->mdp_sync_pt_data.async_wait_fences = true;
rc = sysfs_create_link_nowarn(&dev->kobj,
diff --git a/drivers/video/msm/mdss/mdss_mdp_pipe.c b/drivers/video/msm/mdss/mdss_mdp_pipe.c
index b7553a9..4999103 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pipe.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pipe.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
@@ -129,6 +129,26 @@
}
}
+/**
+ * @mdss_mdp_smp_get_size - get allocated smp size for a pipe
+ * @pipe: pointer to a pipe
+ *
+ * Function counts number of blocks that are currently allocated for a
+ * pipe, then smp buffer size is number of blocks multiplied by block
+ * size.
+ */
+u32 mdss_mdp_smp_get_size(struct mdss_mdp_pipe *pipe)
+{
+ int i, mb_cnt = 0;
+
+ for (i = 0; i < MAX_PLANES; i++) {
+ mb_cnt += bitmap_weight(pipe->smp_map[i].allocated, SMP_MB_CNT);
+ mb_cnt += bitmap_weight(pipe->smp_map[i].fixed, SMP_MB_CNT);
+ }
+
+ return mb_cnt * SMP_MB_SIZE;
+}
+
static void mdss_mdp_smp_set_wm_levels(struct mdss_mdp_pipe *pipe, int mb_cnt)
{
u32 fetch_size, val, wm[3];
@@ -270,7 +290,10 @@
}
}
- nlines = pipe->bwc_mode ? 1 : 2;
+ if (pipe->src_fmt->tile)
+ nlines = 8;
+ else
+ nlines = pipe->bwc_mode ? 1 : 2;
mutex_lock(&mdss_mdp_smp_lock);
for (i = (MAX_PLANES - 1); i >= ps.num_planes; i--) {
@@ -798,25 +821,7 @@
return rc;
}
-void mdss_mdp_crop_rect(struct mdss_mdp_img_rect *src_rect,
- struct mdss_mdp_img_rect *dst_rect,
- const struct mdss_mdp_img_rect *sci_rect)
-{
- struct mdss_mdp_img_rect res;
- mdss_mdp_intersect_rect(&res, dst_rect, sci_rect);
- if (res.w && res.h) {
- if ((res.w != dst_rect->w) || (res.h != dst_rect->h)) {
- src_rect->x = src_rect->x + (res.x - dst_rect->x);
- src_rect->y = src_rect->y + (res.y - dst_rect->y);
- src_rect->w = res.w;
- src_rect->h = res.h;
- }
- *dst_rect = (struct mdss_mdp_img_rect)
- {(res.x - sci_rect->x), (res.y - sci_rect->y),
- res.w, res.h};
- }
-}
static int mdss_mdp_image_setup(struct mdss_mdp_pipe *pipe,
struct mdss_mdp_data *data)
@@ -919,6 +924,7 @@
u32 chroma_samp, unpack, src_format;
u32 secure = 0;
u32 opmode;
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
fmt = pipe->src_fmt;
@@ -949,6 +955,9 @@
(fmt->bits[C1_B_Cb] << 2) |
(fmt->bits[C0_G_Y] << 0);
+ if (fmt->tile)
+ src_format |= BIT(30);
+
if (pipe->flags & MDP_ROT_90)
src_format |= BIT(11); /* ROT90 */
@@ -968,6 +977,11 @@
if (pipe->scale.enable_pxl_ext)
opmode |= (1 << 31);
+ if (fmt->tile && mdata->highest_bank_bit) {
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_FETCH_CONFIG,
+ MDSS_MDP_FETCH_CONFIG_RESET_VALUE |
+ mdata->highest_bank_bit << 18);
+ }
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_FORMAT, src_format);
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN, unpack);
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_OP_MODE, opmode);
diff --git a/drivers/video/msm/mdss/mdss_mdp_util.c b/drivers/video/msm/mdss/mdss_mdp_util.c
index 79873b6..69506d4 100644
--- a/drivers/video/msm/mdss/mdss_mdp_util.c
+++ b/drivers/video/msm/mdss/mdss_mdp_util.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
@@ -257,6 +257,27 @@
else
*res_rect = (struct mdss_mdp_img_rect){l, t, (r-l), (b-t)};
}
+
+void mdss_mdp_crop_rect(struct mdss_mdp_img_rect *src_rect,
+ struct mdss_mdp_img_rect *dst_rect,
+ const struct mdss_mdp_img_rect *sci_rect)
+{
+ struct mdss_mdp_img_rect res;
+ mdss_mdp_intersect_rect(&res, dst_rect, sci_rect);
+
+ if (res.w && res.h) {
+ if ((res.w != dst_rect->w) || (res.h != dst_rect->h)) {
+ src_rect->x = src_rect->x + (res.x - dst_rect->x);
+ src_rect->y = src_rect->y + (res.y - dst_rect->y);
+ src_rect->w = res.w;
+ src_rect->h = res.h;
+ }
+ *dst_rect = (struct mdss_mdp_img_rect)
+ {(res.x - sci_rect->x), (res.y - sci_rect->y),
+ res.w, res.h};
+ }
+}
+
int mdss_mdp_get_rau_strides(u32 w, u32 h,
struct mdss_mdp_format_params *fmt,
struct mdss_mdp_plane_sizes *ps)
diff --git a/drivers/video/msm/mdss/mdss_mdp_wb.c b/drivers/video/msm/mdss/mdss_mdp_wb.c
index 761b85a..6eb4d6e 100644
--- a/drivers/video/msm/mdss/mdss_mdp_wb.c
+++ b/drivers/video/msm/mdss/mdss_mdp_wb.c
@@ -61,11 +61,14 @@
struct msmfb_data buf_info;
struct mdss_mdp_data buf_data;
int state;
+ bool user_alloc;
};
static DEFINE_MUTEX(mdss_mdp_wb_buf_lock);
static struct mdss_mdp_wb mdss_mdp_wb_info;
+static void mdss_mdp_wb_free_node(struct mdss_mdp_wb_data *node);
+
#ifdef DEBUG_WRITEBACK
/* for debugging: writeback output buffer to allocated memory */
static inline
@@ -285,6 +288,7 @@
struct mdss_mdp_wb_data *node, *temp;
list_for_each_entry_safe(node, temp, &wb->register_queue,
registered_entry) {
+ mdss_mdp_wb_free_node(node);
list_del(&node->registered_entry);
kfree(node);
}
@@ -401,12 +405,24 @@
struct mdss_mdp_img_data *buf;
int ret;
+ if (!list_empty(&wb->register_queue)) {
+ list_for_each_entry(node, &wb->register_queue, registered_entry)
+ if ((node->buf_info.memory_id == data->memory_id) &&
+ (node->buf_info.offset == data->offset)) {
+ pr_debug("found node fd=%x off=%x addr=%x\n",
+ data->memory_id, data->offset,
+ node->buf_data.p[0].addr);
+ return node;
+ }
+ }
+
node = kzalloc(sizeof(struct mdss_mdp_wb_data), GFP_KERNEL);
if (node == NULL) {
pr_err("out of memory\n");
return NULL;
}
+ node->user_alloc = true;
node->buf_data.num_planes = 1;
buf = &node->buf_data.p[0];
if (wb->is_secure)
@@ -434,6 +450,22 @@
return NULL;
}
+static void mdss_mdp_wb_free_node(struct mdss_mdp_wb_data *node)
+{
+ struct mdss_mdp_img_data *buf;
+
+ if (node->user_alloc) {
+ buf = &node->buf_data.p[0];
+ pr_debug("free user node mem_id=%d offset=%u addr=0x%x\n",
+ node->buf_info.memory_id,
+ node->buf_info.offset,
+ buf->addr);
+
+ mdss_mdp_put_img(&node->buf_data.p[0]);
+ node->user_alloc = false;
+ }
+}
+
static int mdss_mdp_wb_queue(struct msm_fb_data_type *mfd,
struct msmfb_data *data, int local)
{
diff --git a/drivers/video/msm/mdss/mdss_panel.h b/drivers/video/msm/mdss/mdss_panel.h
index 5d8e03b..274c523 100644
--- a/drivers/video/msm/mdss/mdss_panel.h
+++ b/drivers/video/msm/mdss/mdss_panel.h
@@ -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
@@ -234,6 +234,7 @@
enum dynamic_fps_update {
DFPS_SUSPEND_RESUME_MODE,
DFPS_IMMEDIATE_CLK_UPDATE_MODE,
+ DFPS_IMMEDIATE_PORCH_UPDATE_MODE,
};
enum lvds_mode {
diff --git a/fs/fat/cache.c b/fs/fat/cache.c
index 1cc7038..bab81ca 100644
--- a/fs/fat/cache.c
+++ b/fs/fat/cache.c
@@ -294,8 +294,9 @@
if (ret < 0)
return ret;
else if (ret == FAT_ENT_EOF) {
- fat_fs_error(sb, "%s: request beyond EOF (i_pos %lld)",
- __func__, MSDOS_I(inode)->i_pos);
+ fat_fs_error_ratelimit(sb,
+ "%s: request beyond EOF (i_pos %lld)",
+ __func__, MSDOS_I(inode)->i_pos);
return -EIO;
}
return dclus;
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 4640d3b..6d18d0f 100755
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -146,6 +146,7 @@
header-y += genetlink.h
header-y += gfs2_ondisk.h
header-y += gigaset_dev.h
+header-y += hbtp_input.h
header-y += hdlc.h
header-y += hdlcdrv.h
header-y += hdreg.h
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index 130d0fd..ac750ea 100644
--- a/include/linux/blk_types.h
+++ b/include/linux/blk_types.h
@@ -176,6 +176,8 @@
REQ_SANITIZE)
#define REQ_CLONE_MASK REQ_COMMON_MASK
+#define MMC_REQ_NOREINSERT_MASK (REQ_URGENT | REQ_FUA | REQ_FLUSH)
+
#define REQ_RAHEAD (1 << __REQ_RAHEAD)
#define REQ_THROTTLED (1 << __REQ_THROTTLED)
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index d525e84..f78d418 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -119,10 +119,10 @@
/* This needs to be modified manually now, when we add
a new RANGE of SSIDs to the msg_mask_tbl */
#define MSG_MASK_TBL_CNT 24
-#define EVENT_LAST_ID 0x09CB
+#define EVENT_LAST_ID 0x09F6
#define MSG_SSID_0 0
-#define MSG_SSID_0_LAST 97
+#define MSG_SSID_0_LAST 101
#define MSG_SSID_1 500
#define MSG_SSID_1_LAST 506
#define MSG_SSID_2 1000
@@ -138,7 +138,7 @@
#define MSG_SSID_7 4600
#define MSG_SSID_7_LAST 4614
#define MSG_SSID_8 5000
-#define MSG_SSID_8_LAST 5030
+#define MSG_SSID_8_LAST 5031
#define MSG_SSID_9 5500
#define MSG_SSID_9_LAST 5516
#define MSG_SSID_10 6000
@@ -154,7 +154,7 @@
#define MSG_SSID_15 8000
#define MSG_SSID_15_LAST 8000
#define MSG_SSID_16 8500
-#define MSG_SSID_16_LAST 8523
+#define MSG_SSID_16_LAST 8524
#define MSG_SSID_17 9000
#define MSG_SSID_17_LAST 9008
#define MSG_SSID_18 9500
@@ -166,7 +166,7 @@
#define MSG_SSID_21 10300
#define MSG_SSID_21_LAST 10300
#define MSG_SSID_22 10350
-#define MSG_SSID_22_LAST 10374
+#define MSG_SSID_22_LAST 10377
#define MSG_SSID_23 0xC000
#define MSG_SSID_23_LAST 0xC063
@@ -182,6 +182,7 @@
MSG_LVL_LOW,
MSG_LVL_ERROR,
MSG_LVL_LOW,
+ MSG_LVL_LOW,
MSG_LVL_MED,
MSG_LVL_MED,
MSG_LVL_HIGH,
@@ -280,7 +281,7 @@
MSG_LVL_LOW,
MSG_LVL_LOW,
MSG_LVL_LOW|MSG_LVL_MED|MSG_LVL_HIGH|MSG_LVL_ERROR|MSG_LVL_FATAL,
- MSG_LVL_MED,
+ MSG_LVL_LOW,
MSG_LVL_LOW|MSG_LVL_MED|MSG_LVL_HIGH|MSG_LVL_ERROR|MSG_LVL_FATAL,
MSG_LVL_LOW,
MSG_LVL_MED,
@@ -292,6 +293,10 @@
MSG_LVL_LOW,
MSG_LVL_LOW|MSG_LVL_MED|MSG_LVL_HIGH|MSG_LVL_ERROR|MSG_LVL_FATAL,
MSG_LVL_MED,
+ MSG_LVL_HIGH,
+ MSG_LVL_LOW,
+ MSG_LVL_HIGH,
+ MSG_LVL_HIGH
};
static const uint32_t msg_bld_masks_1[] = {
@@ -321,7 +326,8 @@
MSG_LVL_MED,
MSG_LVL_MED,
MSG_LVL_MED,
- MSG_LVL_MED,
+ MSG_LVL_MED|MSG_MASK_5|MSG_MASK_6|MSG_MASK_7|
+ MSG_MASK_8|MSG_MASK_9|MSG_MASK_10,
MSG_LVL_MED,
MSG_LVL_MED,
MSG_LVL_MED
@@ -439,6 +445,7 @@
MSG_LVL_MED,
MSG_LVL_MED,
MSG_LVL_MED,
+ MSG_LVL_MED,
MSG_LVL_MED
};
@@ -632,6 +639,7 @@
MSG_LVL_LOW,
MSG_LVL_LOW,
MSG_LVL_LOW,
+ MSG_LVL_LOW,
};
static const uint32_t msg_bld_masks_17[] = {
@@ -722,13 +730,16 @@
MSG_LVL_LOW,
MSG_LVL_LOW,
MSG_LVL_LOW,
+ MSG_LVL_LOW,
+ MSG_LVL_LOW,
+ MSG_LVL_LOW,
MSG_LVL_LOW
};
/* LOG CODES */
static const uint32_t log_code_last_tbl[] = {
0x0, /* EQUIP ID 0 */
- 0x182F, /* EQUIP ID 1 */
+ 0x184A, /* EQUIP ID 1 */
0x0, /* EQUIP ID 2 */
0x0, /* EQUIP ID 3 */
0x4910, /* EQUIP ID 4 */
diff --git a/include/linux/hbtp_input.h b/include/linux/hbtp_input.h
new file mode 100644
index 0000000..2c3798e
--- /dev/null
+++ b/include/linux/hbtp_input.h
@@ -0,0 +1,46 @@
+#ifndef _HBTP_INPUT_H
+#define _HBTP_INPUT_H
+
+#include <linux/input.h>
+
+#define HBTP_MAX_FINGER 10
+
+struct hbtp_input_touch {
+ bool active;
+ __s32 tool;
+ __s32 x;
+ __s32 y;
+ __s32 pressure;
+ __s32 major;
+ __s32 minor;
+ __s32 orientation;
+};
+
+struct hbtp_input_mt {
+ __s32 num_touches;
+ struct hbtp_input_touch touches[HBTP_MAX_FINGER];
+};
+
+struct hbtp_input_absinfo {
+ bool active;
+ __u16 code;
+ __s32 minimum;
+ __s32 maximum;
+};
+
+enum hbtp_afe_power_cmd {
+ HBTP_AFE_POWER_ON,
+ HBTP_AFE_POWER_OFF,
+};
+
+/* ioctl */
+#define HBTP_INPUT_IOCTL_BASE 'T'
+#define HBTP_SET_ABSPARAM _IOW(HBTP_INPUT_IOCTL_BASE, 201, \
+ struct hbtp_input_absinfo *)
+#define HBTP_SET_TOUCHDATA _IOW(HBTP_INPUT_IOCTL_BASE, 202, \
+ struct hbtp_input_mt)
+#define HBTP_SET_POWERSTATE _IOW(HBTP_INPUT_IOCTL_BASE, 203, \
+ enum hbtp_afe_power_cmd)
+
+#endif /* _HBTP_INPUT_H */
+
diff --git a/arch/arm/boot/dts/msm8926-qrd-skug.dts b/include/linux/i2c/i2c-qup.h
similarity index 62%
copy from arch/arm/boot/dts/msm8926-qrd-skug.dts
copy to include/linux/i2c/i2c-qup.h
index 4aab4f9..a950864 100644
--- a/arch/arm/boot/dts/msm8926-qrd-skug.dts
+++ b/include/linux/i2c/i2c-qup.h
@@ -1,4 +1,5 @@
-/* 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
@@ -10,12 +11,13 @@
* GNU General Public License for more details.
*/
-/dts-v1/;
-/include/ "msm8926-qrd-skug.dtsi"
+#ifndef __I2C_QUP_H__
+#define __I2C_QUP_H__
-/ {
- model = "Qualcomm MSM 8926 QRD SKUG";
- compatible = "qcom,msm8926-qrd", "qcom,msm8926", "qcom,qrd";
- qcom,board-id = <11 5>;
-};
+#ifdef CONFIG_I2C_QUP
+int __init qup_i2c_init_driver(void);
+#else
+static inline int __init qup_i2c_init_driver(void) { return 0; }
+#endif
+#endif /* __I2C_QUP_H__ */
diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h
index f74fcbe..17986b5 100644
--- a/include/linux/msm_kgsl.h
+++ b/include/linux/msm_kgsl.h
@@ -24,6 +24,7 @@
#define KGSL_CONTEXT_NO_FAULT_TOLERANCE 0x00000200
#define KGSL_CONTEXT_SYNC 0x00000400
+#define KGSL_CONTEXT_PWR_CONSTRAINT 0x00000800
/* bits [12:15] are reserved for future use */
#define KGSL_CONTEXT_TYPE_MASK 0x01F00000
#define KGSL_CONTEXT_TYPE_SHIFT 20
@@ -197,6 +198,7 @@
KGSL_PROP_VERSION = 0x00000008,
KGSL_PROP_GPU_RESET_STAT = 0x00000009,
KGSL_PROP_PWRCTRL = 0x0000000E,
+ KGSL_PROP_PWR_CONSTRAINT = 0x00000012,
};
struct kgsl_shadowprop {
@@ -882,6 +884,34 @@
#define IOCTL_KGSL_SUBMIT_COMMANDS \
_IOWR(KGSL_IOC_TYPE, 0x3D, struct kgsl_submit_commands)
+/**
+ * struct kgsl_device_constraint - device constraint argument
+ * @context_id: KGSL context ID
+ * @type: type of constraint i.e pwrlevel/none
+ * @data: constraint data
+ * @size: size of the constraint data
+ */
+struct kgsl_device_constraint {
+ unsigned int type;
+ unsigned int context_id;
+ void __user *data;
+ size_t size;
+};
+
+/* Constraint Type*/
+#define KGSL_CONSTRAINT_NONE 0
+#define KGSL_CONSTRAINT_PWRLEVEL 1
+
+/* PWRLEVEL constraint level*/
+/* set to min frequency */
+#define KGSL_CONSTRAINT_PWR_MIN 0
+/* set to max frequency */
+#define KGSL_CONSTRAINT_PWR_MAX 1
+
+struct kgsl_device_constraint_pwrlevel {
+ unsigned int level;
+};
+
#ifdef __KERNEL__
#ifdef CONFIG_MSM_KGSL_DRM
int kgsl_gem_obj_addr(int drm_fd, int handle, unsigned long *start,
diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h
index b7f9b86..f36ee04 100644
--- a/include/linux/msm_mdp.h
+++ b/include/linux/msm_mdp.h
@@ -984,6 +984,7 @@
#define MDP_MAX_FENCE_FD 32
#define MDP_BUF_SYNC_FLAG_WAIT 1
+#define MDP_BUF_SYNC_FLAG_RETIRE_FENCE 0x10
struct mdp_buf_sync {
uint32_t flags;
@@ -991,6 +992,7 @@
uint32_t session_id;
int *acq_fen_fd;
int *rel_fen_fd;
+ int *retire_fen_fd;
};
struct mdp_async_blit_req_list {
@@ -1000,19 +1002,11 @@
};
#define MDP_DISPLAY_COMMIT_OVERLAY 1
-struct mdp_buf_fence {
- uint32_t flags;
- uint32_t acq_fen_fd_cnt;
- int acq_fen_fd[MDP_MAX_FENCE_FD];
- int rel_fen_fd[MDP_MAX_FENCE_FD];
-};
-
struct mdp_display_commit {
uint32_t flags;
uint32_t wait_for_finish;
struct fb_var_screeninfo var;
- struct mdp_buf_fence buf_fence;
struct mdp_rect roi;
};
diff --git a/include/linux/msm_vidc_enc.h b/include/linux/msm_vidc_enc.h
index dcc2353..4ce3db1 100644
--- a/include/linux/msm_vidc_enc.h
+++ b/include/linux/msm_vidc_enc.h
@@ -59,6 +59,7 @@
#define VEN_EXTRADATA_QCOMFILLER 0x002
#define VEN_EXTRADATA_SLICEINFO 0x100
#define VEN_EXTRADATA_LTRINFO 0x200
+#define VEN_EXTRADATA_MBINFO 0x400
/*ENCODER CONFIGURATION CONSTANTS*/
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 6a68b72..7be6cd2 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -1380,6 +1380,28 @@
* allowed to be used with the first @NL80211_CMD_SET_STATION command to
* update a TDLS peer STA entry.
*
+ * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information.
+ *
+ * @NL80211_ATTR_CH_SWITCH_COUNT: u32 attribute specifying the number of TBTT's
+ * until the channel switch event.
+ * @NL80211_ATTR_CH_SWITCH_BLOCK_TX: flag attribute specifying that transmission
+ * must be blocked on the current channel (before the channel switch
+ * operation).
+ * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information
+ * for the time while performing a channel switch.
+ * @NL80211_ATTR_CSA_C_OFF_BEACON: Offset of the channel switch counter
+ * field in the beacons tail (%NL80211_ATTR_BEACON_TAIL).
+ * @NL80211_ATTR_CSA_C_OFF_PRESP: Offset of the channel switch counter
+ * field in the probe response (%NL80211_ATTR_PROBE_RESP).
+ *
+ * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32.
+ * As specified in the &enum nl80211_rxmgmt_flags.
+ *
+ * @NL80211_ATTR_STA_SUPPORTED_CHANNELS: array of supported channels.
+ *
+ * @NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES: array of supported
+ * supported operating classes.
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -1680,6 +1702,19 @@
NL80211_ATTR_PEER_AID,
+ NL80211_ATTR_COALESCE_RULE,
+
+ NL80211_ATTR_CH_SWITCH_COUNT,
+ NL80211_ATTR_CH_SWITCH_BLOCK_TX,
+ NL80211_ATTR_CSA_IES,
+ NL80211_ATTR_CSA_C_OFF_BEACON,
+ NL80211_ATTR_CSA_C_OFF_PRESP,
+
+ NL80211_ATTR_RXMGMT_FLAGS,
+
+ NL80211_ATTR_STA_SUPPORTED_CHANNELS,
+
+ NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES,
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
diff --git a/include/linux/regulator/cpr-regulator.h b/include/linux/regulator/cpr-regulator.h
index 3b23d17..e6a8f58 100644
--- a/include/linux/regulator/cpr-regulator.h
+++ b/include/linux/regulator/cpr-regulator.h
@@ -1,5 +1,5 @@
/*
- * 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
@@ -91,12 +91,15 @@
* %VDD_MX_VMIN_APC_SLOW_CORNER_CEILING:
* Equal to slow speed corner ceiling
* %VDD_MX_VMIN_MX_VMAX: Equal to specified vdd-mx-vmax voltage
+ * %VDD_MX_VMIN_APC_CORNER_MAP: Equal to the APC corner mapped MX
+ * voltage
*/
enum vdd_mx_vmin_method {
VDD_MX_VMIN_APC,
VDD_MX_VMIN_APC_CORNER_CEILING,
VDD_MX_VMIN_APC_SLOW_CORNER_CEILING,
VDD_MX_VMIN_MX_VMAX,
+ VDD_MX_VMIN_APC_CORNER_MAP,
};
#ifdef CONFIG_MSM_CPR_REGULATOR
diff --git a/include/linux/regulator/fan53555.h b/include/linux/regulator/fan53555.h
new file mode 100644
index 0000000..bb22419
--- /dev/null
+++ b/include/linux/regulator/fan53555.h
@@ -0,0 +1,66 @@
+/*
+ * fan53555.h - Fairchild Regulator FAN53555 Driver
+ *
+ * Copyright (C) 2012 Marvell Technology Ltd.
+ * Yunfan Zhang <yfzhang@marvell.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __FAN53555_H__
+
+/* VSEL ID */
+enum {
+ FAN53555_VSEL_ID_0 = 0,
+ FAN53555_VSEL_ID_1,
+};
+
+/* Transition slew rate limiting from a low to high voltage.
+ * -----------------------
+ * Bin |Slew Rate(mV/uS)
+ * ------|----------------
+ * 000 | 64.00
+ * ------|----------------
+ * 001 | 32.00
+ * ------|----------------
+ * 010 | 16.00
+ * ------|----------------
+ * 011 | 8.00
+ * ------|----------------
+ * 100 | 4.00
+ * ------|----------------
+ * 101 | 2.00
+ * ------|----------------
+ * 110 | 1.00
+ * ------|----------------
+ * 111 | 0.50
+ * -----------------------
+ */
+enum {
+ FAN53555_SLEW_RATE_64MV = 0,
+ FAN53555_SLEW_RATE_32MV,
+ FAN53555_SLEW_RATE_16MV,
+ FAN53555_SLEW_RATE_8MV,
+ FAN53555_SLEW_RATE_4MV,
+ FAN53555_SLEW_RATE_2MV,
+ FAN53555_SLEW_RATE_1MV,
+ FAN53555_SLEW_RATE_0_5MV,
+};
+
+struct fan53555_platform_data {
+ struct regulator_init_data *regulator;
+ unsigned int slew_rate;
+ /* Sleep VSEL ID */
+ unsigned int sleep_vsel_id;
+};
+
+#ifdef CONFIG_REGULATOR_FAN53555
+int __init fan53555_regulator_init(void);
+#else
+static inline int __init fan53555_regulator_init(void) { return 0; }
+#endif
+
+#endif /* __FAN53555_H__ */
diff --git a/include/linux/regulator/onsemi-ncp6335d.h b/include/linux/regulator/onsemi-ncp6335d.h
index 98a5fea..399742f 100644
--- a/include/linux/regulator/onsemi-ncp6335d.h
+++ b/include/linux/regulator/onsemi-ncp6335d.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
@@ -26,4 +26,10 @@
bool sleep_enable;
};
+#ifdef CONFIG_REGULATOR_ONSEMI_NCP6335D
+int __init ncp6335d_regulator_init(void);
+#else
+static inline int __init ncp6335d_regulator_init(void) { return 0; }
+#endif
+
#endif
diff --git a/include/linux/remote_spinlock.h b/include/linux/remote_spinlock.h
index e39846f..49d19ed 100644
--- a/include/linux/remote_spinlock.h
+++ b/include/linux/remote_spinlock.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2009, 2011, 2013 The Linux Foundation.
+/* Copyright (c) 2008-2009, 2011, 2013-2014 The Linux Foundation.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -83,6 +83,11 @@
0; }) \
: 0; \
})
+#define remote_spin_lock_rlock_id(lock, tid) \
+ _remote_spin_lock_rlock_id(&((lock)->remote), tid)
+
+#define remote_spin_unlock_rlock(lock) \
+ _remote_spin_unlock_rlock(&((lock)->remote))
#define remote_spin_release(lock, pid) \
_remote_spin_release(&((lock)->remote), pid)
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index 045a58b..862a0cc 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -2,7 +2,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Author: Brian Swetland <swetland@google.com>
- * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-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
@@ -346,6 +346,7 @@
* @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
*/
struct msm_otg {
struct usb_phy phy;
@@ -462,6 +463,7 @@
bool ext_chg_active;
struct completion ext_chg_wait;
int ui_enabled;
+ bool pm_done;
};
struct ci13xxx_platform_data {
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 51ca67d..81d5b9c 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -1841,7 +1841,11 @@
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_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,
};
#define V4L2_CID_MPEG_VIDC_SET_PERF_LEVEL (V4L2_CID_MPEG_MSM_VIDC_BASE + 26)
@@ -1909,6 +1913,27 @@
#define V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR \
(V4L2_CID_MPEG_MSM_VIDC_BASE + 38)
+#define V4L2_CID_MPEG_VIDC_VIDEO_LTRMODE \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 39)
+
+enum v4l2_mpeg_vidc_video_ltrmode {
+ V4L2_MPEG_VIDC_VIDEO_LTR_MODE_DISABLE = 0,
+ V4L2_MPEG_VIDC_VIDEO_LTR_MODE_MANUAL = 1,
+ V4L2_MPEG_VIDC_VIDEO_LTR_MODE_PERIODIC = 2
+};
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_LTRCOUNT \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 40)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_USELTRFRAME \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 41)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_MARKLTRFRAME \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 42)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 43)
+
/* Camera class control IDs */
#define V4L2_CID_CAMERA_CLASS_BASE (V4L2_CTRL_CLASS_CAMERA | 0x900)
#define V4L2_CID_CAMERA_CLASS (V4L2_CTRL_CLASS_CAMERA | 1)
diff --git a/include/media/msm_cam_sensor.h b/include/media/msm_cam_sensor.h
index 38d3aab..c0df1d7 100644
--- a/include/media/msm_cam_sensor.h
+++ b/include/media/msm_cam_sensor.h
@@ -47,6 +47,7 @@
#define MAX_EEPROM_NAME 32
#define MAX_AF_ITERATIONS 3
+#define MAX_NUMBER_OF_STEPS 47
enum flash_type {
LED_FLASH = 1,
@@ -440,6 +441,7 @@
CFG_GET_ACTUATOR_INFO,
CFG_SET_ACTUATOR_INFO,
CFG_SET_DEFAULT_FOCUS,
+ CFG_SET_POSITION,
CFG_MOVE_FOCUS,
};
@@ -538,6 +540,13 @@
ACTUATOR_WEB_CAM_2,
};
+
+struct msm_actuator_set_position_t {
+ uint16_t number_of_steps;
+ uint16_t pos[MAX_NUMBER_OF_STEPS];
+ uint16_t delay[MAX_NUMBER_OF_STEPS];
+};
+
struct msm_actuator_cfg_data {
int cfgtype;
uint8_t is_af_supported;
@@ -545,6 +554,7 @@
struct msm_actuator_move_params_t move;
struct msm_actuator_set_info_t set_info;
struct msm_actuator_get_info_t get_info;
+ struct msm_actuator_set_position_t setpos;
enum af_camera_name cam_name;
} cfg;
};
diff --git a/include/media/msm_vidc.h b/include/media/msm_vidc.h
index 81db11a..9028b1a 100644
--- a/include/media/msm_vidc.h
+++ b/include/media/msm_vidc.h
@@ -60,6 +60,7 @@
int msm_vidc_s_fmt(void *instance, struct v4l2_format *f);
int msm_vidc_g_fmt(void *instance, struct v4l2_format *f);
int msm_vidc_s_ctrl(void *instance, struct v4l2_control *a);
+int msm_vidc_s_ext_ctrl(void *instance, struct v4l2_ext_controls *a);
int msm_vidc_g_ctrl(void *instance, struct v4l2_control *a);
int msm_vidc_reqbufs(void *instance, struct v4l2_requestbuffers *b);
int msm_vidc_prepare_buf(void *instance, struct v4l2_buffer *b);
@@ -95,6 +96,16 @@
int *domain_num, int *partition_num);
void *msm_vidc_smem_get_client(void *instance);
#endif
+
+struct msm_vidc_extradata_header {
+ unsigned int size;
+ unsigned int:32; /** Keeping binary compatibility */
+ unsigned int:32; /* with firmware and OpenMAX IL **/
+ unsigned int type; /* msm_vidc_extradata_type */
+ unsigned int data_size;
+ unsigned char data[1];
+};
+
struct msm_vidc_interlace_payload {
unsigned int format;
};
@@ -160,6 +171,13 @@
unsigned int fpa_repetition_period;
unsigned int fpa_extension_flag;
};
+struct msm_vidc_frame_qp_payload {
+ unsigned int frame_qp;
+};
+struct msm_vidc_frame_bits_info_payload {
+ unsigned int frame_bits;
+ unsigned int header_bits;
+};
enum msm_vidc_extradata_type {
EXTRADATA_NONE = 0x00000000,
@@ -173,11 +191,15 @@
EXTRADATA_PANSCAN_WINDOW = 0x00000008,
EXTRADATA_RECOVERY_POINT_SEI = 0x00000009,
EXTRADATA_MPEG2_SEQDISP = 0x0000000D,
+ EXTRADATA_FRAME_QP = 0x0000000F,
+ EXTRADATA_FRAME_BITS_INFO = 0x00000010,
EXTRADATA_MULTISLICE_INFO = 0x7F100000,
EXTRADATA_NUM_CONCEALED_MB = 0x7F100001,
EXTRADATA_INDEX = 0x7F100002,
EXTRADATA_ASPECT_RATIO = 0x7F100003,
EXTRADATA_METADATA_FILLER = 0x7FE00002,
+ MSM_VIDC_EXTRADATA_METADATA_LTR = 0x7F100004,
+ EXTRADATA_METADATA_MBI = 0x7F100005,
};
enum msm_vidc_interlace_type {
INTERLACE_FRAME_PROGRESSIVE = 0x01,
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 7932ba1..77084ed 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -524,6 +524,10 @@
* @capability: station capability
* @ext_capab: extended capabilities of the station
* @ext_capab_len: number of extended capabilities
+ * @supported_channels: supported channels in IEEE 802.11 format
+ * @supported_channels_len: number of supported channels
+ * @supported_oper_classes: supported oper classes in IEEE 802.11 format
+ * @supported_oper_classes_len: number of supported operating classes
*/
struct station_parameters {
u8 *supported_rates;
@@ -542,6 +546,10 @@
u16 capability;
u8 *ext_capab;
u8 ext_capab_len;
+ const u8 *supported_channels;
+ u8 supported_channels_len;
+ const u8 *supported_oper_classes;
+ u8 supported_oper_classes_len;
};
/**
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index e4170a2..a5a9e4d 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -252,6 +252,14 @@
atomic_dec(&fl->users);
}
+extern void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info);
+
+int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6,
+ struct icmp6hdr *thdr, int len);
+
+struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *skb,
+ struct sock *sk, struct flowi6 *fl6);
+
extern int ip6_ra_control(struct sock *sk, int sel);
extern int ipv6_parse_hopopts(struct sk_buff *skb);
@@ -294,6 +302,18 @@
return __ipv6_addr_src_scope(__ipv6_addr_type(addr));
}
+static inline bool __ipv6_addr_needs_scope_id(int type)
+{
+ return type & IPV6_ADDR_LINKLOCAL ||
+ (type & IPV6_ADDR_MULTICAST &&
+ (type & (IPV6_ADDR_LOOPBACK|IPV6_ADDR_LINKLOCAL)));
+}
+
+static inline __u32 ipv6_iface_scope_id(const struct in6_addr *addr, int iface)
+{
+ return __ipv6_addr_needs_scope_id(__ipv6_addr_type(addr)) ? iface : 0;
+}
+
static inline int ipv6_addr_cmp(const struct in6_addr *a1, const struct in6_addr *a2)
{
return memcmp(a1, a2, sizeof(struct in6_addr));
diff --git a/include/net/ping.h b/include/net/ping.h
index 682b5ae..b1717ae 100644
--- a/include/net/ping.h
+++ b/include/net/ping.h
@@ -13,6 +13,7 @@
#ifndef _PING_H
#define _PING_H
+#include <net/icmp.h>
#include <net/netns/hash.h>
/* PING_HTABLE_SIZE must be power of 2 */
@@ -28,6 +29,18 @@
*/
#define GID_T_MAX (((gid_t)~0U) >> 1)
+/* Compatibility glue so we can support IPv6 when it's compiled as a module */
+struct pingv6_ops {
+ int (*ipv6_recv_error)(struct sock *sk, struct msghdr *msg, int len);
+ int (*datagram_recv_ctl)(struct sock *sk, struct msghdr *msg,
+ struct sk_buff *skb);
+ int (*icmpv6_err_convert)(u8 type, u8 code, int *err);
+ void (*ipv6_icmp_error)(struct sock *sk, struct sk_buff *skb, int err,
+ __be16 port, u32 info, u8 *payload);
+ int (*ipv6_chk_addr)(struct net *net, const struct in6_addr *addr,
+ struct net_device *dev, int strict);
+};
+
struct ping_table {
struct hlist_nulls_head hash[PING_HTABLE_SIZE];
rwlock_t lock;
@@ -39,10 +52,40 @@
};
extern struct proto ping_prot;
+extern struct ping_table ping_table;
+#if IS_ENABLED(CONFIG_IPV6)
+extern struct pingv6_ops pingv6_ops;
+#endif
+struct pingfakehdr {
+ struct icmphdr icmph;
+ struct iovec *iov;
+ sa_family_t family;
+ __wsum wcheck;
+};
-extern void ping_rcv(struct sk_buff *);
-extern void ping_err(struct sk_buff *, u32 info);
+int ping_get_port(struct sock *sk, unsigned short ident);
+void ping_hash(struct sock *sk);
+void ping_unhash(struct sock *sk);
+
+int ping_init_sock(struct sock *sk);
+void ping_close(struct sock *sk, long timeout);
+int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len);
+void ping_err(struct sk_buff *skb, int offset, u32 info);
+void ping_v4_err(struct sk_buff *skb, u32 info);
+int ping_getfrag(void *from, char *to, int offset, int fraglen, int odd,
+ struct sk_buff *);
+
+int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+ size_t len, int noblock, int flags, int *addr_len);
+int ping_common_sendmsg(int family, struct msghdr *msg, size_t len,
+ void *user_icmph, size_t icmph_len);
+int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+ size_t len);
+int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+ size_t len);
+int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
+void ping_rcv(struct sk_buff *skb);
#ifdef CONFIG_PROC_FS
extern int __init ping_proc_init(void);
@@ -50,6 +93,7 @@
#endif
void __init ping_init(void);
-
+int __init pingv6_init(void);
+void pingv6_exit(void);
#endif /* _PING_H */
diff --git a/include/net/transp_v6.h b/include/net/transp_v6.h
index 498433d..48b42ea 100644
--- a/include/net/transp_v6.h
+++ b/include/net/transp_v6.h
@@ -11,6 +11,7 @@
extern struct proto udpv6_prot;
extern struct proto udplitev6_prot;
extern struct proto tcpv6_prot;
+extern struct proto pingv6_prot;
struct flowi6;
@@ -21,6 +22,8 @@
extern void ipv6_frag_exit(void);
/* transport protocols */
+extern int pingv6_init(void);
+extern void pingv6_exit(void);
extern int rawv6_init(void);
extern void rawv6_exit(void);
extern int udpv6_init(void);
diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h
index a78c333..1635fc3 100644
--- a/include/sound/q6asm-v2.h
+++ b/include/sound/q6asm-v2.h
@@ -72,6 +72,7 @@
#define SYNC_IO_MODE 0x0001
#define ASYNC_IO_MODE 0x0002
#define COMPRESSED_IO 0x0040
+#define COMPRESSED_STREAM_IO 0x0080
#define NT_MODE 0x0400
#define NO_TIMESTAMP 0xFF00
@@ -399,4 +400,8 @@
int q6asm_send_meta_data(struct audio_client *ac, uint32_t initial_samples,
uint32_t trailing_samples);
+/* Send the stream meta data to remove initial and trailing silence */
+int q6asm_stream_send_meta_data(struct audio_client *ac, uint32_t stream_id,
+ uint32_t initial_samples, uint32_t trailing_samples);
+
#endif /* __Q6_ASM_H__ */
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 954a8c4..d9e1f1d 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -1682,9 +1682,12 @@
{
struct rq *rq = task_rq(p);
- if (WARN_ON(rq != this_rq()) ||
- WARN_ON(p == current))
+ if (rq != this_rq() || p == current) {
+ printk_sched("%s: Failed to wakeup task %d (%s), rq = %p, this_rq = %p, p = %p, current = %p\n",
+ __func__, task_pid_nr(p), p->comm, rq,
+ this_rq(), p, current);
return;
+ }
lockdep_assert_held(&rq->lock);
@@ -6947,6 +6950,7 @@
#ifdef CONFIG_CGROUP_SCHED
struct task_group root_task_group;
+LIST_HEAD(task_groups);
#endif
DECLARE_PER_CPU(cpumask_var_t, load_balance_tmpmask);
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index c0288a6..34fe64f 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -80,7 +80,7 @@
struct cfs_rq;
struct rt_rq;
-static LIST_HEAD(task_groups);
+extern struct list_head task_groups;
struct cfs_bandwidth {
#ifdef CONFIG_CFS_BANDWIDTH
diff --git a/lib/scatterlist.c b/lib/scatterlist.c
index 6096e89..547a01f 100644
--- a/lib/scatterlist.c
+++ b/lib/scatterlist.c
@@ -228,12 +228,14 @@
struct scatterlist *sg, *prv;
unsigned int left;
+ memset(table, 0, sizeof(*table));
+
+ if (nents == 0)
+ return -EINVAL;
#ifndef ARCH_HAS_SG_CHAIN
BUG_ON(nents > max_ents);
#endif
- memset(table, 0, sizeof(*table));
-
left = nents;
prv = NULL;
do {
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index a5e8dc2..d012c75 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -190,6 +190,18 @@
zone_reclaimable_pages(z) - z->dirty_balance_reserve;
}
/*
+ * Unreclaimable memory (kernel memory or anonymous memory
+ * without swap) can bring down the dirtyable pages below
+ * the zone's dirty balance reserve and the above calculation
+ * will underflow. However we still want to add in nodes
+ * which are below threshold (negative values) to get a more
+ * accurate calculation but make sure that the total never
+ * underflows.
+ */
+ if ((long)x < 0)
+ x = 0;
+
+ /*
* Make sure that the number of highmem pages is never larger
* than the number of the total dirtyable memory. This can only
* occur in very strange VM situations but we want to make sure
@@ -211,8 +223,8 @@
{
unsigned long x;
- x = global_page_state(NR_FREE_PAGES) + global_reclaimable_pages() -
- dirty_balance_reserve;
+ x = global_page_state(NR_FREE_PAGES) + global_reclaimable_pages();
+ x -= min(x, dirty_balance_reserve);
if (!vm_highmem_is_dirtyable)
x -= highmem_dirtyable_memory(x);
@@ -279,9 +291,12 @@
* highmem zone can hold its share of dirty pages, so we don't
* care about vm_highmem_is_dirtyable here.
*/
- return zone_page_state(zone, NR_FREE_PAGES) +
- zone_reclaimable_pages(zone) -
- zone->dirty_balance_reserve;
+ unsigned long nr_pages = zone_page_state(zone, NR_FREE_PAGES) +
+ zone_reclaimable_pages(zone);
+
+ /* don't allow this to underflow */
+ nr_pages -= min(nr_pages, zone->dirty_balance_reserve);
+ return nr_pages;
}
/**
diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c
index a225089..f378b38 100644
--- a/net/core/flow_dissector.c
+++ b/net/core/flow_dissector.c
@@ -35,7 +35,7 @@
struct iphdr _iph;
ip:
iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph);
- if (!iph)
+ if (!iph || iph->ihl < 5)
return false;
if (ip_is_fragment(iph))
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index f20b5cc..b848d6f 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1559,7 +1559,7 @@
static const struct net_protocol icmp_protocol = {
.handler = icmp_rcv,
- .err_handler = ping_err,
+ .err_handler = ping_v4_err,
.no_policy = 1,
.netns_ok = 1,
};
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 2cb2bf8..2e109ff 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -788,7 +788,7 @@
if (iph->protocol == IPPROTO_ICMP &&
iph->ihl >= 5 &&
pskb_may_pull(skb, (iph->ihl<<2)+8)) {
- ping_err(skb, icmp_hdr(skb)->un.gateway);
+ ping_v4_err(skb, icmp_hdr(skb)->un.gateway);
}
out:
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
index 50009c7..7f38d35 100644
--- a/net/ipv4/ping.c
+++ b/net/ipv4/ping.c
@@ -33,7 +33,6 @@
#include <linux/netdevice.h>
#include <net/snmp.h>
#include <net/ip.h>
-#include <net/ipv6.h>
#include <net/icmp.h>
#include <net/protocol.h>
#include <linux/skbuff.h>
@@ -46,8 +45,18 @@
#include <net/inet_common.h>
#include <net/checksum.h>
+#if IS_ENABLED(CONFIG_IPV6)
+#include <linux/in6.h>
+#include <linux/icmpv6.h>
+#include <net/addrconf.h>
+#include <net/ipv6.h>
+#include <net/transp_v6.h>
+#endif
-static struct ping_table ping_table;
+
+struct ping_table ping_table;
+struct pingv6_ops pingv6_ops;
+EXPORT_SYMBOL_GPL(pingv6_ops);
static u16 ping_port_rover;
@@ -57,6 +66,7 @@
pr_debug("hash(%d) = %d\n", num, res);
return res;
}
+EXPORT_SYMBOL_GPL(ping_hash);
static inline struct hlist_nulls_head *ping_hashslot(struct ping_table *table,
struct net *net, unsigned num)
@@ -64,7 +74,7 @@
return &table->hash[ping_hashfn(net, num, PING_HTABLE_MASK)];
}
-static int ping_v4_get_port(struct sock *sk, unsigned short ident)
+int ping_get_port(struct sock *sk, unsigned short ident)
{
struct hlist_nulls_node *node;
struct hlist_nulls_head *hlist;
@@ -102,6 +112,10 @@
ping_portaddr_for_each_entry(sk2, node, hlist) {
isk2 = inet_sk(sk2);
+ /* BUG? Why is this reuse and not reuseaddr? ping.c
+ * doesn't turn off SO_REUSEADDR, and it doesn't expect
+ * that other ping processes can steal its packets.
+ */
if ((isk2->inet_num == ident) &&
(sk2 != sk) &&
(!sk2->sk_reuse || !sk->sk_reuse))
@@ -124,17 +138,18 @@
write_unlock_bh(&ping_table.lock);
return 1;
}
+EXPORT_SYMBOL_GPL(ping_get_port);
-static void ping_v4_hash(struct sock *sk)
+void ping_hash(struct sock *sk)
{
- pr_debug("ping_v4_hash(sk->port=%u)\n", inet_sk(sk)->inet_num);
+ pr_debug("ping_hash(sk->port=%u)\n", inet_sk(sk)->inet_num);
BUG(); /* "Please do not press this button again." */
}
-static void ping_v4_unhash(struct sock *sk)
+void ping_unhash(struct sock *sk)
{
struct inet_sock *isk = inet_sk(sk);
- pr_debug("ping_v4_unhash(isk=%p,isk->num=%u)\n", isk, isk->inet_num);
+ pr_debug("ping_unhash(isk=%p,isk->num=%u)\n", isk, isk->inet_num);
if (sk_hashed(sk)) {
write_lock_bh(&ping_table.lock);
hlist_nulls_del(&sk->sk_nulls_node);
@@ -145,31 +160,61 @@
write_unlock_bh(&ping_table.lock);
}
}
+EXPORT_SYMBOL_GPL(ping_unhash);
-static struct sock *ping_v4_lookup(struct net *net, __be32 saddr, __be32 daddr,
- u16 ident, int dif)
+static struct sock *ping_lookup(struct net *net, struct sk_buff *skb, u16 ident)
{
struct hlist_nulls_head *hslot = ping_hashslot(&ping_table, net, ident);
struct sock *sk = NULL;
struct inet_sock *isk;
struct hlist_nulls_node *hnode;
+ int dif = skb->dev->ifindex;
- pr_debug("try to find: num = %d, daddr = %pI4, dif = %d\n",
- (int)ident, &daddr, dif);
+ if (skb->protocol == htons(ETH_P_IP)) {
+ pr_debug("try to find: num = %d, daddr = %pI4, dif = %d\n",
+ (int)ident, &ip_hdr(skb)->daddr, dif);
+#if IS_ENABLED(CONFIG_IPV6)
+ } else if (skb->protocol == htons(ETH_P_IPV6)) {
+ pr_debug("try to find: num = %d, daddr = %pI6c, dif = %d\n",
+ (int)ident, &ipv6_hdr(skb)->daddr, dif);
+#endif
+ }
+
read_lock_bh(&ping_table.lock);
ping_portaddr_for_each_entry(sk, hnode, hslot) {
isk = inet_sk(sk);
- pr_debug("found: %p: num = %d, daddr = %pI4, dif = %d\n", sk,
- (int)isk->inet_num, &isk->inet_rcv_saddr,
- sk->sk_bound_dev_if);
-
pr_debug("iterate\n");
if (isk->inet_num != ident)
continue;
- if (isk->inet_rcv_saddr && isk->inet_rcv_saddr != daddr)
- continue;
+
+ if (skb->protocol == htons(ETH_P_IP) &&
+ sk->sk_family == AF_INET) {
+ pr_debug("found: %p: num=%d, daddr=%pI4, dif=%d\n", sk,
+ (int) isk->inet_num, &isk->inet_rcv_saddr,
+ sk->sk_bound_dev_if);
+
+ if (isk->inet_rcv_saddr &&
+ isk->inet_rcv_saddr != ip_hdr(skb)->daddr)
+ continue;
+#if IS_ENABLED(CONFIG_IPV6)
+ } else if (skb->protocol == htons(ETH_P_IPV6) &&
+ sk->sk_family == AF_INET6) {
+ struct ipv6_pinfo *np = inet6_sk(sk);
+
+ pr_debug("found: %p: num=%d, daddr=%pI6c, dif=%d\n", sk,
+ (int) isk->inet_num,
+ &inet6_sk(sk)->rcv_saddr,
+ sk->sk_bound_dev_if);
+
+ if (!ipv6_addr_any(&np->rcv_saddr) &&
+ !ipv6_addr_equal(&np->rcv_saddr,
+ &ipv6_hdr(skb)->daddr))
+ continue;
+#endif
+ }
+
if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif)
continue;
@@ -198,7 +243,7 @@
}
-static int ping_init_sock(struct sock *sk)
+int ping_init_sock(struct sock *sk)
{
struct net *net = sock_net(sk);
gid_t group = current_egid();
@@ -224,8 +269,9 @@
return -EACCES;
}
+EXPORT_SYMBOL_GPL(ping_init_sock);
-static void ping_close(struct sock *sk, long timeout)
+void ping_close(struct sock *sk, long timeout)
{
pr_debug("ping_close(sk=%p,sk->num=%u)\n",
inet_sk(sk), inet_sk(sk)->inet_num);
@@ -233,36 +279,122 @@
sk_common_release(sk);
}
+EXPORT_SYMBOL_GPL(ping_close);
+/* Checks the bind address and possibly modifies sk->sk_bound_dev_if. */
+int ping_check_bind_addr(struct sock *sk, struct inet_sock *isk,
+ struct sockaddr *uaddr, int addr_len) {
+ struct net *net = sock_net(sk);
+ if (sk->sk_family == AF_INET) {
+ struct sockaddr_in *addr = (struct sockaddr_in *) uaddr;
+ int chk_addr_ret;
+
+ if (addr_len < sizeof(*addr))
+ return -EINVAL;
+
+ pr_debug("ping_check_bind_addr(sk=%p,addr=%pI4,port=%d)\n",
+ sk, &addr->sin_addr.s_addr, ntohs(addr->sin_port));
+
+ chk_addr_ret = inet_addr_type(net, addr->sin_addr.s_addr);
+
+ if (addr->sin_addr.s_addr == htonl(INADDR_ANY))
+ chk_addr_ret = RTN_LOCAL;
+
+ if ((sysctl_ip_nonlocal_bind == 0 &&
+ isk->freebind == 0 && isk->transparent == 0 &&
+ chk_addr_ret != RTN_LOCAL) ||
+ chk_addr_ret == RTN_MULTICAST ||
+ chk_addr_ret == RTN_BROADCAST)
+ return -EADDRNOTAVAIL;
+
+#if IS_ENABLED(CONFIG_IPV6)
+ } else if (sk->sk_family == AF_INET6) {
+ struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr;
+ int addr_type, scoped, has_addr;
+ struct net_device *dev = NULL;
+
+ if (addr_len < sizeof(*addr))
+ return -EINVAL;
+
+ pr_debug("ping_check_bind_addr(sk=%p,addr=%pI6c,port=%d)\n",
+ sk, addr->sin6_addr.s6_addr, ntohs(addr->sin6_port));
+
+ addr_type = ipv6_addr_type(&addr->sin6_addr);
+ scoped = __ipv6_addr_needs_scope_id(addr_type);
+ if ((addr_type != IPV6_ADDR_ANY &&
+ !(addr_type & IPV6_ADDR_UNICAST)) ||
+ (scoped && !addr->sin6_scope_id))
+ return -EINVAL;
+
+ rcu_read_lock();
+ if (addr->sin6_scope_id) {
+ dev = dev_get_by_index_rcu(net, addr->sin6_scope_id);
+ if (!dev) {
+ rcu_read_unlock();
+ return -ENODEV;
+ }
+ }
+ has_addr = pingv6_ops.ipv6_chk_addr(net, &addr->sin6_addr, dev,
+ scoped);
+ rcu_read_unlock();
+
+ if (!(isk->freebind || isk->transparent || has_addr ||
+ addr_type == IPV6_ADDR_ANY))
+ return -EADDRNOTAVAIL;
+
+ if (scoped)
+ sk->sk_bound_dev_if = addr->sin6_scope_id;
+#endif
+ } else {
+ return -EAFNOSUPPORT;
+ }
+ return 0;
+}
+
+void ping_set_saddr(struct sock *sk, struct sockaddr *saddr)
+{
+ if (saddr->sa_family == AF_INET) {
+ struct inet_sock *isk = inet_sk(sk);
+ struct sockaddr_in *addr = (struct sockaddr_in *) saddr;
+ isk->inet_rcv_saddr = isk->inet_saddr = addr->sin_addr.s_addr;
+#if IS_ENABLED(CONFIG_IPV6)
+ } else if (saddr->sa_family == AF_INET6) {
+ struct sockaddr_in6 *addr = (struct sockaddr_in6 *) saddr;
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ np->rcv_saddr = np->saddr = addr->sin6_addr;
+#endif
+ }
+}
+
+void ping_clear_saddr(struct sock *sk, int dif)
+{
+ sk->sk_bound_dev_if = dif;
+ if (sk->sk_family == AF_INET) {
+ struct inet_sock *isk = inet_sk(sk);
+ isk->inet_rcv_saddr = isk->inet_saddr = 0;
+#if IS_ENABLED(CONFIG_IPV6)
+ } else if (sk->sk_family == AF_INET6) {
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ memset(&np->rcv_saddr, 0, sizeof(np->rcv_saddr));
+ memset(&np->saddr, 0, sizeof(np->saddr));
+#endif
+ }
+}
/*
* We need our own bind because there are no privileged id's == local ports.
* Moreover, we don't allow binding to multi- and broadcast addresses.
*/
-static int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
+int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
- struct sockaddr_in *addr = (struct sockaddr_in *)uaddr;
struct inet_sock *isk = inet_sk(sk);
unsigned short snum;
- int chk_addr_ret;
int err;
+ int dif = sk->sk_bound_dev_if;
- if (addr_len < sizeof(struct sockaddr_in))
- return -EINVAL;
-
- pr_debug("ping_v4_bind(sk=%p,sa_addr=%08x,sa_port=%d)\n",
- sk, addr->sin_addr.s_addr, ntohs(addr->sin_port));
-
- chk_addr_ret = inet_addr_type(sock_net(sk), addr->sin_addr.s_addr);
- if (addr->sin_addr.s_addr == htonl(INADDR_ANY))
- chk_addr_ret = RTN_LOCAL;
-
- if ((sysctl_ip_nonlocal_bind == 0 &&
- isk->freebind == 0 && isk->transparent == 0 &&
- chk_addr_ret != RTN_LOCAL) ||
- chk_addr_ret == RTN_MULTICAST ||
- chk_addr_ret == RTN_BROADCAST)
- return -EADDRNOTAVAIL;
+ err = ping_check_bind_addr(sk, isk, uaddr, addr_len);
+ if (err)
+ return err;
lock_sock(sk);
@@ -271,42 +403,50 @@
goto out;
err = -EADDRINUSE;
- isk->inet_rcv_saddr = isk->inet_saddr = addr->sin_addr.s_addr;
- snum = ntohs(addr->sin_port);
- if (ping_v4_get_port(sk, snum) != 0) {
- isk->inet_saddr = isk->inet_rcv_saddr = 0;
+ ping_set_saddr(sk, uaddr);
+ snum = ntohs(((struct sockaddr_in *)uaddr)->sin_port);
+ if (ping_get_port(sk, snum) != 0) {
+ ping_clear_saddr(sk, dif);
goto out;
}
- pr_debug("after bind(): num = %d, daddr = %pI4, dif = %d\n",
+ pr_debug("after bind(): num = %d, dif = %d\n",
(int)isk->inet_num,
- &isk->inet_rcv_saddr,
(int)sk->sk_bound_dev_if);
err = 0;
- if (isk->inet_rcv_saddr)
+ if ((sk->sk_family == AF_INET && isk->inet_rcv_saddr) ||
+ (sk->sk_family == AF_INET6 &&
+ !ipv6_addr_any(&inet6_sk(sk)->rcv_saddr)))
sk->sk_userlocks |= SOCK_BINDADDR_LOCK;
+
if (snum)
sk->sk_userlocks |= SOCK_BINDPORT_LOCK;
isk->inet_sport = htons(isk->inet_num);
isk->inet_daddr = 0;
isk->inet_dport = 0;
+
+#if IS_ENABLED(CONFIG_IPV6)
+ if (sk->sk_family == AF_INET6)
+ memset(&inet6_sk(sk)->daddr, 0, sizeof(inet6_sk(sk)->daddr));
+#endif
+
sk_dst_reset(sk);
out:
release_sock(sk);
pr_debug("ping_v4_bind -> %d\n", err);
return err;
}
+EXPORT_SYMBOL_GPL(ping_bind);
/*
* Is this a supported type of ICMP message?
*/
-static inline int ping_supported(int type, int code)
+static inline int ping_supported(int family, int type, int code)
{
- if (type == ICMP_ECHO && code == 0)
- return 1;
- return 0;
+ return (family == AF_INET && type == ICMP_ECHO && code == 0) ||
+ (family == AF_INET6 && type == ICMPV6_ECHO_REQUEST && code == 0);
}
/*
@@ -314,30 +454,44 @@
* sort of error condition.
*/
-static int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
-
-void ping_err(struct sk_buff *skb, u32 info)
+void ping_err(struct sk_buff *skb, int offset, u32 info)
{
- struct iphdr *iph = (struct iphdr *)skb->data;
- struct icmphdr *icmph = (struct icmphdr *)(skb->data+(iph->ihl<<2));
+ int family;
+ struct icmphdr *icmph;
struct inet_sock *inet_sock;
- int type = icmph->type;
- int code = icmph->code;
+ int type;
+ int code;
struct net *net = dev_net(skb->dev);
struct sock *sk;
int harderr;
int err;
+ if (skb->protocol == htons(ETH_P_IP)) {
+ struct iphdr *iph = (struct iphdr *)skb->data;
+ offset = iph->ihl << 2;
+ family = AF_INET;
+ type = icmp_hdr(skb)->type;
+ code = icmp_hdr(skb)->code;
+ icmph = (struct icmphdr *)(skb->data + offset);
+ } else if (skb->protocol == htons(ETH_P_IPV6)) {
+ family = AF_INET6;
+ type = icmp6_hdr(skb)->icmp6_type;
+ code = icmp6_hdr(skb)->icmp6_code;
+ icmph = (struct icmphdr *) (skb->data + offset);
+ } else {
+ BUG();
+ }
+
/* We assume the packet has already been checked by icmp_unreach */
- if (!ping_supported(icmph->type, icmph->code))
+ if (!ping_supported(family, icmph->type, icmph->code))
return;
- pr_debug("ping_err(type=%04x,code=%04x,id=%04x,seq=%04x)\n", type,
- code, ntohs(icmph->un.echo.id), ntohs(icmph->un.echo.sequence));
+ pr_debug("ping_err(proto=0x%x,type=%d,code=%d,id=%04x,seq=%04x)\n",
+ skb->protocol, type, code, ntohs(icmph->un.echo.id),
+ ntohs(icmph->un.echo.sequence));
- sk = ping_v4_lookup(net, iph->daddr, iph->saddr,
- ntohs(icmph->un.echo.id), skb->dev->ifindex);
+ sk = ping_lookup(net, skb, ntohs(icmph->un.echo.id));
if (sk == NULL) {
pr_debug("no socket, dropping\n");
return; /* No socket for error */
@@ -348,70 +502,85 @@
harderr = 0;
inet_sock = inet_sk(sk);
- switch (type) {
- default:
- case ICMP_TIME_EXCEEDED:
- err = EHOSTUNREACH;
- break;
- case ICMP_SOURCE_QUENCH:
- /* This is not a real error but ping wants to see it.
- * Report it with some fake errno. */
- err = EREMOTEIO;
- break;
- case ICMP_PARAMETERPROB:
- err = EPROTO;
- harderr = 1;
- break;
- case ICMP_DEST_UNREACH:
- if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */
- if (inet_sock->pmtudisc != IP_PMTUDISC_DONT) {
- err = EMSGSIZE;
- harderr = 1;
- break;
+ if (skb->protocol == htons(ETH_P_IP)) {
+ switch (type) {
+ default:
+ case ICMP_TIME_EXCEEDED:
+ err = EHOSTUNREACH;
+ break;
+ case ICMP_SOURCE_QUENCH:
+ /* This is not a real error but ping wants to see it.
+ * Report it with some fake errno. */
+ err = EREMOTEIO;
+ break;
+ case ICMP_PARAMETERPROB:
+ err = EPROTO;
+ harderr = 1;
+ break;
+ case ICMP_DEST_UNREACH:
+ if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */
+ if (inet_sock->pmtudisc != IP_PMTUDISC_DONT) {
+ err = EMSGSIZE;
+ harderr = 1;
+ break;
+ }
+ goto out;
}
- goto out;
+ err = EHOSTUNREACH;
+ if (code <= NR_ICMP_UNREACH) {
+ harderr = icmp_err_convert[code].fatal;
+ err = icmp_err_convert[code].errno;
+ }
+ break;
+ case ICMP_REDIRECT:
+ /* See ICMP_SOURCE_QUENCH */
+ err = EREMOTEIO;
+ break;
}
- err = EHOSTUNREACH;
- if (code <= NR_ICMP_UNREACH) {
- harderr = icmp_err_convert[code].fatal;
- err = icmp_err_convert[code].errno;
- }
- break;
- case ICMP_REDIRECT:
- /* See ICMP_SOURCE_QUENCH */
- err = EREMOTEIO;
- break;
+#if IS_ENABLED(CONFIG_IPV6)
+ } else if (skb->protocol == htons(ETH_P_IPV6)) {
+ harderr = pingv6_ops.icmpv6_err_convert(type, code, &err);
+#endif
}
/*
* RFC1122: OK. Passes ICMP errors back to application, as per
* 4.1.3.3.
*/
- if (!inet_sock->recverr) {
+ if ((family == AF_INET && !inet_sock->recverr) ||
+ (family == AF_INET6 && !inet6_sk(sk)->recverr)) {
if (!harderr || sk->sk_state != TCP_ESTABLISHED)
goto out;
} else {
- ip_icmp_error(sk, skb, err, 0 /* no remote port */,
- info, (u8 *)icmph);
+ if (family == AF_INET) {
+ ip_icmp_error(sk, skb, err, 0 /* no remote port */,
+ info, (u8 *)icmph);
+#if IS_ENABLED(CONFIG_IPV6)
+ } else if (family == AF_INET6) {
+ pingv6_ops.ipv6_icmp_error(sk, skb, err, 0,
+ info, (u8 *)icmph);
+#endif
+ }
}
sk->sk_err = err;
sk->sk_error_report(sk);
out:
sock_put(sk);
}
+EXPORT_SYMBOL_GPL(ping_err);
+
+void ping_v4_err(struct sk_buff *skb, u32 info)
+{
+ ping_err(skb, 0, info);
+}
/*
- * Copy and checksum an ICMP Echo packet from user space into a buffer.
+ * Copy and checksum an ICMP Echo packet from user space into a buffer
+ * starting from the payload.
*/
-struct pingfakehdr {
- struct icmphdr icmph;
- struct iovec *iov;
- __wsum wcheck;
-};
-
-static int ping_getfrag(void *from, char * to,
- int offset, int fraglen, int odd, struct sk_buff *skb)
+int ping_getfrag(void *from, char *to,
+ int offset, int fraglen, int odd, struct sk_buff *skb)
{
struct pingfakehdr *pfh = (struct pingfakehdr *)from;
@@ -422,20 +591,33 @@
pfh->iov, 0, fraglen - sizeof(struct icmphdr),
&pfh->wcheck))
return -EFAULT;
-
- return 0;
+ } else if (offset < sizeof(struct icmphdr)) {
+ BUG();
+ } else {
+ if (csum_partial_copy_fromiovecend
+ (to, pfh->iov, offset - sizeof(struct icmphdr),
+ fraglen, &pfh->wcheck))
+ return -EFAULT;
}
- if (offset < sizeof(struct icmphdr))
- BUG();
- if (csum_partial_copy_fromiovecend
- (to, pfh->iov, offset - sizeof(struct icmphdr),
- fraglen, &pfh->wcheck))
- return -EFAULT;
+
+#if IS_ENABLED(CONFIG_IPV6)
+ /* For IPv6, checksum each skb as we go along, as expected by
+ * icmpv6_push_pending_frames. For IPv4, accumulate the checksum in
+ * wcheck, it will be finalized in ping_v4_push_pending_frames.
+ */
+ if (pfh->family == AF_INET6) {
+ skb->csum = pfh->wcheck;
+ skb->ip_summed = CHECKSUM_NONE;
+ pfh->wcheck = 0;
+ }
+#endif
+
return 0;
}
+EXPORT_SYMBOL_GPL(ping_getfrag);
-static int ping_push_pending_frames(struct sock *sk, struct pingfakehdr *pfh,
- struct flowi4 *fl4)
+static int ping_v4_push_pending_frames(struct sock *sk, struct pingfakehdr *pfh,
+ struct flowi4 *fl4)
{
struct sk_buff *skb = skb_peek(&sk->sk_write_queue);
@@ -447,24 +629,9 @@
return ip_push_pending_frames(sk, fl4);
}
-static int ping_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
- size_t len)
-{
- struct net *net = sock_net(sk);
- struct flowi4 fl4;
- struct inet_sock *inet = inet_sk(sk);
- struct ipcm_cookie ipc;
- struct icmphdr user_icmph;
- struct pingfakehdr pfh;
- struct rtable *rt = NULL;
- struct ip_options_data opt_copy;
- int free = 0;
- __be32 saddr, daddr, faddr;
- u8 tos;
- int err;
-
- pr_debug("ping_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num);
-
+int ping_common_sendmsg(int family, struct msghdr *msg, size_t len,
+ void *user_icmph, size_t icmph_len) {
+ u8 type, code;
if (len > 0xFFFF)
return -EMSGSIZE;
@@ -479,15 +646,53 @@
/*
* Fetch the ICMP header provided by the userland.
- * iovec is modified!
+ * iovec is modified! The ICMP header is consumed.
*/
-
- if (memcpy_fromiovec((u8 *)&user_icmph, msg->msg_iov,
- sizeof(struct icmphdr)))
+ if (memcpy_fromiovec(user_icmph, msg->msg_iov, icmph_len))
return -EFAULT;
- if (!ping_supported(user_icmph.type, user_icmph.code))
+
+ if (family == AF_INET) {
+ type = ((struct icmphdr *) user_icmph)->type;
+ code = ((struct icmphdr *) user_icmph)->code;
+#if IS_ENABLED(CONFIG_IPV6)
+ } else if (family == AF_INET6) {
+ type = ((struct icmp6hdr *) user_icmph)->icmp6_type;
+ code = ((struct icmp6hdr *) user_icmph)->icmp6_code;
+#endif
+ } else {
+ BUG();
+ }
+
+ if (!ping_supported(family, type, code))
return -EINVAL;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ping_common_sendmsg);
+
+int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+ size_t len)
+{
+ struct net *net = sock_net(sk);
+ struct flowi4 fl4;
+ struct inet_sock *inet = inet_sk(sk);
+ struct ipcm_cookie ipc;
+ struct icmphdr user_icmph;
+ struct pingfakehdr pfh;
+ struct rtable *rt = NULL;
+ struct ip_options_data opt_copy;
+ int free = 0;
+ __be32 saddr, daddr, faddr;
+ u8 tos;
+ int err;
+
+ pr_debug("ping_v4_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num);
+
+ err = ping_common_sendmsg(AF_INET, msg, len, &user_icmph,
+ sizeof(user_icmph));
+ if (err)
+ return err;
+
/*
* Get and verify the address.
*/
@@ -593,13 +798,14 @@
pfh.icmph.un.echo.sequence = user_icmph.un.echo.sequence;
pfh.iov = msg->msg_iov;
pfh.wcheck = 0;
+ pfh.family = AF_INET;
err = ip_append_data(sk, &fl4, ping_getfrag, &pfh, len,
0, &ipc, &rt, msg->msg_flags);
if (err)
ip_flush_pending_frames(sk);
else
- err = ping_push_pending_frames(sk, &pfh, &fl4);
+ err = ping_v4_push_pending_frames(sk, &pfh, &fl4);
release_sock(sk);
out:
@@ -620,11 +826,13 @@
goto out;
}
-static int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
- size_t len, int noblock, int flags, int *addr_len)
+int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+ size_t len, int noblock, int flags, int *addr_len)
{
struct inet_sock *isk = inet_sk(sk);
- struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name;
+ int family = sk->sk_family;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
struct sk_buff *skb;
int copied, err;
@@ -634,11 +842,22 @@
if (flags & MSG_OOB)
goto out;
- if (addr_len)
- *addr_len = sizeof(*sin);
+ if (addr_len) {
+ if (family == AF_INET)
+ *addr_len = sizeof(*sin);
+ else if (family == AF_INET6 && addr_len)
+ *addr_len = sizeof(*sin6);
+ }
- if (flags & MSG_ERRQUEUE)
- return ip_recv_error(sk, msg, len);
+ if (flags & MSG_ERRQUEUE) {
+ if (family == AF_INET) {
+ return ip_recv_error(sk, msg, len);
+#if IS_ENABLED(CONFIG_IPV6)
+ } else if (family == AF_INET6) {
+ return pingv6_ops.ipv6_recv_error(sk, msg, len);
+#endif
+ }
+ }
skb = skb_recv_datagram(sk, flags, noblock, &err);
if (!skb)
@@ -657,15 +876,41 @@
sock_recv_timestamp(msg, sk, skb);
- /* Copy the address. */
- if (sin) {
+ /* Copy the address and add cmsg data. */
+ if (family == AF_INET) {
+ sin = (struct sockaddr_in *) msg->msg_name;
sin->sin_family = AF_INET;
sin->sin_port = 0 /* skb->h.uh->source */;
sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+
+ if (isk->cmsg_flags)
+ ip_cmsg_recv(msg, skb);
+
+#if IS_ENABLED(CONFIG_IPV6)
+ } else if (family == AF_INET6) {
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ struct ipv6hdr *ip6 = ipv6_hdr(skb);
+ sin6 = (struct sockaddr_in6 *) msg->msg_name;
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = 0;
+ sin6->sin6_addr = ip6->saddr;
+
+ if (np->sndflow)
+ sin6->sin6_flowinfo =
+ *(__be32 *)ip6 & IPV6_FLOWINFO_MASK;
+
+ if (__ipv6_addr_needs_scope_id(
+ ipv6_addr_type(&sin6->sin6_addr)))
+ sin6->sin6_scope_id = IP6CB(skb)->iif;
+
+ if (inet6_sk(sk)->rxopt.all)
+ pingv6_ops.datagram_recv_ctl(sk, msg, skb);
+#endif
+ } else {
+ BUG();
}
- if (isk->cmsg_flags)
- ip_cmsg_recv(msg, skb);
+
err = copied;
done:
@@ -674,8 +919,9 @@
pr_debug("ping_recvmsg -> %d\n", err);
return err;
}
+EXPORT_SYMBOL_GPL(ping_recvmsg);
-static int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
+int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
pr_debug("ping_queue_rcv_skb(sk=%p,sk->num=%d,skb=%p)\n",
inet_sk(sk), inet_sk(sk)->inet_num, skb);
@@ -686,6 +932,7 @@
}
return 0;
}
+EXPORT_SYMBOL_GPL(ping_queue_rcv_skb);
/*
@@ -696,10 +943,7 @@
{
struct sock *sk;
struct net *net = dev_net(skb->dev);
- struct iphdr *iph = ip_hdr(skb);
struct icmphdr *icmph = icmp_hdr(skb);
- __be32 saddr = iph->saddr;
- __be32 daddr = iph->daddr;
/* We assume the packet has already been checked by icmp_rcv */
@@ -709,8 +953,7 @@
/* Push ICMP header back */
skb_push(skb, skb->data - (u8 *)icmph);
- sk = ping_v4_lookup(net, saddr, daddr, ntohs(icmph->un.echo.id),
- skb->dev->ifindex);
+ sk = ping_lookup(net, skb, ntohs(icmph->un.echo.id));
if (sk != NULL) {
pr_debug("rcv on socket %p\n", sk);
ping_queue_rcv_skb(sk, skb_get(skb));
@@ -721,6 +964,7 @@
/* We're called from icmp_rcv(). kfree_skb() is done there. */
}
+EXPORT_SYMBOL_GPL(ping_rcv);
struct proto ping_prot = {
.name = "PING",
@@ -731,13 +975,13 @@
.disconnect = udp_disconnect,
.setsockopt = ip_setsockopt,
.getsockopt = ip_getsockopt,
- .sendmsg = ping_sendmsg,
+ .sendmsg = ping_v4_sendmsg,
.recvmsg = ping_recvmsg,
.bind = ping_bind,
.backlog_rcv = ping_queue_rcv_skb,
- .hash = ping_v4_hash,
- .unhash = ping_v4_unhash,
- .get_port = ping_v4_get_port,
+ .hash = ping_hash,
+ .unhash = ping_unhash,
+ .get_port = ping_get_port,
.obj_size = sizeof(struct inet_sock),
};
EXPORT_SYMBOL(ping_prot);
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index 686934a..753be5d 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -7,7 +7,7 @@
ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \
addrlabel.o \
route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \
- raw.o protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
+ raw.o protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o ping.o \
exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o
ipv6-$(CONFIG_SYSCTL) = sysctl_net_ipv6.o
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 29625e9..22ebbb9 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -49,6 +49,7 @@
#include <net/udplite.h>
#include <net/tcp.h>
#include <net/ipip.h>
+#include <net/ping.h>
#include <net/protocol.h>
#include <net/inet_common.h>
#include <net/route.h>
@@ -1130,6 +1131,9 @@
if (err)
goto out_unregister_udplite_proto;
+ err = proto_register(&pingv6_prot, 1);
+ if (err)
+ goto out_unregister_ping_proto;
/* We MUST register RAW sockets before we create the ICMP6,
* IGMP6, or NDISC control sockets.
@@ -1225,6 +1229,10 @@
if (err)
goto ipv6_packet_fail;
+ err = pingv6_init();
+ if (err)
+ goto pingv6_fail;
+
#ifdef CONFIG_SYSCTL
err = ipv6_sysctl_register();
if (err)
@@ -1237,6 +1245,8 @@
sysctl_fail:
ipv6_packet_cleanup();
#endif
+pingv6_fail:
+ pingv6_exit();
ipv6_packet_fail:
tcpv6_exit();
tcpv6_fail:
@@ -1284,6 +1294,8 @@
rtnl_unregister_all(PF_INET6);
out_sock_register_fail:
rawv6_exit();
+out_unregister_ping_proto:
+ proto_unregister(&pingv6_prot);
out_unregister_raw_proto:
proto_unregister(&rawv6_prot);
out_unregister_udplite_proto:
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 27ac95a..ba0c147 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -55,6 +55,7 @@
#include <net/ipv6.h>
#include <net/ip6_checksum.h>
+#include <net/ping.h>
#include <net/protocol.h>
#include <net/raw.h>
#include <net/rawv6.h>
@@ -79,10 +80,22 @@
return net->ipv6.icmp_sk[smp_processor_id()];
}
+static void icmpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+ u8 type, u8 code, int offset, __be32 info)
+{
+ /* icmpv6_notify checks 8 bytes can be pulled, icmp6hdr is 8 bytes */
+ struct icmp6hdr *icmp6 = (struct icmp6hdr *) (skb->data + offset);
+
+ if (!(type & ICMPV6_INFOMSG_MASK))
+ if (icmp6->icmp6_type == ICMPV6_ECHO_REQUEST)
+ ping_err(skb, offset, info);
+}
+
static int icmpv6_rcv(struct sk_buff *skb);
static const struct inet6_protocol icmpv6_protocol = {
.handler = icmpv6_rcv,
+ .err_handler = icmpv6_err,
.flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
};
@@ -217,7 +230,8 @@
return (*op & 0xC0) == 0x80;
}
-static int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, struct icmp6hdr *thdr, int len)
+int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6,
+ struct icmp6hdr *thdr, int len)
{
struct sk_buff *skb;
struct icmp6hdr *icmp6h;
@@ -300,8 +314,8 @@
static inline void mip6_addr_swap(struct sk_buff *skb) {}
#endif
-static struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *skb,
- struct sock *sk, struct flowi6 *fl6)
+struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *skb,
+ struct sock *sk, struct flowi6 *fl6)
{
struct dst_entry *dst, *dst2;
struct flowi6 fl2;
@@ -594,7 +608,7 @@
icmpv6_xmit_unlock(sk);
}
-static void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info)
+void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info)
{
const struct inet6_protocol *ipprot;
int inner_offset;
@@ -687,7 +701,8 @@
skb->csum = ~csum_unfold(csum_ipv6_magic(saddr, daddr, skb->len,
IPPROTO_ICMPV6, 0));
if (__skb_checksum_complete(skb)) {
- LIMIT_NETDEBUG(KERN_DEBUG "ICMPv6 checksum failed [%pI6 > %pI6]\n",
+ LIMIT_NETDEBUG(KERN_DEBUG
+ "ICMPv6 checksum failed [%pI6c > %pI6c]\n",
saddr, daddr);
goto discard_it;
}
@@ -708,7 +723,7 @@
break;
case ICMPV6_ECHO_REPLY:
- /* we couldn't care less */
+ ping_rcv(skb);
break;
case ICMPV6_PKT_TOOBIG:
diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c
new file mode 100644
index 0000000..f46e315
--- /dev/null
+++ b/net/ipv6/ping.c
@@ -0,0 +1,222 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * "Ping" sockets
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Based on ipv4/ping.c code.
+ *
+ * Authors: Lorenzo Colitti (IPv6 support)
+ * Vasiliy Kulikov / Openwall (IPv4 implementation, for Linux 2.6),
+ * Pavel Kankovsky (IPv4 implementation, for Linux 2.4.32)
+ *
+ */
+
+#include <net/addrconf.h>
+#include <net/ipv6.h>
+#include <net/ip6_route.h>
+#include <net/protocol.h>
+#include <net/udp.h>
+#include <net/transp_v6.h>
+#include <net/ping.h>
+#include <linux/module.h>
+
+struct proto pingv6_prot = {
+ .name = "PINGv6",
+ .owner = THIS_MODULE,
+ .init = ping_init_sock,
+ .close = ping_close,
+ .connect = ip6_datagram_connect,
+ .disconnect = udp_disconnect,
+ .setsockopt = ipv6_setsockopt,
+ .getsockopt = ipv6_getsockopt,
+ .sendmsg = ping_v6_sendmsg,
+ .recvmsg = ping_recvmsg,
+ .bind = ping_bind,
+ .backlog_rcv = ping_queue_rcv_skb,
+ .hash = ping_hash,
+ .unhash = ping_unhash,
+ .get_port = ping_get_port,
+ .obj_size = sizeof(struct raw6_sock),
+};
+EXPORT_SYMBOL_GPL(pingv6_prot);
+
+static struct inet_protosw pingv6_protosw = {
+ .type = SOCK_DGRAM,
+ .protocol = IPPROTO_ICMPV6,
+ .prot = &pingv6_prot,
+ .ops = &inet6_dgram_ops,
+ .no_check = UDP_CSUM_DEFAULT,
+ .flags = INET_PROTOSW_REUSE,
+};
+
+
+/* Compatibility glue so we can support IPv6 when it's compiled as a module */
+int dummy_ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len)
+{
+ return -EAFNOSUPPORT;
+}
+int dummy_datagram_recv_ctl(struct sock *sk, struct msghdr *msg,
+ struct sk_buff *skb)
+{
+ return -EAFNOSUPPORT;
+}
+int dummy_icmpv6_err_convert(u8 type, u8 code, int *err)
+{
+ return -EAFNOSUPPORT;
+}
+void dummy_ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
+ __be16 port, u32 info, u8 *payload) {}
+int dummy_ipv6_chk_addr(struct net *net, const struct in6_addr *addr,
+ struct net_device *dev, int strict)
+{
+ return 0;
+}
+
+int __init pingv6_init(void)
+{
+ pingv6_ops.ipv6_recv_error = ipv6_recv_error;
+ pingv6_ops.datagram_recv_ctl = datagram_recv_ctl;
+ pingv6_ops.icmpv6_err_convert = icmpv6_err_convert;
+ pingv6_ops.ipv6_icmp_error = ipv6_icmp_error;
+ pingv6_ops.ipv6_chk_addr = ipv6_chk_addr;
+ return inet6_register_protosw(&pingv6_protosw);
+}
+
+/* This never gets called because it's not possible to unload the ipv6 module,
+ * but just in case.
+ */
+void pingv6_exit(void)
+{
+ pingv6_ops.ipv6_recv_error = dummy_ipv6_recv_error;
+ pingv6_ops.datagram_recv_ctl = dummy_datagram_recv_ctl;
+ pingv6_ops.icmpv6_err_convert = dummy_icmpv6_err_convert;
+ pingv6_ops.ipv6_icmp_error = dummy_ipv6_icmp_error;
+ pingv6_ops.ipv6_chk_addr = dummy_ipv6_chk_addr;
+ inet6_unregister_protosw(&pingv6_protosw);
+}
+
+int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+ size_t len)
+{
+ struct inet_sock *inet = inet_sk(sk);
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ struct icmp6hdr user_icmph;
+ int addr_type;
+ struct in6_addr *daddr;
+ int iif = 0;
+ struct flowi6 fl6;
+ int err;
+ int hlimit;
+ struct dst_entry *dst;
+ struct rt6_info *rt;
+ struct pingfakehdr pfh;
+
+ pr_debug("ping_v6_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num);
+
+ err = ping_common_sendmsg(AF_INET6, msg, len, &user_icmph,
+ sizeof(user_icmph));
+ if (err)
+ return err;
+
+ if (msg->msg_name) {
+ struct sockaddr_in6 *u = (struct sockaddr_in6 *) msg->msg_name;
+ if (msg->msg_namelen < sizeof(struct sockaddr_in6) ||
+ u->sin6_family != AF_INET6) {
+ return -EINVAL;
+ }
+ if (sk->sk_bound_dev_if &&
+ sk->sk_bound_dev_if != u->sin6_scope_id) {
+ return -EINVAL;
+ }
+ daddr = &(u->sin6_addr);
+ iif = u->sin6_scope_id;
+ } else {
+ if (sk->sk_state != TCP_ESTABLISHED)
+ return -EDESTADDRREQ;
+ daddr = &np->daddr;
+ }
+
+ if (!iif)
+ iif = sk->sk_bound_dev_if;
+
+ addr_type = ipv6_addr_type(daddr);
+ if (__ipv6_addr_needs_scope_id(addr_type) && !iif)
+ return -EINVAL;
+ if (addr_type & IPV6_ADDR_MAPPED)
+ return -EINVAL;
+
+ /* TODO: use ip6_datagram_send_ctl to get options from cmsg */
+
+ memset(&fl6, 0, sizeof(fl6));
+
+ fl6.flowi6_proto = IPPROTO_ICMPV6;
+ fl6.saddr = np->saddr;
+ fl6.daddr = *daddr;
+ fl6.fl6_icmp_type = user_icmph.icmp6_type;
+ fl6.fl6_icmp_code = user_icmph.icmp6_code;
+ security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
+
+ if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
+ fl6.flowi6_oif = np->mcast_oif;
+ else if (!fl6.flowi6_oif)
+ fl6.flowi6_oif = np->ucast_oif;
+
+ dst = ip6_sk_dst_lookup_flow(sk, &fl6, daddr, 1);
+ if (IS_ERR(dst))
+ return PTR_ERR(dst);
+ rt = (struct rt6_info *) dst;
+
+ np = inet6_sk(sk);
+ if (!np)
+ return -EBADF;
+
+ if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
+ fl6.flowi6_oif = np->mcast_oif;
+ else if (!fl6.flowi6_oif)
+ fl6.flowi6_oif = np->ucast_oif;
+
+ pfh.icmph.type = user_icmph.icmp6_type;
+ pfh.icmph.code = user_icmph.icmp6_code;
+ pfh.icmph.checksum = 0;
+ pfh.icmph.un.echo.id = inet->inet_sport;
+ pfh.icmph.un.echo.sequence = user_icmph.icmp6_sequence;
+ pfh.iov = msg->msg_iov;
+ pfh.wcheck = 0;
+ pfh.family = AF_INET6;
+
+ if (ipv6_addr_is_multicast(&fl6.daddr))
+ hlimit = np->mcast_hops;
+ else
+ hlimit = np->hop_limit;
+ if (hlimit < 0)
+ hlimit = ip6_dst_hoplimit(dst);
+
+ lock_sock(sk);
+ err = ip6_append_data(sk, ping_getfrag, &pfh, len,
+ 0, hlimit,
+ np->tclass, NULL, &fl6, rt,
+ MSG_DONTWAIT, np->dontfrag);
+
+ if (err) {
+ ICMP6_INC_STATS_BH(sock_net(sk), rt->rt6i_idev,
+ ICMP6_MIB_OUTERRORS);
+ ip6_flush_pending_frames(sk);
+ } else {
+ err = icmpv6_push_pending_frames(sk, &fl6,
+ (struct icmp6hdr *) &pfh.icmph,
+ len);
+ }
+ release_sock(sk);
+
+ if (err)
+ return err;
+
+ return len;
+}
diff --git a/net/wireless/db.txt b/net/wireless/db.txt
index 35b86ff..0e44fa8 100644
--- a/net/wireless/db.txt
+++ b/net/wireless/db.txt
@@ -14,14 +14,6 @@
(57240 - 63720 @ 2160), (N/A, 0)
-country AD:
- (2402 - 2482 @ 40), (N/A, 20)
- (5170 - 5250 @ 80), (N/A, 20)
- (5250 - 5330 @ 80), (N/A, 20), DFS
- (5490 - 5710 @ 80), (N/A, 27), DFS
- # 60 gHz band channels 1-4, ref: Etsi En 302 567
- (57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
-
country AE:
(2402 - 2482 @ 40), (N/A, 20)
(5170 - 5250 @ 80), (3, 17)
@@ -126,11 +118,6 @@
(5250 - 5330 @ 20), (N/A, 20), DFS
(5735 - 5835 @ 20), (N/A, 20)
-country BL:
- (2402 - 2482 @ 40), (N/A, 20)
- (5170 - 5250 @ 40), (N/A, 18)
- (5250 - 5330 @ 40), (N/A, 18), DFS
-
country BM:
(2402 - 2472 @ 40), (N/A, 30)
(5150 - 5250 @ 80), (6, 17)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 7db02f3..a251231 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -223,6 +223,8 @@
[NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_DATA_LEN },
[NL80211_ATTR_PEER_AID] = { .type = NLA_U16 },
+ [NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY },
+ [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY },
};
/* policy for the key attributes */
@@ -2774,6 +2776,41 @@
return ERR_PTR(ret);
}
+static int nl80211_parse_sta_channel_info(struct genl_info *info,
+ struct station_parameters *params)
+{
+ if (info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]) {
+ params->supported_channels =
+ nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]);
+ params->supported_channels_len =
+ nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]);
+ /*
+ * Need to include at least one (first channel, number of
+ * channels) tuple for each subband, and must have proper
+ * tuples for the rest of the data as well.
+ */
+ if (params->supported_channels_len < 2)
+ return -EINVAL;
+ if (params->supported_channels_len % 2)
+ return -EINVAL;
+ }
+
+ if (info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]) {
+ params->supported_oper_classes =
+ nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]);
+ params->supported_oper_classes_len =
+ nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]);
+ /*
+ * The value of the Length field of the Supported Operating
+ * Classes element is between 2 and 253.
+ */
+ if (params->supported_oper_classes_len < 2 ||
+ params->supported_oper_classes_len > 253)
+ return -EINVAL;
+ }
+ return 0;
+}
+
static struct nla_policy
nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = {
[NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
@@ -2797,6 +2834,10 @@
params->vht_capa =
nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
+ err = nl80211_parse_sta_channel_info(info, params);
+ if (err)
+ return err;
+
/* parse WME attributes if present */
if (!info->attrs[NL80211_ATTR_STA_WME])
return 0;
@@ -3063,6 +3104,10 @@
params.plink_action =
nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
+ err = nl80211_parse_sta_channel_info(info, ¶ms);
+ if (err)
+ return err;
+
if (!rdev->ops->add_station)
return -EOPNOTSUPP;
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
index a9227db..fd6b813 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.c
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -3102,16 +3102,15 @@
if (wcd9xxx_cancel_btn_work(mbhc))
pr_debug("%s: button press is canceled\n", __func__);
- /* cancel detect plug */
- wcd9xxx_cancel_hs_detect_plug(mbhc,
- &mbhc->correct_plug_swch);
-
insert = !wcd9xxx_swch_level_remove(mbhc);
pr_debug("%s: Current plug type %d, insert %d\n", __func__,
mbhc->current_plug, insert);
if ((mbhc->current_plug == PLUG_TYPE_NONE) && insert) {
mbhc->lpi_enabled = false;
wmb();
+ /* cancel detect plug */
+ wcd9xxx_cancel_hs_detect_plug(mbhc,
+ &mbhc->correct_plug_swch);
if ((mbhc->current_plug != PLUG_TYPE_NONE) &&
!(snd_soc_read(codec, WCD9XXX_A_MBHC_INSERT_DETECT) &
@@ -3126,6 +3125,9 @@
} else if ((mbhc->current_plug != PLUG_TYPE_NONE) && !insert) {
mbhc->lpi_enabled = false;
wmb();
+ /* cancel detect plug */
+ wcd9xxx_cancel_hs_detect_plug(mbhc,
+ &mbhc->correct_plug_swch);
if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
index 1b5b1a6..0feb8e1 100644
--- a/sound/soc/msm/msm8974.c
+++ b/sound/soc/msm/msm8974.c
@@ -1316,6 +1316,8 @@
SNDRV_PCM_HW_PARAM_CHANNELS);
pr_debug("%s()\n", __func__);
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ slim0_rx_bit_format);
rate->min = rate->max = 48000;
channels->min = channels->max = msm_slim_0_tx_ch;
diff --git a/sound/soc/msm/msm8x10.c b/sound/soc/msm/msm8x10.c
index f884259..7b3a028 100644
--- a/sound/soc/msm/msm8x10.c
+++ b/sound/soc/msm/msm8x10.c
@@ -287,6 +287,8 @@
SNDRV_PCM_HW_PARAM_CHANNELS);
pr_debug("%s(), channel:%d\n", __func__, msm_pri_mi2s_tx_ch);
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_mi2s_rx_bit_format);
rate->min = rate->max = 48000;
channels->min = channels->max = msm_pri_mi2s_tx_ch;
diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
index 6394f0b..41ef3e3 100644
--- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
@@ -49,8 +49,13 @@
#define PARTIAL_DRAIN_ACK_EARLY_BY_MSEC 150
#define MP3_OUTPUT_FRAME_SZ 1152
#define AAC_OUTPUT_FRAME_SZ 1024
+#define AC3_OUTPUT_FRAME_SZ 1536
+#define EAC3_OUTPUT_FRAME_SZ 1536
#define DSP_NUM_OUTPUT_FRAME_BUFFERED 2
+/* decoder parameter length */
+#define DDP_DEC_MAX_NUM_PARAM 18
+
/* Default values used if user space does not set */
#define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024)
#define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024)
@@ -61,9 +66,22 @@
const DECLARE_TLV_DB_LINEAR(msm_compr_vol_gain, 0,
COMPRESSED_LR_VOL_MAX_STEPS);
+/*
+ * LSB 8 bits is used as stream id for some DSP
+ * commands for compressed playback.
+ */
+#define STREAM_ID_FROM_TOKEN(i) (i & 0xFF)
+
+/* Stream id switches between 1 and 2 */
+#define NEXT_STREAM_ID(stream_id) ((stream_id & 1) + 1)
+
+#define STREAM_ARRAY_INDEX(stream_id) (stream_id - 1)
+
+#define MAX_NUMBER_OF_STREAMS 2
+
struct msm_compr_gapless_state {
bool set_next_stream_id;
- int32_t stream_opened[2];
+ int32_t stream_opened[MAX_NUMBER_OF_STREAMS];
uint32_t initial_samples_drop;
uint32_t trailing_samples_drop;
uint32_t gapless_transition;
@@ -76,6 +94,7 @@
uint32_t volume[MSM_FRONTEND_DAI_MAX][2]; /* For both L & R */
struct msm_compr_audio_effects *audio_effects[MSM_FRONTEND_DAI_MAX];
bool use_dsp_gapless_mode;
+ struct msm_compr_dec_params *dec_params[MSM_FRONTEND_DAI_MAX];
};
struct msm_compr_audio {
@@ -91,8 +110,10 @@
uint32_t app_pointer;
uint32_t buffer_size;
uint32_t byte_offset;
- uint32_t copied_total;
- uint32_t bytes_received;
+ uint32_t copied_total; /* bytes consumed by DSP */
+ uint32_t bytes_received; /* from userspace */
+ uint32_t bytes_sent; /* to DSP */
+
int32_t first_buffer;
int32_t last_buffer;
int32_t partial_drain_delay;
@@ -105,6 +126,8 @@
uint32_t cmd_ack;
uint32_t cmd_interrupt;
uint32_t drain_ready;
+ uint32_t stream_available;
+ uint32_t next_stream;
struct msm_compr_gapless_state gapless_state;
@@ -120,6 +143,7 @@
wait_queue_head_t drain_wait;
wait_queue_head_t flush_wait;
wait_queue_head_t close_wait;
+ wait_queue_head_t wait_for_stream_avail;
spinlock_t lock;
};
@@ -131,6 +155,10 @@
struct eq_params equalizer;
};
+struct msm_compr_dec_params {
+ struct snd_dec_ddp ddp_params;
+};
+
static int msm_compr_set_volume(struct snd_compr_stream *cstream,
uint32_t volume_l, uint32_t volume_r)
{
@@ -158,6 +186,23 @@
return rc;
}
+static int msm_compr_send_ddp_cfg(struct audio_client *ac,
+ struct snd_dec_ddp *ddp)
+{
+ int i, rc;
+ pr_debug("%s\n", __func__);
+ for (i = 0; i < ddp->params_length; i++) {
+ rc = q6asm_ds1_set_endp_params(ac, ddp->params_id[i],
+ ddp->params_value[i]);
+ if (rc) {
+ pr_err("sending params_id: %d failed\n",
+ ddp->params_id[i]);
+ return rc;
+ }
+ }
+ return 0;
+}
+
static int msm_compr_send_buffer(struct msm_compr_audio *prtd)
{
int buffer_length;
@@ -178,7 +223,8 @@
pr_debug("%s: bytes_received = %d copied_total = %d\n",
__func__, prtd->bytes_received, prtd->copied_total);
if (prtd->first_buffer && prtd->gapless_state.use_dsp_gapless_mode)
- q6asm_send_meta_data(prtd->audio_client,
+ q6asm_stream_send_meta_data(prtd->audio_client,
+ prtd->audio_client->stream_id,
prtd->gapless_state.initial_samples_drop,
prtd->gapless_state.trailing_samples_drop);
@@ -211,6 +257,7 @@
if (q6asm_async_write(prtd->audio_client, ¶m) < 0) {
pr_err("%s:q6asm_async_write failed\n", __func__);
} else {
+ prtd->bytes_sent += buffer_length;
if (prtd->first_buffer)
prtd->first_buffer = 0;
}
@@ -227,6 +274,7 @@
uint32_t chan_mode = 0;
uint32_t sample_rate = 0;
int bytes_available, stream_id;
+ uint32_t stream_index;
pr_debug("%s opcode =%08x\n", __func__, opcode);
switch (opcode) {
@@ -284,8 +332,10 @@
spin_unlock(&prtd->lock);
break;
case ASM_DATA_EVENT_RENDERED_EOS:
- pr_debug("ASM_DATA_CMDRSP_EOS\n");
spin_lock(&prtd->lock);
+ pr_debug("%s: ASM_DATA_CMDRSP_EOS token 0x%x,stream id %d\n",
+ __func__, token, STREAM_ID_FROM_TOKEN(token));
+ stream_id = STREAM_ID_FROM_TOKEN(token);
if (atomic_read(&prtd->eos) &&
!prtd->gapless_state.set_next_stream_id) {
pr_debug("ASM_DATA_CMDRSP_EOS wake up\n");
@@ -293,15 +343,26 @@
wake_up(&prtd->eos_wait);
}
atomic_set(&prtd->eos, 0);
- stream_id = ac->stream_id^1; /*prev stream */
+ stream_index = STREAM_ARRAY_INDEX(stream_id);
+ if (stream_index >= MAX_NUMBER_OF_STREAMS ||
+ stream_index < 0) {
+ pr_err("%s: Invalid stream index %d", __func__,
+ stream_index);
+ spin_unlock(&prtd->lock);
+ break;
+ }
+
if (prtd->gapless_state.set_next_stream_id &&
- prtd->gapless_state.stream_opened[stream_id]) {
- q6asm_stream_cmd_nowait(prtd->audio_client,
- CMD_CLOSE, stream_id);
+ prtd->gapless_state.stream_opened[stream_index]) {
+ pr_debug("%s: CMD_CLOSE stream_id %d\n",
+ __func__, stream_id);
+ q6asm_stream_cmd_nowait(ac, CMD_CLOSE, stream_id);
atomic_set(&prtd->close, 1);
- prtd->gapless_state.stream_opened[stream_id] = 0;
+ prtd->gapless_state.stream_opened[stream_index] = 0;
prtd->gapless_state.set_next_stream_id = false;
}
+ if (prtd->gapless_state.gapless_transition)
+ prtd->gapless_state.gapless_transition = 0;
spin_unlock(&prtd->lock);
break;
case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
@@ -323,7 +384,7 @@
spin_lock(&prtd->lock);
/* FIXME: A state is a much better way of dealing with this */
- if (!prtd->copied_total) {
+ if (prtd->bytes_sent == 0) {
bytes_available = prtd->bytes_received - prtd->copied_total;
if (bytes_available < cstream->runtime->fragment_size) {
pr_debug("CMD_RUN_V2 Insufficient data to send. break out\n");
@@ -334,18 +395,39 @@
spin_unlock(&prtd->lock);
break;
case ASM_STREAM_CMD_FLUSH:
- pr_debug("ASM_STREAM_CMD_FLUSH\n");
+ pr_debug("%s: ASM_STREAM_CMD_FLUSH:", __func__);
+ pr_debug("token 0x%x, stream id %d\n", token,
+ STREAM_ID_FROM_TOKEN(token));
prtd->cmd_ack = 1;
wake_up(&prtd->flush_wait);
break;
case ASM_DATA_CMD_REMOVE_INITIAL_SILENCE:
- pr_debug("ASM_DATA_CMD_REMOVE_INITIAL_SILENCE\n");
+ pr_debug("%s: ASM_DATA_CMD_REMOVE_INITIAL_SILENCE:",
+ __func__);
+ pr_debug("token 0x%x, stream id = %d\n", token,
+ STREAM_ID_FROM_TOKEN(token));
break;
case ASM_DATA_CMD_REMOVE_TRAILING_SILENCE:
- pr_debug("ASM_DATA_CMD_REMOVE_TRAILING_SILENCE\n");
+ pr_debug("%s: ASM_DATA_CMD_REMOVE_TRAILING_SILENCE:",
+ __func__);
+ pr_debug("token = 0x%x, stream id = %d\n", token,
+ STREAM_ID_FROM_TOKEN(token));
break;
case ASM_STREAM_CMD_CLOSE:
- pr_debug("ASM_DATA_CMD_CLOSE\n");
+ pr_debug("%s: ASM_DATA_CMD_CLOSE:", __func__);
+ pr_debug("token 0x%x, stream id %d\n", token,
+ STREAM_ID_FROM_TOKEN(token));
+ /*
+ * wakeup wait for stream avail on stream 3
+ * after stream 1 ends.
+ */
+ if (prtd->next_stream) {
+ pr_debug("%s:CLOSE:wakeup wait for stream\n",
+ __func__);
+ prtd->stream_available = 1;
+ wake_up(&prtd->wait_for_stream_avail);
+ prtd->next_stream = 0;
+ }
if (atomic_read(&prtd->close) &&
atomic_read(&prtd->wait_on_close)) {
prtd->cmd_ack = 1;
@@ -359,10 +441,12 @@
break;
}
case ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3:
- pr_debug("ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3\n");
+ pr_debug("%s: ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3\n",
+ __func__);
break;
case RESET_EVENTS:
- pr_err("Received reset events CB, move to error state");
+ pr_err("%s: Received reset events CB, move to error state",
+ __func__);
spin_lock(&prtd->lock);
snd_compr_fragment_elapsed(cstream);
prtd->copied_total = prtd->bytes_received;
@@ -370,7 +454,8 @@
spin_unlock(&prtd->lock);
break;
default:
- pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
+ pr_debug("%s: Not Supported Event opcode[0x%x]\n",
+ __func__, opcode);
break;
}
}
@@ -440,6 +525,7 @@
uint16_t bits_per_sample = 16;
int dir = IN, ret = 0;
struct audio_client *ac = prtd->audio_client;
+ uint32_t stream_index;
struct asm_softpause_params softpause = {
.enable = SOFT_PAUSE_ENABLE,
.period = SOFT_PAUSE_PERIOD,
@@ -452,7 +538,7 @@
.rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
};
- pr_debug("%s\n", __func__);
+ pr_debug("%s: stream_id %d\n", __func__, ac->stream_id);
ret = q6asm_stream_open_write_v2(ac,
prtd->codec, bits_per_sample,
ac->stream_id,
@@ -462,7 +548,13 @@
return -ENOMEM;
}
- prtd->gapless_state.stream_opened[ac->stream_id] = 1;
+ stream_index = STREAM_ARRAY_INDEX(ac->stream_id);
+ if (stream_index >= MAX_NUMBER_OF_STREAMS || stream_index < 0) {
+ pr_err("%s: Invalid stream index:%d", __func__, stream_index);
+ return -EINVAL;
+ }
+
+ prtd->gapless_state.stream_opened[stream_index] = 1;
pr_debug("%s be_id %d\n", __func__, soc_prtd->dai_link->be_id);
msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
ac->perf_mode,
@@ -483,7 +575,7 @@
pr_err("%s: Send SoftVolume Param failed ret=%d\n",
__func__, ret);
- ret = q6asm_set_io_mode(ac, (COMPRESSED_IO | ASYNC_IO_MODE));
+ ret = q6asm_set_io_mode(ac, (COMPRESSED_STREAM_IO | ASYNC_IO_MODE));
if (ret < 0) {
pr_err("%s: Set IO mode failed\n", __func__);
return -EINVAL;
@@ -506,6 +598,7 @@
prtd->copied_total = 0;
prtd->app_pointer = 0;
prtd->bytes_received = 0;
+ prtd->bytes_sent = 0;
prtd->buffer = ac->port[dir].buf[0].data;
prtd->buffer_paddr = ac->port[dir].buf[0].phys;
prtd->buffer_size = runtime->fragments * runtime->fragment_size;
@@ -541,11 +634,20 @@
kfree(prtd);
return -ENOMEM;
}
+ pdata->dec_params[rtd->dai_link->be_id] =
+ kzalloc(sizeof(struct msm_compr_dec_params), GFP_KERNEL);
+ if (!pdata->dec_params[rtd->dai_link->be_id]) {
+ pr_err("%s: Could not allocate memory for dec params\n",
+ __func__);
+ kfree(prtd);
+ return -ENOMEM;
+ }
prtd->audio_client = q6asm_audio_client_alloc(
(app_cb)compr_event_handler, prtd);
if (!prtd->audio_client) {
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]);
kfree(prtd);
return -ENOMEM;
}
@@ -555,6 +657,7 @@
prtd->session_id = prtd->audio_client->session;
prtd->codec = FORMAT_MP3;
prtd->bytes_received = 0;
+ prtd->bytes_sent = 0;
prtd->copied_total = 0;
prtd->byte_offset = 0;
prtd->sample_rate = 44100;
@@ -563,6 +666,7 @@
prtd->last_buffer = 0;
prtd->first_buffer = 1;
prtd->partial_drain_delay = 0;
+ prtd->next_stream = 0;
memset(&prtd->gapless_state, 0, sizeof(struct msm_compr_gapless_state));
/*
* Update the use_dsp_gapless_mode from gapless struture with the value
@@ -586,6 +690,7 @@
init_waitqueue_head(&prtd->drain_wait);
init_waitqueue_head(&prtd->flush_wait);
init_waitqueue_head(&prtd->close_wait);
+ init_waitqueue_head(&prtd->wait_for_stream_avail);
runtime->private_data = prtd;
populate_codec_list(prtd);
@@ -614,21 +719,9 @@
struct audio_client *ac = prtd->audio_client;
int dir = IN, ret = 0, stream_id;
unsigned long flags;
+ uint32_t stream_index;
pr_debug("%s\n", __func__);
- pdata->cstream[soc_prtd->dai_link->be_id] = NULL;
- if (cstream->direction == SND_COMPRESS_PLAYBACK) {
- if (atomic_read(&pdata->audio_ocmem_req) > 1)
- atomic_dec(&pdata->audio_ocmem_req);
- else if (atomic_cmpxchg(&pdata->audio_ocmem_req, 1, 0))
- audio_ocmem_process_req(AUDIO, false);
-
- msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
- SNDRV_PCM_STREAM_PLAYBACK);
- }
-
- pr_debug("%s: ocmem_req: %d\n", __func__,
- atomic_read(&pdata->audio_ocmem_req));
if (atomic_read(&prtd->eos)) {
ret = wait_event_timeout(prtd->eos_wait,
@@ -647,25 +740,44 @@
spin_lock_irqsave(&prtd->lock, flags);
stream_id = ac->stream_id;
- if (prtd->gapless_state.stream_opened[stream_id^1]) {
+ stream_index = STREAM_ARRAY_INDEX(NEXT_STREAM_ID(stream_id));
+
+ if ((stream_index < MAX_NUMBER_OF_STREAMS && stream_index >= 0) &&
+ (prtd->gapless_state.stream_opened[stream_index])) {
spin_unlock_irqrestore(&prtd->lock, flags);
- q6asm_stream_cmd(ac, CMD_CLOSE, stream_id^1);
+ pr_debug(" close stream %d", NEXT_STREAM_ID(stream_id));
+ q6asm_stream_cmd(ac, CMD_CLOSE, NEXT_STREAM_ID(stream_id));
spin_lock_irqsave(&prtd->lock, flags);
}
- if (prtd->gapless_state.stream_opened[stream_id]) {
+
+ stream_index = STREAM_ARRAY_INDEX(stream_id);
+ if ((stream_index < MAX_NUMBER_OF_STREAMS && stream_index >= 0) &&
+ (prtd->gapless_state.stream_opened[stream_index])) {
spin_unlock_irqrestore(&prtd->lock, flags);
+ pr_debug("close stream %d", stream_id);
q6asm_stream_cmd(ac, CMD_CLOSE, stream_id);
spin_lock_irqsave(&prtd->lock, flags);
}
spin_unlock_irqrestore(&prtd->lock, flags);
- /* client buf alloc was with stream id 0, so free with the same */
- ac->stream_id = 0;
+ pdata->cstream[soc_prtd->dai_link->be_id] = NULL;
+ if (cstream->direction == SND_COMPRESS_PLAYBACK) {
+ if (atomic_read(&pdata->audio_ocmem_req) > 1)
+ atomic_dec(&pdata->audio_ocmem_req);
+ else if (atomic_cmpxchg(&pdata->audio_ocmem_req, 1, 0))
+ audio_ocmem_process_req(AUDIO, false);
+ msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ }
+
+ pr_debug("%s: ocmem_req: %d\n", __func__,
+ atomic_read(&pdata->audio_ocmem_req));
q6asm_audio_client_buf_free_contiguous(dir, ac);
q6asm_audio_client_free(ac);
kfree(pdata->audio_effects[soc_prtd->dai_link->be_id]);
+ kfree(pdata->dec_params[soc_prtd->dai_link->be_id]);
kfree(prtd);
return 0;
@@ -730,11 +842,13 @@
case SND_AUDIOCODEC_AC3: {
prtd->codec = FORMAT_AC3;
+ frame_sz = AC3_OUTPUT_FRAME_SZ;
break;
}
case SND_AUDIOCODEC_EAC3: {
prtd->codec = FORMAT_EAC3;
+ frame_sz = EAC3_OUTPUT_FRAME_SZ;
break;
}
@@ -777,6 +891,45 @@
return rc;
}
+static int msm_compr_wait_for_stream_avail(struct msm_compr_audio *prtd,
+ unsigned long *flags)
+{
+ int rc = 0;
+ pr_debug("next session is already in opened state\n");
+ prtd->next_stream = 1;
+ prtd->cmd_interrupt = 0;
+ spin_unlock_irqrestore(&prtd->lock, *flags);
+ /*
+ * Wait for stream to be available, or the wait to be interrupted by
+ * commands like flush or till a timeout of one second.
+ */
+ rc = wait_event_timeout(prtd->wait_for_stream_avail,
+ prtd->stream_available || prtd->cmd_interrupt, 1 * HZ);
+ pr_err("%s:prtd->stream_available %d, prtd->cmd_interrupt %d rc %d\n",
+ __func__, prtd->stream_available, prtd->cmd_interrupt, rc);
+
+ spin_lock_irqsave(&prtd->lock, *flags);
+ if (rc == 0) {
+ pr_err("%s: wait_for_stream_avail timed out\n",
+ __func__);
+ rc = -ETIMEDOUT;
+ } else if (prtd->cmd_interrupt == 1) {
+ /*
+ * This scenario might not happen as we do not allow
+ * flush in transition state.
+ */
+ pr_debug("%s: wait_for_stream_avail interrupted\n", __func__);
+ prtd->cmd_interrupt = 0;
+ prtd->stream_available = 0;
+ rc = -EINTR;
+ } else {
+ prtd->stream_available = 0;
+ rc = 0;
+ }
+ pr_debug("%s : rc = %d", __func__, rc);
+ return rc;
+}
+
static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
{
struct snd_compr_runtime *runtime = cstream->runtime;
@@ -790,6 +943,7 @@
int bytes_to_write;
unsigned long flags;
int stream_id;
+ uint32_t stream_index;
if (cstream->direction != SND_COMPRESS_PLAYBACK) {
pr_err("%s: Unsupported stream type\n", __func__);
@@ -821,6 +975,13 @@
prtd->gapless_state.gapless_transition);
stream_id = ac->stream_id;
atomic_set(&prtd->start, 0);
+ if (prtd->next_stream) {
+ pr_debug("%s: interrupt next track wait queues\n",
+ __func__);
+ prtd->cmd_interrupt = 1;
+ wake_up(&prtd->wait_for_stream_avail);
+ prtd->next_stream = 0;
+ }
if (atomic_read(&prtd->eos)) {
pr_debug("%s: interrupt eos wait queues", __func__);
prtd->cmd_interrupt = 1;
@@ -835,9 +996,9 @@
atomic_set(&prtd->drain, 0);
}
prtd->last_buffer = 0;
- pr_debug("issue CMD_FLUSH\n");
prtd->cmd_ack = 0;
if (!prtd->gapless_state.gapless_transition) {
+ pr_debug("issue CMD_FLUSH stream_id %d\n", stream_id);
spin_unlock_irqrestore(&prtd->lock, flags);
rc = q6asm_stream_cmd(
prtd->audio_client, CMD_FLUSH, stream_id);
@@ -863,6 +1024,8 @@
prtd->copied_total = 0;
prtd->app_pointer = 0;
prtd->bytes_received = 0;
+ prtd->bytes_sent = 0;
+
atomic_set(&prtd->xrun, 0);
spin_unlock_irqrestore(&prtd->lock, flags);
break;
@@ -870,7 +1033,9 @@
pr_debug("SNDRV_PCM_TRIGGER_PAUSE_PUSH transition %d\n",
prtd->gapless_state.gapless_transition);
if (!prtd->gapless_state.gapless_transition) {
- q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+ pr_debug("issue CMD_PAUSE stream_id %d\n",
+ ac->stream_id);
+ q6asm_stream_cmd_nowait(ac, CMD_PAUSE, ac->stream_id);
atomic_set(&prtd->start, 0);
}
break;
@@ -946,7 +1111,8 @@
/* send EOS */
prtd->cmd_ack = 0;
- q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
+ pr_debug("issue CMD_EOS stream_id %d\n", ac->stream_id);
+ q6asm_stream_cmd_nowait(ac, CMD_EOS, ac->stream_id);
pr_info("PARTIAL DRAIN, do not wait for EOS ack\n");
/* send a zero length buffer */
@@ -983,8 +1149,9 @@
}
/* move to next stream and reset vars */
- pr_debug("%s: Moving to next stream in gapless\n", __func__);
- ac->stream_id ^= 1;
+ pr_debug("%s: Moving to next stream in gapless\n",
+ __func__);
+ ac->stream_id = NEXT_STREAM_ID(ac->stream_id);
prtd->byte_offset = 0;
prtd->app_pointer = 0;
prtd->first_buffer = 1;
@@ -1011,11 +1178,11 @@
stream can be used for gapless playback
*/
prtd->gapless_state.set_next_stream_id = false;
- pr_debug("%s: CMD_EOS\n", __func__);
+ pr_debug("%s:CMD_EOS stream_id %d\n", __func__, ac->stream_id);
prtd->cmd_ack = 0;
atomic_set(&prtd->eos, 1);
- q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
+ q6asm_stream_cmd_nowait(ac, CMD_EOS, ac->stream_id);
spin_unlock_irqrestore(&prtd->lock, flags);
@@ -1027,7 +1194,8 @@
if (rc < 0)
pr_err("%s: EOS wait failed\n", __func__);
- pr_debug("%s: SNDRV_COMPRESS_DRAIN out of wait for EOS\n", __func__);
+ pr_debug("%s: SNDRV_COMPRESS_DRAIN out of wait for EOS\n",
+ __func__);
if (prtd->cmd_interrupt)
rc = -EINTR;
@@ -1039,12 +1207,14 @@
* so prepare the current stream in session for gapless playback
*/
spin_lock_irqsave(&prtd->lock, flags);
- pr_debug("%s: issue CMD_PAUSE ", __func__);
- q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+ pr_debug("%s:issue CMD_PAUSE stream_id %d",
+ __func__, ac->stream_id);
+ q6asm_stream_cmd_nowait(ac, CMD_PAUSE, ac->stream_id);
prtd->cmd_ack = 0;
spin_unlock_irqrestore(&prtd->lock, flags);
- pr_debug("%s: issue CMD_FLUSH", __func__);
- q6asm_cmd(prtd->audio_client, CMD_FLUSH);
+ pr_debug("%s:issue CMD_FLUSH ac->stream_id %d",
+ __func__, ac->stream_id);
+ q6asm_stream_cmd(ac, CMD_FLUSH, ac->stream_id);
wait_event_timeout(prtd->flush_wait,
prtd->cmd_ack, 1 * HZ / 4);
@@ -1056,8 +1226,11 @@
in the next avail() ioctl
prtd->copied_total = 0;
prtd->bytes_received = 0;
+ do not reset prtd->bytes_sent as well as the same
+ session is used for gapless playback
*/
prtd->byte_offset = 0;
+
prtd->app_pointer = 0;
prtd->first_buffer = 1;
prtd->last_buffer = 0;
@@ -1077,13 +1250,56 @@
pr_debug("%s: SND_COMPR_TRIGGER_NEXT_TRACK\n", __func__);
spin_lock_irqsave(&prtd->lock, flags);
rc = 0;
- stream_id = ac->stream_id^1; /*next stream in gapless*/
- if (prtd->gapless_state.stream_opened[stream_id]) {
- pr_debug("next session is already in opened state\n");
+ /* next stream in gapless */
+ stream_id = NEXT_STREAM_ID(ac->stream_id);
+ /*
+ * Wait if stream 1 has not completed before honoring next
+ * track for stream 3. Scenario happens if second clip is
+ * small and fills in one buffer so next track will be
+ * called immediately.
+ */
+ stream_index = STREAM_ARRAY_INDEX(stream_id);
+ if (stream_index >= MAX_NUMBER_OF_STREAMS ||
+ stream_index < 0) {
+ pr_err("%s: Invalid stream index: %d", __func__,
+ stream_index);
spin_unlock_irqrestore(&prtd->lock, flags);
+ rc = -EINVAL;
break;
}
+
+ if (prtd->gapless_state.stream_opened[stream_index]) {
+ if (prtd->gapless_state.gapless_transition) {
+ rc = msm_compr_wait_for_stream_avail(prtd,
+ &flags);
+ } else {
+ /*
+ * If session is already opened break out if
+ * the state is not gapless transition. This
+ * is when seek happens after the last buffer
+ * is sent to the driver. Next track would be
+ * called again after last buffer is sent.
+ */
+ pr_debug("next session is in opened state\n");
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+ }
+ }
spin_unlock_irqrestore(&prtd->lock, flags);
+ if (rc < 0) {
+ /*
+ * if return type EINTR then reset to zero. Tiny
+ * compress treats EINTR as error and prevents PARTIAL
+ * DRAIN. EINTR is not an error. wait for stream avail
+ * is interrupted by some other command like FLUSH.
+ */
+ if (rc == -EINTR) {
+ pr_debug("%s: EINTR reset rc to 0\n", __func__);
+ rc = 0;
+ }
+ break;
+ }
+ pr_debug("%s: open_write stream_id %d", __func__, stream_id);
rc = q6asm_stream_open_write_v2(prtd->audio_client,
prtd->codec, 16,
stream_id,
@@ -1100,7 +1316,7 @@
break;
}
spin_lock_irqsave(&prtd->lock, flags);
- prtd->gapless_state.stream_opened[stream_id] = 1;
+ prtd->gapless_state.stream_opened[stream_index] = 1;
prtd->gapless_state.set_next_stream_id = true;
spin_unlock_irqrestore(&prtd->lock, flags);
break;
@@ -1251,8 +1467,6 @@
* since the available bytes fits fragment_size, copy the data right away
*/
spin_lock_irqsave(&prtd->lock, flags);
- if (prtd->gapless_state.gapless_transition)
- prtd->gapless_state.gapless_transition = 0;
prtd->bytes_received += count;
if (atomic_read(&prtd->start)) {
if (atomic_read(&prtd->xrun)) {
@@ -1474,6 +1688,74 @@
return 0;
}
+static int msm_compr_dec_params_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_platform_get_drvdata(platform);
+ struct msm_compr_dec_params *dec_params = NULL;
+ struct snd_compr_stream *cstream = NULL;
+ struct msm_compr_audio *prtd = NULL;
+ long *values = &(ucontrol->value.integer.value[0]);
+
+ pr_debug("%s\n", __func__);
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received out of bounds fe_id %lu\n",
+ __func__, fe_id);
+ return -EINVAL;
+ }
+
+ cstream = pdata->cstream[fe_id];
+ dec_params = pdata->dec_params[fe_id];
+
+ if (!cstream || !dec_params) {
+ pr_err("%s: stream or dec_params inactive\n", __func__);
+ return -EINVAL;
+ }
+ prtd = cstream->runtime->private_data;
+ if (!prtd) {
+ pr_err("%s: cannot set dec_params\n", __func__);
+ return -EINVAL;
+ }
+ switch (prtd->codec) {
+ case FORMAT_MP3:
+ case FORMAT_MPEG4_AAC:
+ pr_debug("%s: no runtime parameters for codec: %d\n", __func__,
+ prtd->codec);
+ break;
+ case FORMAT_AC3:
+ case FORMAT_EAC3: {
+ struct snd_dec_ddp *ddp = &dec_params->ddp_params;
+ int cnt;
+ ddp->params_length = (*values++);
+ if (ddp->params_length > DDP_DEC_MAX_NUM_PARAM) {
+ pr_err("%s: invalid num of params:: %d\n", __func__,
+ ddp->params_length);
+ return -EINVAL;
+ }
+ for (cnt = 0; cnt < ddp->params_length; cnt++) {
+ ddp->params_id[cnt] = *values++;
+ ddp->params_value[cnt] = *values++;
+ }
+ if (msm_compr_send_ddp_cfg(prtd->audio_client, ddp) < 0)
+ pr_err("%s: DDP CMD CFG failed\n", __func__);
+ break;
+ }
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int msm_compr_dec_params_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ /* dummy function */
+ return 0;
+}
+
static int msm_compr_probe(struct snd_soc_platform *platform)
{
struct msm_compr_pdata *pdata;
@@ -1493,6 +1775,7 @@
pdata->volume[i][0] = COMPRESSED_LR_VOL_MAX_STEPS;
pdata->volume[i][1] = COMPRESSED_LR_VOL_MAX_STEPS;
pdata->audio_effects[i] = NULL;
+ pdata->dec_params[i] = NULL;
pdata->cstream[i] = NULL;
}
@@ -1526,6 +1809,16 @@
return 0;
}
+static int msm_compr_dec_params_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 128;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xFFFFFFFF;
+ return 0;
+}
+
static int msm_compr_add_volume_control(struct snd_soc_pcm_runtime *rtd)
{
const char *mixer_ctl_name = "Compress Playback";
@@ -1652,6 +1945,57 @@
msm_compr_gapless_put),
};
+static int msm_compr_add_dec_runtime_params_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = "Audio Stream";
+ const char *deviceNo = "NN";
+ const char *suffix = "Dec Params";
+ char *mixer_str = NULL;
+ int ctl_len;
+ struct snd_kcontrol_new fe_dec_params_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_compr_dec_params_info,
+ .get = msm_compr_dec_params_get,
+ .put = msm_compr_dec_params_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ return 0;
+ }
+
+ pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n",
+ __func__, rtd->dai_link->name, rtd->dai_link->be_id,
+ rtd->dai_link->cpu_dai_name, rtd->pcm->device);
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1 +
+ strlen(suffix) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+
+ if (!mixer_str) {
+ pr_err("failed to allocate mixer ctrl str of len %d", ctl_len);
+ return 0;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d %s", mixer_ctl_name,
+ rtd->pcm->device, suffix);
+
+ fe_dec_params_control[0].name = mixer_str;
+ fe_dec_params_control[0].private_value = rtd->dai_link->be_id;
+ pr_debug("Registering new mixer ctl %s", mixer_str);
+ snd_soc_add_platform_controls(rtd->platform,
+ fe_dec_params_control,
+ ARRAY_SIZE(fe_dec_params_control));
+ kfree(mixer_str);
+ return 0;
+}
+
static int msm_compr_new(struct snd_soc_pcm_runtime *rtd)
{
int rc;
@@ -1663,6 +2007,10 @@
if (rc)
pr_err("%s: Could not add Compr Audio Effects Control\n",
__func__);
+ rc = msm_compr_add_dec_runtime_params_control(rtd);
+ if (rc)
+ pr_err("%s: Could not add Compr Dec runtime params Control\n",
+ __func__);
return 0;
}
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
index 91c0744..2c001fa 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -2784,6 +2784,7 @@
int msm_routing_get_rms_value_control(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) {
int rc = 0;
+ int be_idx = 0;
char *param_value;
int *update_param_value;
uint32_t param_length = sizeof(uint32_t);
@@ -2793,21 +2794,26 @@
pr_err("%s, param memory alloc failed\n", __func__);
return -ENOMEM;
}
- rc = adm_get_params(SLIMBUS_0_TX,
- RMS_MODULEID_APPI_PASSTHRU,
- RMS_PARAM_FIRST_SAMPLE,
- param_length + param_payload_len,
- param_value);
- if (rc) {
- pr_err("%s: get parameters failed\n", __func__);
- kfree(param_value);
- return -EINVAL;
- }
- update_param_value = (int *)param_value;
- ucontrol->value.integer.value[0] = update_param_value[0];
+ for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++)
+ if (msm_bedais[be_idx].port_id == SLIMBUS_0_TX)
+ break;
+ if ((be_idx < MSM_BACKEND_DAI_MAX) && msm_bedais[be_idx].active) {
+ rc = adm_get_params(SLIMBUS_0_TX,
+ RMS_MODULEID_APPI_PASSTHRU,
+ RMS_PARAM_FIRST_SAMPLE,
+ param_length + param_payload_len,
+ param_value);
+ if (rc) {
+ pr_err("%s: get parameters failed\n", __func__);
+ kfree(param_value);
+ return -EINVAL;
+ }
+ update_param_value = (int *)param_value;
+ ucontrol->value.integer.value[0] = update_param_value[0];
- pr_debug("%s: FROM DSP value[0] 0x%x\n",
- __func__, update_param_value[0]);
+ pr_debug("%s: FROM DSP value[0] 0x%x\n",
+ __func__, update_param_value[0]);
+ }
kfree(param_value);
return 0;
}
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c
index 97c7b5d..e3c8944 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.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
@@ -86,6 +86,10 @@
struct voip_frame_hdr {
uint32_t timestamp;
union {
+ /*
+ * Bits 0-15: Frame type
+ * Bits 16-31: Frame rate
+ */
uint32_t frame_type;
uint32_t packet_rate;
};
@@ -162,8 +166,6 @@
struct snd_ctl_elem_value *ucontrol);
static int msm_voip_rate_config_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
-static int msm_voip_rate_config_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
static int msm_voip_evrc_min_max_rate_config_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
static int msm_voip_evrc_min_max_rate_config_get(struct snd_kcontrol *kcontrol,
@@ -277,7 +279,7 @@
SOC_SINGLE_EXT("Voip Mode Config", SND_SOC_NOPM, 0, VOIP_MODE_MAX, 0,
msm_voip_mode_config_get, msm_voip_mode_config_put),
SOC_SINGLE_EXT("Voip Rate Config", SND_SOC_NOPM, 0, VOIP_RATE_MAX, 0,
- msm_voip_rate_config_get, msm_voip_rate_config_put),
+ NULL, msm_voip_rate_config_put),
SOC_SINGLE_MULTI_EXT("Voip Evrc Min Max Rate Config", SND_SOC_NOPM,
0, VOC_1_RATE, 0, 2, msm_voip_evrc_min_max_rate_config_get,
msm_voip_evrc_min_max_rate_config_put),
@@ -385,6 +387,8 @@
struct voip_buf_node *buf_node = NULL;
struct voip_drv_info *prtd = private_data;
unsigned long dsp_flags;
+ uint32_t rate_type;
+ uint32_t frame_rate;
if (prtd->playback_substream == NULL)
return;
@@ -408,7 +412,19 @@
* Bits 4-7: Frame type
*/
*voc_pkt = ((buf_node->frame.frm_hdr.frame_type &
- 0x0F) << 4) | (prtd->rate_type & 0x0F);
+ 0x0F) << 4);
+ frame_rate = (buf_node->frame.frm_hdr.frame_type &
+ 0xFFFF0000) >> 16;
+ if (frame_rate) {
+ if (voip_get_rate_type(prtd->mode, frame_rate,
+ &rate_type)) {
+ pr_err("%s(): fail at getting rate_type \n",
+ __func__);
+ } else
+ prtd->rate_type = rate_type;
+ }
+ *voc_pkt |= prtd->rate_type & 0x0F;
+
voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
memcpy(voc_pkt,
&buf_node->frame.voc_pkt[0],
@@ -1087,30 +1103,43 @@
return 0;
}
-static int msm_voip_rate_config_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- mutex_lock(&voip_info.lock);
-
- ucontrol->value.integer.value[0] = voip_info.rate;
-
- mutex_unlock(&voip_info.lock);
-
- return 0;
-}
-
static int msm_voip_rate_config_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
+ int ret = 0;
+ int rate = ucontrol->value.integer.value[0];
+
mutex_lock(&voip_info.lock);
- voip_info.rate = ucontrol->value.integer.value[0];
+ if (voip_info.rate != rate) {
+ voip_info.rate = rate;
+ pr_debug("%s: rate=%d\n", __func__, voip_info.rate);
- pr_debug("%s: rate=%d\n", __func__, voip_info.rate);
+ if (voip_info.state == VOIP_STARTED &&
+ (voip_info.mode == MODE_AMR ||
+ voip_info.mode == MODE_AMR_WB)) {
+ ret = voip_config_vocoder(
+ voip_info.capture_substream);
+ if (ret) {
+ pr_err("%s:Failed to configure vocoder, ret=%d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ret = voc_update_amr_vocoder_rate(
+ voc_get_session_id(VOIP_SESSION_NAME));
+ if (ret) {
+ pr_err("%s:Failed to update AMR rate, ret=%d\n",
+ __func__, ret);
+ }
+ }
+ }
+
+done:
mutex_unlock(&voip_info.lock);
- return 0;
+ return ret;
}
static int msm_voip_evrc_min_max_rate_config_get(struct snd_kcontrol *kcontrol,
diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c
index 54b1263..3e30290 100644
--- a/sound/soc/msm/qdsp6v2/q6adm.c
+++ b/sound/soc/msm/qdsp6v2/q6adm.c
@@ -93,6 +93,11 @@
sz = sizeof(struct adm_cmd_set_pp_params_inband_v5) +
sizeof(struct srs_trumedia_params_GLOBAL);
adm_params = kzalloc(sz, GFP_KERNEL);
+ if (!adm_params) {
+ pr_err("%s, adm params memory alloc failed\n",
+ __func__);
+ return -ENOMEM;
+ }
adm_params->payload_size =
sizeof(struct srs_trumedia_params_GLOBAL) +
sizeof(struct adm_param_data_v5);
@@ -117,6 +122,11 @@
sz = sizeof(struct adm_cmd_set_pp_params_inband_v5) +
sizeof(struct srs_trumedia_params_WOWHD);
adm_params = kzalloc(sz, GFP_KERNEL);
+ if (!adm_params) {
+ pr_err("%s, adm params memory alloc failed\n",
+ __func__);
+ return -ENOMEM;
+ }
adm_params->payload_size =
sizeof(struct srs_trumedia_params_WOWHD) +
sizeof(struct adm_param_data_v5);
@@ -142,6 +152,11 @@
sz = sizeof(struct adm_cmd_set_pp_params_inband_v5) +
sizeof(struct srs_trumedia_params_CSHP);
adm_params = kzalloc(sz, GFP_KERNEL);
+ if (!adm_params) {
+ pr_err("%s, adm params memory alloc failed\n",
+ __func__);
+ return -ENOMEM;
+ }
adm_params->payload_size =
sizeof(struct srs_trumedia_params_CSHP) +
sizeof(struct adm_param_data_v5);
@@ -166,6 +181,11 @@
sz = sizeof(struct adm_cmd_set_pp_params_inband_v5) +
sizeof(struct srs_trumedia_params_HPF);
adm_params = kzalloc(sz, GFP_KERNEL);
+ if (!adm_params) {
+ pr_err("%s, adm params memory alloc failed\n",
+ __func__);
+ return -ENOMEM;
+ }
adm_params->payload_size =
sizeof(struct srs_trumedia_params_HPF) +
sizeof(struct adm_param_data_v5);
@@ -186,6 +206,11 @@
sz = sizeof(struct adm_cmd_set_pp_params_inband_v5) +
sizeof(struct srs_trumedia_params_PEQ);
adm_params = kzalloc(sz, GFP_KERNEL);
+ if (!adm_params) {
+ pr_err("%s, adm params memory alloc failed\n",
+ __func__);
+ return -ENOMEM;
+ }
adm_params->payload_size =
sizeof(struct srs_trumedia_params_PEQ) +
sizeof(struct adm_param_data_v5);
@@ -208,6 +233,11 @@
sz = sizeof(struct adm_cmd_set_pp_params_inband_v5) +
sizeof(struct srs_trumedia_params_HL);
adm_params = kzalloc(sz, GFP_KERNEL);
+ if (!adm_params) {
+ pr_err("%s, adm params memory alloc failed\n",
+ __func__);
+ return -ENOMEM;
+ }
adm_params->payload_size =
sizeof(struct srs_trumedia_params_HL) +
sizeof(struct adm_param_data_v5);
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index ee26f2e..3dac1f9 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -81,6 +81,7 @@
static int q6asm_map_channels(u8 *channel_mapping, uint32_t channels);
void *q6asm_mmap_apr_reg(void);
+static int q6asm_is_valid_session(struct apr_client_data *data, void *priv);
/* for ASM custom topology */
static struct audio_buffer common_buf[2];
@@ -817,6 +818,8 @@
ac->io_mode = SYNC_IO_MODE;
ac->perf_mode = LEGACY_PCM_MODE;
ac->fptr_cache_ops = NULL;
+ /* DSP expects stream id from 1 */
+ ac->stream_id = 1;
ac->apr = apr_register("ADSP", "ASM", \
(apr_fn)q6asm_callback,\
((ac->session) << 8 | 0x0001),\
@@ -1213,6 +1216,7 @@
unsigned long dsp_flags;
uint32_t *payload;
uint32_t wakeup_flag = 1;
+ int32_t ret = 0;
if ((ac == NULL) || (data == NULL)) {
@@ -1279,11 +1283,10 @@
case ASM_SESSION_CMD_REGISTER_FORX_OVERFLOW_EVENTS:
case ASM_STREAM_CMD_FLUSH_READBUFS:
pr_debug("%s:Payload = [0x%x]\n", __func__, payload[0]);
- if (token != ac->session) {
- pr_err("%s:Invalid session[%d] rxed expected[%d]",
- __func__, token, ac->session);
- return -EINVAL;
- }
+ ret = q6asm_is_valid_session(data, priv);
+ if (ret != 0)
+ return ret;
+
case ASM_STREAM_CMD_OPEN_READ_V3:
case ASM_STREAM_CMD_OPEN_WRITE_V3:
case ASM_STREAM_CMD_OPEN_READWRITE_V2:
@@ -1590,8 +1593,8 @@
hdr->src_domain = APR_DOMAIN_APPS;
hdr->dest_svc = APR_SVC_ASM;
hdr->dest_domain = APR_DOMAIN_ADSP;
- hdr->src_port = ((ac->session << 8) & 0xFF00) | (stream_id+1);
- hdr->dest_port = ((ac->session << 8) & 0xFF00) | (stream_id+1);
+ hdr->src_port = ((ac->session << 8) & 0xFF00) | (stream_id);
+ hdr->dest_port = ((ac->session << 8) & 0xFF00) | (stream_id);
if (cmd_flg) {
hdr->token = ac->session;
atomic_set(&ac->cmd_state, 1);
@@ -1631,8 +1634,8 @@
hdr->src_domain = APR_DOMAIN_APPS;
hdr->dest_svc = APR_SVC_ASM;
hdr->dest_domain = APR_DOMAIN_ADSP;
- hdr->src_port = ((ac->session << 8) & 0xFF00) | (stream_id+1);
- hdr->dest_port = ((ac->session << 8) & 0xFF00) | (stream_id+1);
+ hdr->src_port = ((ac->session << 8) & 0xFF00) | (stream_id);
+ hdr->dest_port = ((ac->session << 8) & 0xFF00) | (stream_id);
if (cmd_flg) {
hdr->token = ac->session;
atomic_set(&ac->cmd_state, 1);
@@ -1810,6 +1813,17 @@
q6asm_stream_add_hdr(ac, &open.hdr, sizeof(open), TRUE, stream_id);
+ /*
+ * Updated the token field with stream/session for compressed playback
+ * Platform driver must know the the stream with which the command is
+ * associated
+ */
+ if (ac->io_mode & COMPRESSED_STREAM_IO)
+ open.hdr.token = ((ac->session << 8) & 0xFFFF00) |
+ (stream_id & 0xFF);
+
+ pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n",
+ __func__, open.hdr.token, stream_id, ac->session);
open.hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_V3;
open.mode_flags = 0x00;
if (ac->perf_mode == ULTRA_LOW_LATENCY_PCM_MODE)
@@ -2765,6 +2779,17 @@
q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+ /*
+ * Updated the token field with stream/session for compressed playback
+ * Platform driver must know the the stream with which the command is
+ * associated
+ */
+ if (ac->io_mode & COMPRESSED_STREAM_IO)
+ fmt.hdr.token = ((ac->session << 8) & 0xFFFF00) |
+ (stream_id & 0xFF);
+
+ pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n",
+ __func__, fmt.hdr.token, stream_id, ac->session);
fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
sizeof(fmt.fmt_blk);
@@ -3409,8 +3434,6 @@
vol.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
vol.param.data_payload_addr_lsw = 0;
vol.param.data_payload_addr_msw = 0;
-
-
vol.param.mem_map_handle = 0;
vol.param.data_payload_size = sizeof(vol) -
sizeof(vol.hdr) - sizeof(vol.param);
@@ -3440,6 +3463,7 @@
fail_cmd:
return rc;
}
+
int q6asm_set_softpause(struct audio_client *ac,
struct asm_softpause_params *pause_param)
{
@@ -3759,13 +3783,15 @@
u32 lbuf_addr_lsw;
u32 liomode;
u32 io_compressed;
+ u32 io_compressed_stream;
if (!ac || ac->apr == NULL) {
pr_err("%s: APR handle NULL\n", __func__);
return -EINVAL;
}
- q6asm_add_hdr_async(ac, &write.hdr, sizeof(write), FALSE);
+ q6asm_stream_add_hdr_async(
+ ac, &write.hdr, sizeof(write), FALSE, ac->stream_id);
port = &ac->port[IN];
ab = &port->buf[port->dsp_buf];
@@ -3780,10 +3806,12 @@
write.timestamp_lsw = param->lsw_ts;
liomode = (ASYNC_IO_MODE | NT_MODE);
io_compressed = (ASYNC_IO_MODE | COMPRESSED_IO);
+ io_compressed_stream = (ASYNC_IO_MODE | COMPRESSED_STREAM_IO);
if (ac->io_mode == liomode)
lbuf_addr_lsw = (write.buf_addr_lsw - 32);
- else if (ac->io_mode == io_compressed)
+ else if (ac->io_mode == io_compressed ||
+ ac->io_mode == io_compressed_stream)
lbuf_addr_lsw = (write.buf_addr_lsw - param->metadata_len);
else
lbuf_addr_lsw = write.buf_addr_lsw;
@@ -4113,6 +4141,17 @@
return -EINVAL;
}
q6asm_stream_add_hdr(ac, &hdr, sizeof(hdr), TRUE, stream_id);
+
+ /*
+ * Updated the token field with stream/session for compressed playback
+ * Platform driver must know the the stream with which the command is
+ * associated
+ */
+ if (ac->io_mode & COMPRESSED_STREAM_IO)
+ hdr.token = ((ac->session << 8) & 0xFFFF00) |
+ (stream_id & 0xFF);
+ pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n",
+ __func__, hdr.token, stream_id, ac->session);
switch (cmd) {
case CMD_PAUSE:
pr_debug("%s:CMD_PAUSE\n", __func__);
@@ -4211,6 +4250,18 @@
return -EINVAL;
}
q6asm_stream_add_hdr_async(ac, &hdr, sizeof(hdr), TRUE, stream_id);
+
+ /*
+ * Updated the token field with stream/session for compressed playback
+ * Platform driver must know the the stream with which the command is
+ * associated
+ */
+ if (ac->io_mode & COMPRESSED_STREAM_IO)
+ hdr.token = ((ac->session << 8) & 0xFFFF00) |
+ (stream_id & 0xFF);
+
+ pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n",
+ __func__, hdr.token, stream_id, ac->session);
switch (cmd) {
case CMD_PAUSE:
pr_debug("%s:CMD_PAUSE\n", __func__);
@@ -4257,17 +4308,30 @@
return __q6asm_cmd_nowait(ac, cmd, stream_id);
}
-int q6asm_send_meta_data(struct audio_client *ac, uint32_t initial_samples,
- uint32_t trailing_samples)
+int __q6asm_send_meta_data(struct audio_client *ac, uint32_t stream_id,
+ uint32_t initial_samples, uint32_t trailing_samples)
{
struct asm_data_cmd_remove_silence silence;
int rc = 0;
+
if (!ac || ac->apr == NULL) {
pr_err("APR handle NULL\n");
return -EINVAL;
}
pr_debug("%s session[%d]", __func__, ac->session);
- q6asm_add_hdr_async(ac, &silence.hdr, sizeof(silence), FALSE);
+ q6asm_stream_add_hdr_async(ac, &silence.hdr, sizeof(silence), FALSE,
+ stream_id);
+
+ /*
+ * Updated the token field with stream/session for compressed playback
+ * Platform driver must know the the stream with which the command is
+ * associated
+ */
+ if (ac->io_mode & COMPRESSED_STREAM_IO)
+ silence.hdr.token = ((ac->session << 8) & 0xFFFF00) |
+ (stream_id & 0xFF);
+ pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n",
+ __func__, silence.hdr.token, stream_id, ac->session);
silence.hdr.opcode = ASM_DATA_CMD_REMOVE_INITIAL_SILENCE;
silence.num_samples_to_remove = initial_samples;
@@ -4292,6 +4356,20 @@
return -EINVAL;
}
+int q6asm_stream_send_meta_data(struct audio_client *ac, uint32_t stream_id,
+ uint32_t initial_samples, uint32_t trailing_samples)
+{
+ return __q6asm_send_meta_data(ac, stream_id, initial_samples,
+ trailing_samples);
+}
+
+int q6asm_send_meta_data(struct audio_client *ac, uint32_t initial_samples,
+ uint32_t trailing_samples)
+{
+ return __q6asm_send_meta_data(ac, ac->stream_id, initial_samples,
+ trailing_samples);
+}
+
static void q6asm_reset_buf_state(struct audio_client *ac)
{
int cnt = 0;
@@ -4395,6 +4473,34 @@
}
+static int q6asm_is_valid_session(struct apr_client_data *data, void *priv)
+{
+
+ struct audio_client *ac = (struct audio_client *)priv;
+ uint32_t token = data->token;
+
+ /*
+ * Some commands for compressed playback has token as session and
+ * other commands has session|stream. Check for both conditions
+ * before deciding if the callback was for a invalud session.
+ */
+ if (ac->io_mode & COMPRESSED_STREAM_IO) {
+ if ((token & 0xFFFFFF00) != ((ac->session << 8) & 0xFFFFFF00)
+ && (token != ac->session)) {
+ pr_err("%s:Invalid compr session[%d] rxed expected[%d]",
+ __func__, token, ac->session);
+ return -EINVAL;
+ }
+ } else {
+ if (token != ac->session) {
+ pr_err("%s:Invalid session[%d] rxed expected[%d]",
+ __func__, token, ac->session);
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
static int __init q6asm_init(void)
{
int lcnt;
diff --git a/sound/soc/msm/qdsp6v2/q6voice.c b/sound/soc/msm/qdsp6v2/q6voice.c
index 0c7472f..61a262f 100644
--- a/sound/soc/msm/qdsp6v2/q6voice.c
+++ b/sound/soc/msm/qdsp6v2/q6voice.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
@@ -943,6 +943,7 @@
if (is_voip_session(v->session_id) ||
is_qchat_session(v->session_id) ||
+ is_volte_session(v->session_id) ||
v->voc_state == VOC_ERROR) {
/* Destroy CVS. */
pr_debug("%s: CVS destroy session\n", __func__);
@@ -1493,6 +1494,77 @@
return ret;
}
+static int voice_config_cvs_vocoder_amr_rate(struct voice_data *v)
+{
+ int ret = 0;
+ void *apr_cvs;
+ u16 cvs_handle;
+ struct cvs_set_amr_enc_rate_cmd cvs_set_amr_rate;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ apr_cvs = common.apr_q6_cvs;
+
+ if (!apr_cvs) {
+ pr_err("%s: apr_cvs is NULL.\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ cvs_handle = voice_get_cvs_handle(v);
+
+ pr_debug("%s: Setting AMR rate. Media Type: %d\n", __func__,
+ common.mvs_info.media_type);
+
+ cvs_set_amr_rate.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvs_set_amr_rate.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_set_amr_rate) - APR_HDR_SIZE);
+ cvs_set_amr_rate.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvs_set_amr_rate.hdr.dest_port = cvs_handle;
+ cvs_set_amr_rate.hdr.token = 0;
+
+ if (common.mvs_info.media_type == VSS_MEDIA_ID_AMR_NB_MODEM)
+ cvs_set_amr_rate.hdr.opcode =
+ VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE;
+ else if (common.mvs_info.media_type == VSS_MEDIA_ID_AMR_WB_MODEM)
+ cvs_set_amr_rate.hdr.opcode =
+ VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE;
+
+ cvs_set_amr_rate.amr_rate.mode = common.mvs_info.rate;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_amr_rate);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending SET_AMR_RATE\n",
+ __func__, ret);
+
+ goto done;
+ }
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ return 0;
+done:
+ return ret;
+}
+
static int voice_config_cvs_vocoder(struct voice_data *v)
{
int ret = 0;
@@ -1597,80 +1669,13 @@
break;
}
- case VSS_MEDIA_ID_AMR_NB_MODEM: {
- struct cvs_set_amr_enc_rate_cmd cvs_set_amr_rate;
-
- pr_debug("Setting AMR rate\n");
-
- cvs_set_amr_rate.hdr.hdr_field =
- APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
- APR_HDR_LEN(APR_HDR_SIZE),
- APR_PKT_VER);
- cvs_set_amr_rate.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
- sizeof(cvs_set_amr_rate) - APR_HDR_SIZE);
- cvs_set_amr_rate.hdr.src_port =
- voice_get_idx_for_session(v->session_id);
- cvs_set_amr_rate.hdr.dest_port = cvs_handle;
- cvs_set_amr_rate.hdr.token = 0;
- cvs_set_amr_rate.hdr.opcode =
- VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE;
- cvs_set_amr_rate.amr_rate.mode = common.mvs_info.rate;
-
- v->cvs_state = CMD_STATUS_FAIL;
-
- ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_amr_rate);
- if (ret < 0) {
- pr_err("%s: Error %d sending SET_AMR_RATE\n",
- __func__, ret);
- goto fail;
- }
- ret = wait_event_timeout(v->cvs_wait,
- (v->cvs_state == CMD_STATUS_SUCCESS),
- msecs_to_jiffies(TIMEOUT_MS));
- if (!ret) {
- pr_err("%s: wait_event timeout\n", __func__);
- goto fail;
- }
-
- ret = voice_set_dtx(v);
- if (ret < 0)
- goto fail;
-
- break;
- }
+ case VSS_MEDIA_ID_AMR_NB_MODEM:
case VSS_MEDIA_ID_AMR_WB_MODEM: {
- struct cvs_set_amrwb_enc_rate_cmd cvs_set_amrwb_rate;
-
- pr_debug("Setting AMR WB rate\n");
-
- cvs_set_amrwb_rate.hdr.hdr_field =
- APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
- APR_HDR_LEN(APR_HDR_SIZE),
- APR_PKT_VER);
- cvs_set_amrwb_rate.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
- sizeof(cvs_set_amrwb_rate) -
- APR_HDR_SIZE);
- cvs_set_amrwb_rate.hdr.src_port =
- voice_get_idx_for_session(v->session_id);
- cvs_set_amrwb_rate.hdr.dest_port = cvs_handle;
- cvs_set_amrwb_rate.hdr.token = 0;
- cvs_set_amrwb_rate.hdr.opcode =
- VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE;
- cvs_set_amrwb_rate.amrwb_rate.mode = common.mvs_info.rate;
-
- v->cvs_state = CMD_STATUS_FAIL;
-
- ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_amrwb_rate);
- if (ret < 0) {
- pr_err("%s: Error %d sending SET_AMRWB_RATE\n",
+ ret = voice_config_cvs_vocoder_amr_rate(v);
+ if (ret) {
+ pr_err("%s: Failed to update vocoder rate. %d\n",
__func__, ret);
- goto fail;
- }
- ret = wait_event_timeout(v->cvs_wait,
- (v->cvs_state == CMD_STATUS_SUCCESS),
- msecs_to_jiffies(TIMEOUT_MS));
- if (!ret) {
- pr_err("%s: wait_event timeout\n", __func__);
+
goto fail;
}
@@ -1697,6 +1702,31 @@
return -EINVAL;
}
+int voc_update_amr_vocoder_rate(uint32_t session_id)
+{
+ int ret = 0;
+ struct voice_data *v;
+
+ pr_debug("%s: session_id:%d", __func__, session_id);
+
+ v = voice_get_session(session_id);
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL, session_id:%d\n", __func__,
+ session_id);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ mutex_lock(&v->lock);
+ ret = voice_config_cvs_vocoder_amr_rate(v);
+ mutex_unlock(&v->lock);
+
+done:
+ return ret;
+}
+
static int voice_send_start_voice_cmd(struct voice_data *v)
{
struct apr_hdr mvm_start_voice_cmd;
@@ -3408,6 +3438,10 @@
mvm_handle = voice_get_mvm_handle(v);
cvp_handle = voice_get_cvp_handle(v);
+ /* disable slowtalk if st_enable is set */
+ if (v->st_enable)
+ voice_send_set_pp_enable_cmd(v, MODULE_ID_VOICE_MODULE_ST, 0);
+
/* stop playback or recording */
v->music_info.force = 1;
voice_cvs_stop_playback(v);
diff --git a/sound/soc/msm/qdsp6v2/q6voice.h b/sound/soc/msm/qdsp6v2/q6voice.h
index 5c0cf21..9efc9fc 100644
--- a/sound/soc/msm/qdsp6v2/q6voice.h
+++ b/sound/soc/msm/qdsp6v2/q6voice.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
@@ -1465,5 +1465,6 @@
int voc_start_record(uint32_t port_id, uint32_t set, uint32_t session_id);
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);
#endif
diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index 0970a83..4792719 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -475,7 +475,6 @@
struct snd_soc_pcm_runtime *fe = cstream->private_data;
struct snd_pcm_substream *fe_substream = fe->pcm->streams[0].substream;
struct snd_soc_platform *platform = fe->platform;
- struct snd_pcm_hw_params *hw_params;
int ret = 0, stream;
if (cstream->direction == SND_COMPRESS_PLAYBACK)
@@ -483,10 +482,6 @@
else
stream = SNDRV_PCM_STREAM_CAPTURE;
- hw_params = kzalloc(sizeof(*hw_params), GFP_KERNEL);
- if (hw_params == NULL)
- return -ENOMEM;
-
mutex_lock(&fe->card->dpcm_mutex);
/* first we call set_params for the platform driver
* this should configure the soc side