Merge "slim_msm: Allow remote management of pipes for MSM slimbus BAM"
diff --git a/Documentation/devicetree/bindings/arm/msm/dcvs-core-info.txt b/Documentation/devicetree/bindings/arm/msm/dcvs-core-info.txt
index a39356c..b7dd427 100644
--- a/Documentation/devicetree/bindings/arm/msm/dcvs-core-info.txt
+++ b/Documentation/devicetree/bindings/arm/msm/dcvs-core-info.txt
@@ -5,74 +5,139 @@
Required properties:
-- qcom,core-max-time-us: Maximum time limit in micorseconds for switching clock rate.
- Limited to this value if switching time takes longer than this limit. Typical value is 100000.
-- qcom,algo-slack-time-us: Time in microseconds after which the QoS guarantee will kick in
- and the clock rate will increased as necessary. Typical value is about 30000.
-- qcom,algo-disable-pc-threshold: If core frequency (kHz) is higher than this value, power collapse is disallowed. Set to 0 for GPU.
-- qcom,algo-ss-window-size: Steady state window size in microseconds.
-- qcom,algo-ss-util-pct: When determining the steady state level, this percentage value is used to provide headroom
- from the utilized cpu to the selected level.
-- qcom,algo-ee-max-util-pct: When determining the level with the lowest energy, any level that exceeds this busy
- percentage, for the measured work in the last window, is disqualified for performance reasons.
-- qcom,algo-ss-iobusy-conv: Used to convert correlation time into assumed IO Busy time, which is removed
- from measured elapsed time when computing cpu utilization.
+- qcom,core-core-type: indicates whether this core is a CPU(0) or a GPU(1)
+- qcom,num-cores: The number of cores this entry represents
+- qcom,sensors: The vector of sensor ids for the cores
+
+- qcom,algo-disable-pc-threshold: sets highest frequency at which DCVS
+ will allow the CPU to power collapse.
+- qcom,algo-em-win-size-min-us: sets minimum Energy Minimization(EM)
+ window size.
+
+- qcom,algo-em-win-size-max-us: sets maximum EM window size.
+- qcom,algo-em-max-util-pct: sets maximum CPU utilization that will
+ not be exceeded by any core when
+ MP-decision decides the number of
+ online cores.
+- qcom,algo-group-id: specifies a group index of a core.
+- qcom,algo-max-freq-chg-time-us: shows time taken for the most recent
+ frequency change.
+- qcom,algo-slack-mode-dynamic: specifies if dynamic slack mode is
+ enabled or not.
+- qcom,algo-slack-weight-thresh-pct: sets occurrence percentage of CPU
+ activity that will make slack timer
+ triggered.
+- qcom,algo-slack-time-min-us: specifies the slack time that slack
+ timer would be set if the current clock
+ frequency is zero.
+- qcom,algo-slack-time-max-us: sets maximum slack timer value to be
+ used by slack timer.
+- qcom,algo-ss-win-size-min-us: sets minimum steady state window size.
+- qcom,algo-ss-win-size-max-us: sets maximum steady state window size.
+- qcom,algo-ss-util-pct: sets target CPU utilization during
+ steady-state.
+- qcom,algo-ss-iobusy-conv: specifies how wait time (i/o busy time)
+ is incorporated into the steady-state
+ algorithm.
+
+- qcom,energy-active-coeff-a: sets active power equation coefficient a.
+- qcom,energy-active-coeff-b: sets active power equation coefficient b.
+- qcom,energy-active-coeff-c: sets active power equation coefficient c.
+- qcom,energy-leakage-coeff-a: sets leakage power equation coefficient a.
+- qcom,energy-leakage-coeff-b: sets leakage power equation coefficient b.
+- qcom,energy-leakage-coeff-c: sets leakage power equation coefficient c.
+- qcom,energy-leakage-coeff-d: sets leakage power equation coefficient d.
+
+- qcom,power-current-temp: the current temperature in degCelcius.
+- qcom,power-num-freq: the number of freq this core supports.
A number of frequency levels are represented as sub-nodes:
required properties:
- reg: The index of the frequency entry
- qcom,freq The frequency of the DVS entry (in kHZ)
-- qcom,idle-energy: The idle energy cost of the entry (in micro watts)
-- qcom,active-energy: The active energy cost of the entry (in micro watts)
+- qcom,is_trans_level This frequency is transient step for DCVS
+- qcom,active-energy-offset: The active energy cost of the entry
+- qcom,leakage-energy-offset: The leakage energy cost of the entry
Sample:
qcom,kgsl-3d0@fdb00000 {
...
- qcom,dcvs-core-info {
- #address-cells = <1>;
- #size-cells = <0>;
+ qcom,dcvs-core-info {
+ #address-cells = <1>;
+ #size-cells = <0>;
- compatible = "qcom,dcvs-core-info";
+ compatible = "qcom,dcvs-core-info";
- qcom,core-max-time-us = <100000>;
- qcom,algo-slack-time-us = <39000>;
- qcom,algo-disable-pc-threshold = <86000>;
- qcom,algo-ss-window-size = <1000000>;
- qcom,algo-ss-util-pct = <95>;
- qcom,algo-em-max-util-pct = <97>;
- qcom,algo-ss-iobusy-conv = <100>;
+ qcom,num-cores = <1>;
+ qcom,sensors = <0>;
- qcom,dcvs-freq@0 {
- reg = <0>;
- qcom,freq = <0>;
- qcom,idle-energy = <0>;
- qcom,active-energy = <333932>;
+ qcom,core-core-type = <1>;
+
+ qcom,algo-disable-pc-threshold = <0>;
+ qcom,algo-em-win-size-min-us = <100000>;
+ qcom,algo-em-win-size-max-us = <300000>;
+ qcom,algo-em-max-util-pct = <97>;
+ qcom,algo-group-id = <95>;
+ qcom,algo-max-freq-chg-time-us = <100000>;
+ qcom,algo-slack-mode-dynamic = <100000>;
+ qcom,algo-slack-weight-thresh-pct = <0>;
+ qcom,algo-slack-time-min-us = <39000>;
+ qcom,algo-slack-time-max-us = <39000>;
+ qcom,algo-ss-win-size-min-us = <1000000>;
+ qcom,algo-ss-win-size-max-us = <1000000>;
+ qcom,algo-ss-util-pct = <95>;
+ qcom,algo-ss-iobusy-conv = <100>;
+
+ qcom,energy-active-coeff-a = <2492>;
+ qcom,energy-active-coeff-b = <0>;
+ qcom,energy-active-coeff-c = <0>;
+ qcom,energy-leakage-coeff-a = <11>;
+ qcom,energy-leakage-coeff-b = <157150>;
+ qcom,energy-leakage-coeff-c = <0>;
+ qcom,energy-leakage-coeff-d = <0>;
+
+ qcom,power-current-temp = <25>;
+ qcom,power-num-freq = <4>;
+
+ qcom,dcvs-freq@0 {
+ reg = <0>;
+ qcom,freq = <0>;
+ qcom,voltage = <0>;
+ qcom,is_trans_level = <0>;
+ qcom,active-energy-offset = <100>;
+ qcom,leakage-energy-offset = <0>;
+ };
+
+ qcom,dcvs-freq@1 {
+ reg = <1>;
+ qcom,freq = <0>;
+ qcom,voltage = <0>;
+ qcom,is_trans_level = <0>;
+ qcom,active-energy-offset = <100>;
+ qcom,leakage-energy-offset = <0>;
+ };
+
+ qcom,dcvs-freq@2 {
+ reg = <2>;
+ qcom,freq = <0>;
+ qcom,voltage = <0>;
+ qcom,is_trans_level = <0>;
+ qcom,active-energy-offset = <100>;
+ qcom,leakage-energy-offset = <0>;
+ };
+
+ qcom,dcvs-freq@3 {
+ reg = <3>;
+ qcom,freq = <0>;
+ qcom,voltage = <0>;
+ qcom,is_trans_level = <0>;
+ qcom,active-energy-offset = <844545>;
+ qcom,leakage-energy-offset = <0>;
+ };
};
-
- qcom,dcvs-freq@1 {
- reg = <1>;
- qcom,freq = <0>;
- qcom,idle-energy = <0>;
- qcom,active-energy = <497532>;
- };
-
- qcom,dcvs-freq@2 {
- reg = <2>;
- qcom,freq = <0>;
- qcom,idle-energy = <0>;
- qcom,active-energy = <707610>;
- };
-
- qcom,dcvs-freq@3 {
- reg = <3>;
- qcom,freq = <0>;
- qcom,idle-energy = <0>;
- qcom,active-energy = <844545>;
- };
- };
...
};
diff --git a/Documentation/devicetree/bindings/arm/msm/lpm-resources.txt b/Documentation/devicetree/bindings/arm/msm/lpm-resources.txt
index b16d40f..7f2a21b 100644
--- a/Documentation/devicetree/bindings/arm/msm/lpm-resources.txt
+++ b/Documentation/devicetree/bindings/arm/msm/lpm-resources.txt
@@ -8,13 +8,25 @@
that need to be monitored for usage requirement to check if a given low power
state can be entered.Each resource is identified by a combination of the name,
id,type and key which is also used by the RPM to identify a shared resource.
+The name and resource-type are required nodes; the type, id and key are
+optional nodes which are needed if the resource type is RPM shared resource
+(MSM_LPM_RPM_RS_TYPE).
-The required nodes for lpm-resources are:
+The nodes for lpm-resources are:
+
+Required Nodes:
- compatible: "qcom,lpm-resources"
- reg: The numeric level id
- qcom,name: The name of the low power resource represented
as a string.
+- qcom,resource-type: The type of the LPM resource.
+ MSM_LPM_RPM_RS_TYPE = 0
+ MSM_LPM_LOCAL_RS_TYPE = 1
+
+
+Optional Nodes:
+
- qcom,type: The type of resource used like smps or pxo
represented as a hex value.
- qcom,id: The id representing a device within a resource type.
@@ -25,6 +37,7 @@
qcom,lpm-resources@0 {
reg = <0x0>;
qcom,name = "vdd-dig";
+ qcom,resource-type = <0>;
qcom,type = <0x62706d73>; /* "smpb" */
qcom,id = <0x02>;
qcom,key = <0x6e726f63>; /* "corn" */
diff --git a/Documentation/devicetree/bindings/arm/msm/spm-v2.txt b/Documentation/devicetree/bindings/arm/msm/spm-v2.txt
index 1a19dbb..a2d8359 100644
--- a/Documentation/devicetree/bindings/arm/msm/spm-v2.txt
+++ b/Documentation/devicetree/bindings/arm/msm/spm-v2.txt
@@ -33,8 +33,10 @@
between AVS controller requests
- qcom,saw2-pmic-data0..7: Specify the pmic data value and the associated FTS
index to send the PMIC data to
-- qcom,saw2-vctl-port: The FTS port used for changing voltage
-- qcom,saw2-phase-port: The FTS port used for changing the number of phases
+- qcom,saw2-vctl-port: The PVC (PMIC Virtual Channel) port used for changing
+ voltage
+- qcom,saw2-phase-port: The PVC port used for changing the number of phases
+- qcom,saw2-pfm-port: The PVC port used for enabling PWM/PFM modes
- qcom,saw2-spm-cmd-wfi: The WFI command sequence
- qcom,saw2-spm-cmd-ret: The Retention command sequence
- qcom,saw2-spm-cmd-spc: The Standalone PC command sequence
diff --git a/Documentation/devicetree/bindings/fb/mdss-edp.txt b/Documentation/devicetree/bindings/fb/mdss-edp.txt
new file mode 100644
index 0000000..4fedc72
--- /dev/null
+++ b/Documentation/devicetree/bindings/fb/mdss-edp.txt
@@ -0,0 +1,28 @@
+Qualcomm MDSS EDP
+
+MDSS EDP is a edp driver which supports panels that are compatable with
+VESA EDP display interface specification.
+
+Required properties
+- compatible : Must be "qcom,mdss-edp".
+- reg : Offset and length of the register set for the device.
+- reg-names : Names to refer to register sets related to this device
+- vdda-supply : Phandle for vdd regulator device node.
+- gpio-panel-en : GPIO for supplying power to panel and to backlight driver.
+- status : A string that has to be set to "okay/ok" to enable
+ the driver. By default this property will be set to
+ "disable". Will be set to "ok/okay" status for specific
+ platforms.
+
+Example:
+ mdss_edp: qcom,mdss_edp@fd923400 {
+ compatible = "qcom,mdss-edp";
+ reg = <0xfd923400 0x700>,
+ <0xfd8c2000 0x1000>;
+ reg-names = "edp_base", "mmss_cc_base";
+ vdda-supply = <&pm8941_l12>;
+ gpio-panel-en = <&msmgpio 58 0>;
+ status = "disable";
+ };
+
+
diff --git a/Documentation/devicetree/bindings/gpio/gpio_keys.txt b/Documentation/devicetree/bindings/gpio/gpio_keys.txt
index 5c2c021..4e810e1 100644
--- a/Documentation/devicetree/bindings/gpio/gpio_keys.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio_keys.txt
@@ -4,6 +4,7 @@
- compatible = "gpio-keys";
Optional properties:
+ - input-name: input name of the device
- autorepeat: Boolean, Enable auto repeat feature of Linux input
subsystem.
@@ -28,6 +29,7 @@
#address-cells = <1>;
#size-cells = <0>;
autorepeat;
+ input-name = "gpio-keys";
button@21 {
label = "GPIO Key UP";
linux,code = <103>;
diff --git a/Documentation/devicetree/bindings/gpu/adreno.txt b/Documentation/devicetree/bindings/gpu/adreno.txt
index 16925fb..38b2721 100644
--- a/Documentation/devicetree/bindings/gpu/adreno.txt
+++ b/Documentation/devicetree/bindings/gpu/adreno.txt
@@ -62,6 +62,7 @@
- qcom,chipid: If it exists this property is used to replace
the chip identification read from the GPU hardware.
This is used to override faulty hardware readings.
+- qcom,strtstp-sleepwake: Boolean. Enables use of GPU SLUMBER instead of SLEEP for power savings
Example of A330 GPU in MSM8974:
diff --git a/Documentation/devicetree/bindings/iommu/msm_iommu.txt b/Documentation/devicetree/bindings/iommu/msm_iommu.txt
index 7a90cc0..7872280 100644
--- a/Documentation/devicetree/bindings/iommu/msm_iommu.txt
+++ b/Documentation/devicetree/bindings/iommu/msm_iommu.txt
@@ -5,6 +5,9 @@
- "qcom,msm-smmu-v2"
- reg : offset and length of the register set for the device.
+Optional properties:
+- qcom,iommu-secure-id : Secure identifier for the IOMMU block
+
- List of sub nodes, one for each of the translation context banks supported.
Each sub node has the following required properties:
@@ -13,7 +16,6 @@
- qcom,iommu-ctx-sids : List of stream identifiers associated with this
translation context.
- label : Name of the context bank
- - qcom,iommu-smt-size : Number of SMR entries in the SMT of this HW block
- vdd-supply : vdd-supply: phandle to GDSC regulator controlling this IOMMU.
Optional properties:
diff --git a/Documentation/devicetree/bindings/pil/pil-mba.txt b/Documentation/devicetree/bindings/pil/pil-mba.txt
index 9692059..ce6bb8f 100644
--- a/Documentation/devicetree/bindings/pil/pil-mba.txt
+++ b/Documentation/devicetree/bindings/pil/pil-mba.txt
@@ -12,6 +12,7 @@
the primary modem image metadata should be stored.
- reg-names: Names for the above base addresses. "rmb_base" and
"metadata_base" are expected.
+- interrupts: The modem watchdog interrupt
- qcom,firmware-name: Base name of the firmware image. Ex. "modem"
Optional properties:
@@ -24,6 +25,7 @@
reg = <0xfc820000 0x0020>,
<0x0d1f0000 0x4000>;
reg-names = "rmb_base", "metadata_base";
+ interrupts = <0 56 1>;
qcom,firmware-name = "modem";
qcom,depends-on = "mba";
diff --git a/Documentation/devicetree/bindings/pil/pil-pronto.txt b/Documentation/devicetree/bindings/pil/pil-pronto.txt
index e123bdb..e3108ac 100644
--- a/Documentation/devicetree/bindings/pil/pil-pronto.txt
+++ b/Documentation/devicetree/bindings/pil/pil-pronto.txt
@@ -10,6 +10,7 @@
- reg: offset and length of the register set for the device.
- reg-names: names of the bases for the above registers. "pmu_base", "clk_base",
and "halt_base" are expected.
+- interrupts: WCNSS to Apps watchdog bite interrupt
- vdd_pronto_pll-supply: regulator to supply pronto pll.
- qcom,firmware-name: Base name of the firmware image. Ex. "wcnss"
@@ -21,6 +22,7 @@
<0xfd485300 0xc>;
reg-names = "pmu_base", "clk_base", "halt_base";
vdd_pronto_pll-supply = <&pm8941_l12>;
+ interrupts = <0 231 1>;
qcom,firmware-name = "wcnss";
};
diff --git a/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt b/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt
index d39c98c..ac9600d 100644
--- a/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt
+++ b/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt
@@ -11,6 +11,7 @@
memory mapped registers.
- reg-names: Names of the bases for the above registers. "qdsp6_base"
and "halt_base" are expected.
+- interrupts: The lpass watchdog interrupt
- qcom,firmware-name: Base name of the firmware image. Ex. "lpass"
Example:
@@ -19,6 +20,7 @@
reg = <0xfe200000 0x00100>,
<0xfd485100 0x00010>;
reg-names = "qdsp6_base", "halt_base";
+ interrupts = <0 194 1>;
qcom,firmware-name = "lpass";
};
diff --git a/Documentation/devicetree/bindings/platform/msm/qpnp-power-on.txt b/Documentation/devicetree/bindings/platform/msm/qpnp-power-on.txt
index 21d376a..adb93b8 100644
--- a/Documentation/devicetree/bindings/platform/msm/qpnp-power-on.txt
+++ b/Documentation/devicetree/bindings/platform/msm/qpnp-power-on.txt
@@ -11,18 +11,24 @@
- compatible: Must be "qcom,qpnp-power-on"
- reg: Specifies the SPMI address and size for this PON (power-on) peripheral
- interrupts: Specifies the interrupt associated with PON.
+- interrupt-names: Specify the interrupt names associated with interrupts. Must be
+ one of "kpdpwr", "kpdpwr-bark", "resin", "resin-bark", "cblpwr".
+ Bark interrupts are associated with system reset configuration
+ to allow default reset configuration to be activated. If system
+ reset configuration is not supported then bark interrupts are
+ nops.
Optional properties:
-- qcom,pon-dbc-delay The debouce delay for the power-key interrupt
- specifed in us. The value ranges from 2 seconds
+- qcom,pon-dbc-delay The debounce delay for the power-key interrupt
+ specified in us. The value ranges from 2 seconds
to 1/64 of a second. Possible values are -
- 2, 1, 1/2, 1/4, 1/8, 1/16, 1/32, 1/64
- Intermediate value is rounded down to the
nearest valid value.
- qcom,pon_1 ...pon_n These represent the child nodes which describe
the properties (reset, key) for each of the pon
- reset source. All the child nodes are optional,
- if none of them are specified the driver fails
+ reset source. All the child nodes are optional.
+ If none of them is specified, the driver fails
to register.
- qcom,system-reset Specifies that this PON peripheral can be used
to reset the system. This property can only be
@@ -32,32 +38,32 @@
All the below properties are in the sub-node section (properties of the child
node).
+Sub-node required properties:
+- qcom,pon-type The type of PON/RESET source. The driver
+ currently supports KPDPWR(0), RESIN(1) and
+ CBLPWR(2) pon/reset sources.
+
+Sub-node optional properties:
- qcom,pull-up The initial state of the reset pin under
consideration.
0 = No pull-up
1 = pull-up enabled
- This property is optional and is set to '0'
- if not specified.
-- qcom,pon-type The type of PON/RESET source. The driver
- currently supports KPDPWR(0) and RESIN(1)
- pon/reset sources. This property must be
- specified.
+ This property is set to '0' if not specified.
- qcom,support-reset Indicates if this PON source supports
reset functionality.
0 = Not supported
1 = Supported
- This property is optional and is set to '0'
- if not specified.
-- qcom,s1-timer The debouce timer for the BARK interrupt for
+ This property is set to '0' if not specified.
+- qcom,s1-timer The debounce timer for the BARK interrupt for
that reset source. Value is specified in ms.
Supported values are -
- 0, 32, 56, 80, 128, 184, 272, 408, 608, 904
1352, 2048, 3072, 4480, 6720, 10256
This property must be specified only if
'support-reset' is set to 1.
-- qcom,s2-timer The debouce timer for the S2 reset specified
+- qcom,s2-timer The debounce timer for the S2 reset specified
in ms. On the expiry of this timer, the PMIC
- executes the reset sequence. Supoprted values -
+ executes the reset sequence. Supported values -
- 0, 10, 50, 100, 250, 500, 1000, 2000
This property is required only if
'support-reset' is set to 1.
@@ -68,7 +74,7 @@
'support-reset' is set to 1.
- linux,code The input key-code associated with the reset source.
The reset source in its default configuration can be
- used to support standard keys. This property is optional.
+ used to support standard keys.
Example:
qcom,power-on@800 {
diff --git a/Documentation/devicetree/bindings/qseecom/qseecom.txt b/Documentation/devicetree/bindings/qseecom/qseecom.txt
index 8b17ba9..5e7c42a 100644
--- a/Documentation/devicetree/bindings/qseecom/qseecom.txt
+++ b/Documentation/devicetree/bindings/qseecom/qseecom.txt
@@ -2,9 +2,21 @@
Required properties:
- compatible : Should be "qcom,qseecom"
+- qcom, msm_bus,name: Should be "qseecom-noc"
+- qcom, msm_bus,num_cases: Depends on the use cases for bus scaling
+- qcom, msm_bus,num_paths: The paths for source and destination ports
+- qcom, msm_bus,vectors: Vectors for bus topology.
Example:
-
qcom,qseecom@fe806000 {
compatible = "qcom,qseecom";
+ qcom,msm_bus,name = "qseecom-noc";
+ qcom,msm_bus,num_cases = <4>;
+ qcom,msm_bus,active_only = <0>;
+ qcom,msm_bus,num_paths = <1>;
+ qcom,msm_bus,vectors =
+ <55 512 0 0>,
+ <55 512 3936000000 393600000>,
+ <55 512 3936000000 393600000>,
+ <55 512 3936000000 393600000>;
};
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index 2864fd1..9743d0d 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -54,6 +54,16 @@
- compatible : "qcom,msm-pcm-afe"
+* msm-dai-q6-hdmi
+
+Required properties:
+ - compatible : "msm-dai-q6-hdmi"
+ - qcom,msm-dai-q6-dev-id : The hdmi multi channel port ID.
+ It is passed onto the dsp from the apps to form an audio
+ path to the HDMI device. Currently the only supported value
+ is 8, which indicates the rx path used for audio playback
+ on HDMI device.
+
* msm-dai-q6
[First Level Nodes]
@@ -71,6 +81,7 @@
Value is from 16384 to 16393
BT SCO port ID value from 12288 to 12289
RT Proxy port ID values from 224 to 225 and 240 to 241
+ FM Rx and TX port ID values from 12292 to 12293
* msm-auxpcm
@@ -123,15 +134,36 @@
- compatible : "qcom,msm-ocmem-audio"
- - qcom,msm-ocmem-audio-src-id: Master port id
+ - qcom,msm_bus,name: Client name
- - qcom,msm-ocmem-audio-dst-id: Slave port id
+ - qcom,msm_bus,num_cases: Total number of use cases
- - qcom,msm-ocmem-audio-ab: arbitrated bandwidth
- in Bytes/s
+ - qcom,msm_bus,active_only: Context flag for requests in active or
+ dual (active & sleep) contex
- - qcom,msm-ocmem-audio-ib: instantaneous bandwidth
- in Bytes/s
+ - qcom,msm_bus,num_paths: Total number of master-slave pairs
+
+ - qcom,msm_bus,vectors: Arrays of unsigned integers representing:
+ master-id, slave-id, arbitrated bandwidth,
+ instantaneous bandwidth
+* wcd9xxx_intc
+
+Required properties:
+
+ - compatible : "qcom,wcd9xxx-irq"
+
+ - interrupt-controller : Mark this device node as an interrupt
+ controller
+
+ - #interrupt-cells : Should be 1
+
+ - interrupt-parent : Parent interrupt controller
+
+ - interrupts : Interrupt number on the parent
+ interrupt controller
+
+ - interrupt-names : Name of interrupt on the parent
+ interrupt controller
Example:
@@ -163,6 +195,11 @@
compatible = "qcom,msm-dai-fe";
};
+ qcom,msm-dai-q6-hdmi {
+ compatible = "qcom,msm-dai-q6-hdmi";
+ qcom,msm-dai-q6-dev-id = <8>;
+ };
+
qcom,msm-dai-q6 {
compatible = "qcom,msm-dai-q6";
qcom,msm-dai-q6-sb-0-rx {
@@ -185,6 +222,16 @@
qcom,msm-dai-q6-dev-id = <12289>;
};
+ qcom,msm-dai-q6-int-fm-rx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <12292>;
+ };
+
+ qcom,msm-dai-q6-int-fm-tx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <12293>;
+ };
+
qcom,msm-dai-q6-be-afe-pcm-rx {
compatible = "qcom,msm-dai-q6-dev";
qcom,msm-dai-q6-dev-id = <224>;
@@ -234,10 +281,22 @@
qcom,msm-ocmem-audio {
compatible = "qcom,msm-ocmem-audio";
- qcom,msm-ocmem-audio-src-id = <11>;
- qcom,msm-ocmem-audio-dst-id = <604>;
- qcom,msm-ocmem-audio-ab = <209715200>;
- qcom,msm-ocmem-audio-ib = <471859200>;
+ qcom,msm_bus,name = "audio-ocmem";
+ qcom,msm_bus,num_cases = <2>;
+ qcom,msm_bus,active_only = <0>;
+ qcom,msm_bus,num_paths = <1>;
+ qcom,msm_bus,vectors =
+ <11 604 0 0>,
+ <11 604 32505856 325058560>;
+ };
+
+ wcd9xxx_intc: wcd9xxx-irq {
+ compatible = "qcom,wcd9xxx-irq";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <72 0>;
+ interrupt-names = "cdc-int";
};
* MSM8974 ASoC Machine driver
diff --git a/Documentation/devicetree/bindings/thermal/qpnp-temp-alarm.txt b/Documentation/devicetree/bindings/thermal/qpnp-temp-alarm.txt
new file mode 100644
index 0000000..19fbd3a
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/qpnp-temp-alarm.txt
@@ -0,0 +1,66 @@
+Qualcomm QPNP Temperature Alarm
+
+QPNP temperature alarm peripherals are found inside of Qualcomm PMIC chips that
+utilize the MSM SPMI implementation. These peripherals provide an interrupt
+signal and status register to identify high PMIC die temperature.
+
+Required properties:
+- compatible: Must be "qcom,qpnp-temp-alarm".
+- reg: Specifies the SPMI address and size for this temperature
+ alarm device.
+- interrupts: PMIC temperature alarm interrupt
+- label: A string used as a descriptive name for this thermal device.
+ This name should be 19 characters or less.
+
+Required structure:
+- A qcom,qpnp-temp-alarm node must be a child of an SPMI node that has specified
+ the spmi-slave-container property
+
+Optional properties:
+- qcom,channel-num: VADC channel number associated PMIC DIE_TEMP thermistor.
+ If no channel is specified, then the die temperature
+ must be estimated based on the over temperature stage.
+- qcom,threshold-set: Integer value which specifies which set of threshold
+ temperatures to use for the over temperature stages.
+ Possible values (x = {stage 1 threshold temperature,
+ stage 2 threshold temperature,
+ stage 3 threshold temperature}):
+ 0 = {105 C, 125 C, 145 C}
+ 1 = {110 C, 130 C, 150 C}
+ 2 = {115 C, 135 C, 155 C}
+ 3 = {120 C, 140 C, 160 C}
+- qcom,allow-override: Boolean which controls the ability of software to
+ override shutdowns. If present, then software is
+ allowed to override automatic PMIC hardware stage 2 and
+ stage 3 over temperature shutdowns. Otherwise, software
+ is not allowed to override automatic shutdown.
+- qcom,default-temp: Specifies the default temperature in millicelcius to use
+ if no ADC channel is present to read the real time
+ temperature.
+
+Note, if a given optional qcom,* binding is not present, then the default
+hardware state for that feature will be maintained.
+
+Example:
+&spmi_bus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupt-controller;
+ #interrupt-cells = <3>;
+
+ qcom,pm8941@0 {
+ spmi-slave-container;
+ reg = <0x0>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ qcom,temp-alarm@2400 {
+ compatible = "qcom,qpnp-temp-alarm";
+ reg = <0x2400 0x100>;
+ interrupts = <0x0 0x24 0x0>;
+ label = "pm8941_tz";
+ qcom,channel-num = <8>;
+ qcom,threshold-set = <0>;
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/thermal/tsens.txt b/Documentation/devicetree/bindings/thermal/tsens.txt
index c683f58..0682cd1 100644
--- a/Documentation/devicetree/bindings/thermal/tsens.txt
+++ b/Documentation/devicetree/bindings/thermal/tsens.txt
@@ -17,10 +17,12 @@
- reg : offset and length of the QFPROM registers used for storing
the calibration data for the individual sensors.
- reg-names : resource names used for the physical address of the TSENS
- registers and the QFPROM efuse calibration address.
- Should be "tsens_physical" for physical address of the TSENS
- and "tsens_eeprom_physical" for physical address where calibration
- data is stored.
+ registers, the QFPROM efuse primary calibration address region,
+ Should be "tsens_physical" for physical address of the TSENS,
+ "tsens_eeprom_physical" for physical address where primary
+ calibration data is stored. This includes the backup
+ calibration address region if TSENS calibration data is stored
+ in the region.
- interrupts : TSENS interrupt for cool/warm temperature threshold.
- qcom,sensors : Total number of available Temperature sensors for TSENS.
- qcom,slope : One point calibration characterized slope data for each
@@ -28,14 +30,20 @@
as ADC code/DegC and the value is multipled by a factor
of 1000.
+Optional properties:
+- qcom,calibration-less-mode : If present the pre-characterized data for offsets
+ are used else it defaults to use calibration data from QFPROM.
+
Example:
tsens@fc4a8000 {
compatible = "qcom,msm-tsens";
reg = <0xfc4a8000 0x2000>,
- <0xfc4b80d0 0x5>;
- reg-names = "tsens_physical", "tsens_eeprom_physical";
+ <0xfc4b8000 0x1000>;
+ reg-names = "tsens_physical",
+ "tsens_eeprom_physical";
interrupts = <0 184 0>;
+ qcom,calibration-less-mode;
qcom,sensors = <11>;
qcom,slope = <1134 1122 1142 1123 1176 1176 1176 1186 1176
1176>;
diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
index 9c2ce6c..186a58d 100644
--- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
@@ -6,6 +6,8 @@
- compatible : should be "qcom,hsusb-otg"
- regs : offset and length of the register set in the memory map
- interrupts: IRQ line
+- interrupt-names: OTG interrupt name(s) referenced in interrupts above
+ HSUSB OTG expects "core_irq" and optionally "async_irq".
- qcom,hsusb-otg-phy-type: PHY type can be one of
1 - Chipidea 45nm PHY
2 - Synopsis 28nm PHY
@@ -20,10 +22,13 @@
1 - PHY control
2 - PMIC control
3 - User control (via debugfs)
-- qcom,hsusb-otg-disable-reset: It present then core is RESET only during
- init, otherwise core is RESET for every cable disconnect as well
Optional properties :
+- qcom,hsusb-otg-disable-reset: If present then core is RESET only during
+ init, otherwise core is RESET for every cable disconnect as well
+- qcom,hsusb-otg-pnoc-errata-fix: If present then workaround for PNOC
+ performance issue is applied which requires changing the mem-type
+ attribute via VMIDMT.
- qcom,hsusb-otg-default-mode: The default USB mode after boot-up.
Applicable only when OTG is controlled by user. Can be one of
0 - None. Low power mode
@@ -48,11 +53,13 @@
compatible = "qcom,hsusb-otg";
reg = <0xf9690000 0x400>;
interrupts = <134>;
+ interrupt-names = "core_irq";
qcom,hsusb-otg-phy-type = <2>;
qcom,hsusb-otg-mode = <1>;
qcom,hsusb-otg-otg-control = <1>;
qcom,hsusb-otg-disable-reset;
+ qcom,hsusb-otg-pnoc-errata-fix;
qcom,hsusb-otg-default-mode = <2>;
qcom,hsusb-otg-phy-init-seq = <0x01 0x90 0xffffffff>;
qcom,hsusb-otg-power-budget = <500>;
diff --git a/Documentation/mmc/mmc-dev-attrs.txt b/Documentation/mmc/mmc-dev-attrs.txt
index 22ae844..7dde34f 100644
--- a/Documentation/mmc/mmc-dev-attrs.txt
+++ b/Documentation/mmc/mmc-dev-attrs.txt
@@ -8,6 +8,32 @@
force_ro Enforce read-only access even if write protect switch is off.
+ num_wr_reqs_to_start_packing This attribute is used to determine
+ the trigger for activating the write packing, in case the write
+ packing control feature is enabled.
+
+ When the MMC manages to reach a point where num_wr_reqs_to_start_packing
+ write requests could be packed, it enables the write packing feature.
+ This allows us to start the write packing only when it is beneficial
+ and has minimum affect on the read latency.
+
+ The number of potential packed requests that will trigger the packing
+ can be configured via sysfs by writing the required value to:
+ /sys/block/<block_dev_name>/num_wr_reqs_to_start_packing.
+
+ The default value of num_wr_reqs_to_start_packing was determined by
+ running parallel lmdd write and lmdd read operations and calculating
+ the max number of packed writes requests.
+
+ min_sectors_to_check_bkops_status This attribute is used to
+ determine whether the status bit that indicates the need for BKOPS
+ should be checked. The value is stored in this attribute represents
+ the minimum number of sectors that needs to be changed in the device
+ (written or discarded) in order to require the status-bit of BKOPS
+ to be checked. The value can modified via sysfs by writing the
+ required value to:
+ /sys/block/<block_dev_name>/min_sectors_to_check_bkops_status
+
SD and MMC Device Attributes
============================
diff --git a/arch/arm/boot/dts/mpq8092-ion.dtsi b/arch/arm/boot/dts/mpq8092-ion.dtsi
new file mode 100644
index 0000000..2cd2f7b
--- /dev/null
+++ b/arch/arm/boot/dts/mpq8092-ion.dtsi
@@ -0,0 +1,77 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/ {
+ qcom,ion {
+ compatible = "qcom,msm-ion";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,ion-heap@30 { /* SYSTEM HEAP */
+ reg = <30>;
+ };
+
+ qcom,ion-heap@8 { /* CP_MM HEAP */
+ compatible = "qcom,msm-ion-reserve";
+ reg = <8>;
+ qcom,heap-align = <0x1000>;
+ qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+ qcom,memory-reservation-size = <0x7800000>;
+ };
+
+ qcom,ion-heap@29 { /* FIRMWARE HEAP */
+ compatible = "qcom,msm-ion-reserve";
+ reg = <29>;
+ qcom,heap-align = <0x20000>;
+ qcom,heap-adjacent = <8>;
+ qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+ qcom,memory-reservation-size = <0xA00000>;
+ };
+
+ qcom,ion-heap@12 { /* MFC HEAP */
+ compatible = "qcom,msm-ion-reserve";
+ reg = <12>;
+ qcom,heap-align = <0x1000>;
+ qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+ qcom,memory-reservation-size = <0x2000>;
+ };
+
+ qcom,ion-heap@24 { /* SF HEAP */
+ compatible = "qcom,msm-ion-reserve";
+ reg = <24>;
+ qcom,heap-align = <0x1000>;
+ qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+ qcom,memory-reservation-size = <0x2800000>;
+ };
+
+ qcom,ion-heap@25 { /* IOMMU HEAP */
+ reg = <25>;
+ };
+
+ qcom,ion-heap@27 { /* QSECOM HEAP */
+ compatible = "qcom,msm-ion-reserve";
+ reg = <27>;
+ qcom,heap-align = <0x1000>;
+ qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+ qcom,memory-reservation-size = <0x600000>;
+ };
+
+ qcom,ion-heap@28 { /* AUDIO HEAP */
+ compatible = "qcom,msm-ion-reserve";
+ reg = <28>;
+ qcom,heap-align = <0x1000>;
+ qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+ qcom,memory-reservation-size = <0x2B4000>;
+ };
+ };
+};
+
diff --git a/arch/arm/boot/dts/mpq8092-regulator.dtsi b/arch/arm/boot/dts/mpq8092-regulator.dtsi
new file mode 100644
index 0000000..fbc9586
--- /dev/null
+++ b/arch/arm/boot/dts/mpq8092-regulator.dtsi
@@ -0,0 +1,290 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/* QPNP controlled regulators: */
+
+&spmi_bus {
+
+ qcom,pm8644@1 {
+
+ pm8644_s3: regulator@1a00 {
+ regulator-min-microvolt = <1350000>;
+ regulator-max-microvolt = <1350000>;
+ qcom,enable-time = <500>;
+ qcom,pull-down-enable = <1>;
+ regulator-always-on;
+ qcom,system-load = <100000>;
+ status = "okay";
+ };
+
+ pm8644_s4: regulator@1d00 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,enable-time = <500>;
+ qcom,pull-down-enable = <1>;
+ regulator-always-on;
+ qcom,system-load = <100000>;
+ status = "okay";
+ };
+
+ pm8644_s5: regulator@2000 {
+ regulator-min-microvolt = <2150000>;
+ regulator-max-microvolt = <2150000>;
+ qcom,enable-time = <500>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_s6: regulator@2300 {
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+ qcom,enable-time = <500>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_s7: regulator@2600 {
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+ qcom,enable-time = <500>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_s8: regulator@2900 {
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+ qcom,enable-time = <500>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l1: regulator@4000 {
+ parent-supply = <&pm8644_s3>;
+ regulator-min-microvolt = <1225000>;
+ regulator-max-microvolt = <1225000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ regulator-always-on;
+ qcom,system-load = <10000>;
+ status = "okay";
+ };
+
+ pm8644_l2: regulator@4100 {
+ parent-supply = <&pm8644_s3>;
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l3: regulator@4200 {
+ parent-supply = <&pm8644_s3>;
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l4: regulator@4300 {
+ parent-supply = <&pm8644_s3>;
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l6: regulator@4500 {
+ parent-supply = <&pm8644_s5>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l8: regulator@4700 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l9: regulator@4800 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <2950000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l10: regulator@4900 {
+ regulator-min-microvolt = <2000000>;
+ regulator-max-microvolt = <2000000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l11: regulator@4a00 {
+ parent-supply = <&pm8644_s3>;
+ regulator-min-microvolt = <1300000>;
+ regulator-max-microvolt = <1300000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l12: regulator@4b00 {
+ parent-supply = <&pm8644_s5>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l13: regulator@4c00 {
+ regulator-min-microvolt = <2950000>;
+ regulator-max-microvolt = <2950000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l14: regulator@4d00 {
+ parent-supply = <&pm8644_s5>;
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l15: regulator@4e00 {
+ parent-supply = <&pm8644_s5>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l16: regulator@4f00 {
+ parent-supply = <&pm8644_s4>;
+ regulator-min-microvolt = <750000>;
+ regulator-max-microvolt = <750000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l17: regulator@5000 {
+ regulator-min-microvolt = <3150000>;
+ regulator-max-microvolt = <3150000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ regulator-always-on;
+ qcom,system-load = <100000>;
+ status = "okay";
+ };
+
+ pm8644_l18: regulator@5100 {
+ regulator-min-microvolt = <2850000>;
+ regulator-max-microvolt = <2850000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l19: regulator@5200 {
+ parent-supply = <&pm8644_s4>;
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <1500000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l20: regulator@5300 {
+ regulator-min-microvolt = <2950000>;
+ regulator-max-microvolt = <2950000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l21: regulator@5400 {
+ regulator-min-microvolt = <2950000>;
+ regulator-max-microvolt = <2950000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l22: regulator@5500 {
+ regulator-min-microvolt = <2500000>;
+ regulator-max-microvolt = <2500000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l23: regulator@5600 {
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l24: regulator@5700 {
+ regulator-min-microvolt = <3075000>;
+ regulator-max-microvolt = <3075000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_lvs1: regulator@8000 {
+ parent-supply = <&pm8644_s4>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_lvs2: regulator@8100 {
+ parent-supply = <&pm8644_s4>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_mvs1: regulator@8200 {
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_mvs2: regulator@8300 {
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+ };
+};
+
diff --git a/arch/arm/boot/dts/mpq8092-sim.dts b/arch/arm/boot/dts/mpq8092-sim.dts
index ac984a1..0cbfa33 100644
--- a/arch/arm/boot/dts/mpq8092-sim.dts
+++ b/arch/arm/boot/dts/mpq8092-sim.dts
@@ -26,6 +26,155 @@
serial@f995e000 {
status = "ok";
};
-
};
+&pm8644_gpios {
+ gpio@c000 { /* GPIO 1 */
+ };
+
+ gpio@c100 { /* GPIO 2 */
+ };
+
+ gpio@c200 { /* GPIO 3 */
+ };
+
+ gpio@c300 { /* GPIO 4 */
+ };
+
+ gpio@c400 { /* GPIO 5 */
+ };
+
+ gpio@c500 { /* GPIO 6 */
+ };
+
+ gpio@c600 { /* GPIO 7 */
+ };
+
+ gpio@c700 { /* GPIO 8 */
+ };
+
+ gpio@c800 { /* GPIO 9 */
+ };
+
+ gpio@c900 { /* GPIO 10 */
+ };
+
+ gpio@ca00 { /* GPIO 11 */
+ };
+
+ gpio@cb00 { /* GPIO 12 */
+ };
+
+ gpio@cc00 { /* GPIO 13 */
+ };
+
+ gpio@cd00 { /* GPIO 14 */
+ };
+
+ gpio@ce00 { /* GPIO 15 */
+ };
+
+ gpio@cf00 { /* GPIO 16 */
+ };
+
+ gpio@d000 { /* GPIO 17 */
+ };
+
+ gpio@d100 { /* GPIO 18 */
+ };
+
+ gpio@d200 { /* GPIO 19 */
+ };
+
+ gpio@d300 { /* GPIO 20 */
+ };
+
+ gpio@d400 { /* GPIO 21 */
+ };
+
+ gpio@d500 { /* GPIO 22 */
+ };
+
+ gpio@d600 { /* GPIO 23 */
+ };
+
+ gpio@d700 { /* GPIO 24 */
+ };
+
+ gpio@d800 { /* GPIO 25 */
+ };
+
+ gpio@d900 { /* GPIO 26 */
+ };
+
+ gpio@da00 { /* GPIO 27 */
+ };
+
+ gpio@db00 { /* GPIO 28 */
+ };
+
+ gpio@dc00 { /* GPIO 29 */
+ };
+
+ gpio@dd00 { /* GPIO 30 */
+ };
+
+ gpio@de00 { /* GPIO 31 */
+ };
+
+ gpio@df00 { /* GPIO 32 */
+ };
+
+ gpio@e000 { /* GPIO 33 */
+ };
+
+ gpio@e100 { /* GPIO 34 */
+ };
+
+ gpio@e200 { /* GPIO 35 */
+ };
+
+ gpio@e300 { /* GPIO 36 */
+ };
+
+ gpio@e400 { /* GPIO 37 */
+ };
+
+ gpio@e500 { /* GPIO 38 */
+ };
+
+ gpio@e600 { /* GPIO 39 */
+ };
+
+ gpio@e700 { /* GPIO 40 */
+ };
+
+ gpio@e800 { /* GPIO 41 */
+ };
+
+ gpio@e900 { /* GPIO 42 */
+ };
+
+ gpio@ea00 { /* GPIO 43 */
+ };
+};
+
+&pm8644_mpps {
+ mpp@a000 { /* MPP 1 */
+ };
+
+ mpp@a100 { /* MPP 2 */
+ };
+
+ mpp@a200 { /* MPP 3 */
+ };
+
+ mpp@a300 { /* MPP 4 */
+ };
+
+ mpp@a400 { /* MPP5 */
+ };
+
+ mpp@a500 { /* MPP 6 */
+ };
+};
diff --git a/arch/arm/boot/dts/mpq8092.dtsi b/arch/arm/boot/dts/mpq8092.dtsi
index 9b51ceb..252b9f5 100644
--- a/arch/arm/boot/dts/mpq8092.dtsi
+++ b/arch/arm/boot/dts/mpq8092.dtsi
@@ -13,6 +13,7 @@
/include/ "skeleton.dtsi"
/include/ "mpq8092-iommu.dtsi"
/include/ "msm-gdsc.dtsi"
+/include/ "mpq8092-ion.dtsi"
/ {
model = "Qualcomm MPQ8092";
@@ -55,4 +56,179 @@
interrupts = <0 114 0>;
status = "disabled";
};
+
+ spmi_bus: qcom,spmi@fc4c0000 {
+ cell-index = <0>;
+ compatible = "qcom,spmi-pmic-arb";
+ reg = <0xfc4cf000 0x1000>,
+ <0Xfc4cb000 0x1000>;
+ /* 190,ee0_krait_hlos_spmi_periph_irq */
+ /* 187,channel_0_krait_hlos_trans_done_irq */
+ interrupts = <0 190 0 0 187 0>;
+ qcom,pmic-arb-ee = <0>;
+ qcom,pmic-arb-channel = <0>;
+ qcom,pmic-arb-ppid-map = <0x00100000>, /* PM8644_0 */
+ <0x10100001>, /* PM8644_1 */
+ <0x00500002>, /* INTERRUPT */
+ <0x00800003>, /* PON0 */
+ <0x03000004>, /* ADC_1 */
+ <0x03100005>, /* ADC_2 */
+ <0x03200006>, /* ADC_3 */
+ <0x03300007>, /* ADC_4 */
+ <0x03400008>, /* ADC_5 */
+ <0x03500009>, /* ADC_6 */
+ <0x0360000a>, /* ADC_7 */
+ <0x0370000b>, /* ADC_8 */
+ <0x0500000c>, /* SHARED_XO */
+ <0x0510000d>, /* BB_CLK1 */
+ <0x0520000e>, /* BB_CLK2 */
+ <0x05a0000f>, /* SLEEP_CLK */
+ <0x06000010>, /* RTC_RW */
+ <0x06100011>, /* RTC_ALARM */
+ <0x07000012>, /* PBS_CORE */
+ <0x07100013>, /* PBS_CLIENT_1 */
+ <0x07200014>, /* PBS_CLIENT_2 */
+ <0x07300015>, /* PBS_CLIENT_3 */
+ <0x07400016>, /* PBS_CLIENT_4 */
+ <0x07500017>, /* PBS_CLIENT_5 */
+ <0x07600018>, /* PBS_CLIENT_6 */
+ <0x07700019>, /* PBS_CLIENT_7 */
+ <0x0780001a>, /* PBS_CLIENT_8 */
+ <0x0790001b>, /* PBS_CLIENT_9 */
+ <0x07a0001c>, /* PBS_CLIENT_10 */
+ <0x07b0001d>, /* PBS_CLIENT_11 */
+ <0x07c0001e>, /* PBS_CLIENT_12 */
+ <0x07d0001f>, /* PBS_CLIENT_13 */
+ <0x07e00020>, /* PBS_CLIENT_14 */
+ <0x07f00021>, /* PBS_CLIENT_15 */
+ <0x08000022>, /* PBS_CLIENT_16 */
+ <0x0a000023>, /* MPP_1 */
+ <0x0a100024>, /* MPP_2 */
+ <0x0a200025>, /* MPP_3 */
+ <0x0a300026>, /* MPP_4 */
+ <0x0a400027>, /* MPP_5 */
+ <0x0a500028>, /* MPP_6 */
+ <0x0c000029>, /* PM8644_GPIO_1 */
+ <0x0c10002a>, /* PM8644_GPIO_2 */
+ <0x0c20002b>, /* PM8644_GPIO_3 */
+ <0x0c30002c>, /* PM8644_GPIO_4 */
+ <0x0c40002d>, /* PM8644_GPIO_5 */
+ <0x0c50002e>, /* PM8644_GPIO_6 */
+ <0x0c60002f>, /* PM8644_GPIO_7 */
+ <0x0c700030>, /* PM8644_GPIO_8 */
+ <0x0c800031>, /* PM8644_GPIO_9 */
+ <0x0c900032>, /* PM8644_GPIO_10 */
+ <0x0ca00033>, /* PM8644_GPIO_11 */
+ <0x0cb00034>, /* PM8644_GPIO_12 */
+ <0x0cc00035>, /* PM8644_GPIO_13 */
+ <0x0cd00036>, /* PM8644_GPIO_14 */
+ <0x0ce00037>, /* PM8644_GPIO_15 */
+ <0x0cf00038>, /* PM8644_GPIO_16 */
+ <0x0d000039>, /* PM8644_GPIO_17 */
+ <0x0d10003a>, /* PM8644_GPIO_18 */
+ <0x0d20003b>, /* PM8644_GPIO_19 */
+ <0x0d30003c>, /* PM8644_GPIO_20 */
+ <0x0d40003d>, /* PM8644_GPIO_21 */
+ <0x0d50003e>, /* PM8644_GPIO_22 */
+ <0x0d60003f>, /* PM8644_GPIO_23 */
+ <0x0d700040>, /* PM8644_GPIO_24 */
+ <0x0d800041>, /* PM8644_GPIO_25 */
+ <0x0d900042>, /* PM8644_GPIO_26 */
+ <0x0da00043>, /* PM8644_GPIO_27 */
+ <0x0db00044>, /* PM8644_GPIO_28 */
+ <0x0dc00045>, /* PM8644_GPIO_29 */
+ <0x0dd00046>, /* PM8644_GPIO_30 */
+ <0x0de00047>, /* PM8644_GPIO_31 */
+ <0x0df00048>, /* PM8644_GPIO_32 */
+ <0x0e000049>, /* PM8644_GPIO_33 */
+ <0x0e10004a>, /* PM8644_GPIO_34 */
+ <0x0e20004b>, /* PM8644_GPIO_35 */
+ <0x0e30004c>, /* PM8644_GPIO_36 */
+ <0x0e40004d>, /* PM8644_GPIO_37 */
+ <0x0e50004e>, /* PM8644_GPIO_38 */
+ <0x0e60004f>, /* PM8644_GPIO_39 */
+ <0x0e700050>, /* PM8644_GPIO_40 */
+ <0x0e800051>, /* PM8644_GPIO_41 */
+ <0x0e900052>, /* PM8644_GPIO_42 */
+ <0x0ea00053>, /* PM8644_GPIO_43 */
+ <0x11000054>, /* BUCK_CMN_1 */
+ <0x11100055>, /* BUCK_CMN_2 */
+ <0x11200056>, /* BUCK_CMN_3 */
+ <0x11400057>, /* PM8644_SMPS1 */
+ <0x11500058>, /* SMPS_1_PS1 */
+ <0x11600059>, /* BUCK_FREQ_1 */
+ <0x1170005a>, /* PM8644_SMPS2 */
+ <0x1180005b>, /* SMPS_2_PS1 */
+ <0x1190005c>, /* BUCK_FREQ_2 */
+ <0x11a0005d>, /* PM8644_SMPS3 */
+ <0x11b0005e>, /* SMPS_3_PS1 */
+ <0x11c0005f>, /* BUCK_FREQ_3 */
+ <0x11d00060>, /* PM8644_SMPS4 */
+ <0x11e00061>, /* SMPS_4_PS1 */
+ <0x11f00062>, /* PM8644_BUCK_FREQ_4 */
+ <0x12000063>, /* PM8644_SMPS5 */
+ <0x12100064>, /* FTPS1_5 */
+ <0x12200065>, /* PM8644_BUCK_FREQ_5 */
+ <0x12300066>, /* PM8644_SMPS6 */
+ <0x12400067>, /* FTPS1_6 */
+ <0x12500068>, /* PM8644_BUCK_FREQ_6 */
+ <0x12600069>, /* PM8644_SMPS7 */
+ <0x1270006a>, /* FTPS1_7 */
+ <0x1280006b>, /* PM8644_BUCK_FREQ_7 */
+ <0x1290006c>, /* PM8644_SMPS8 */
+ <0x12a0006d>, /* FTPS1_8 */
+ <0x12b0006e>, /* PM8644_BUCK_FREQ_8 */
+ <0x12c0006f>, /* PM8644_SMPS9 */
+ <0x12d00070>, /* FTPS1_9 */
+ <0x12e00071>, /* PM8644_BUCK_FREQ_9 */
+ <0x12f00072>, /* PM8644_SMPS10 */
+ <0x13000073>, /* FTPS1_10 */
+ <0x13100074>, /* PM8644_BUCK_FREQ_10 */
+ <0x13200075>, /* PM8644_SMPS11 */
+ <0x13300076>, /* FTPS1_11 */
+ <0x13400077>, /* BUCK_FREQ_11 */
+ <0x14000078>, /* PM8644_LDO_1 */
+ <0x14100079>, /* PM8644_LDO_2 */
+ <0x1420007a>, /* PM8644_LDO_3 */
+ <0x1430007b>, /* PM8644_LDO_4 */
+ <0x1440007c>, /* PM8644_LDO_5 */
+ <0x1450007d>, /* PM8644_LDO_6 */
+ <0x1460007e>, /* PM8644_LDO_7 */
+ <0x1470007f>, /* PM8644_LDO_8 */
+ <0x14800080>, /* PM8644_LDO_9 */
+ <0x14900081>, /* PM8644_LDO_10 */
+ <0x14a00082>, /* PM8644_LDO_11 */
+ <0x14b00083>, /* PM8644_LDO_12 */
+ <0x14c00084>, /* PM8644_LDO_13 */
+ <0x14d00085>, /* PM8644_LDO_14 */
+ <0x14e00086>, /* PM8644_LDO_15 */
+ <0x14f00087>, /* PM8644_LDO_16 */
+ <0x15000088>, /* PM8644_LDO_17 */
+ <0x15100089>, /* PM8644_LDO_18 */
+ <0x1520008a>, /* PM8644_LDO_19 */
+ <0x1530008b>, /* PM8644_LDO_20 */
+ <0x1540008c>, /* PM8644_LDO_21 */
+ <0x1550008d>, /* PM8644_LDO_22 */
+ <0x1560008e>, /* PM8644_LDO_23 */
+ <0x1570008f>, /* PM8644_LDO_24 */
+ <0x15800090>, /* PM8644_LDO_25 */
+ <0x18000091>, /* PM8644_LVS_1 */
+ <0x18100092>, /* PM8644_LVS_2 */
+ <0x18200093>, /* PM8644_OTG */
+ <0x18300094>, /* PM8644_HDMI */
+ <0x1a800095>, /* KEYPAD */
+ <0x1b000096>, /* LPG_LUT */
+ <0x1b100097>, /* LPG_CHAN_1 */
+ <0x1b200098>, /* LPG_CHAN_2 */
+ <0x1b300099>, /* LPG_CHAN_3 */
+ <0x1b40009a>, /* LPG_CHAN_4 */
+ <0x1b50009b>, /* LPG_CHAN_5 */
+ <0x1b60009c>, /* LPG_CHAN_6 */
+ <0x1b70009d>, /* LPG_CHAN_7 */
+ <0x1b80009e>, /* LPG_CHAN_8 */
+ <0x1bc0009f>; /* LPG_PWM */
+ };
};
+
+/include/ "msm-pm8644.dtsi"
+/include/ "mpq8092-regulator.dtsi"
diff --git a/arch/arm/boot/dts/msm-iommu.dtsi b/arch/arm/boot/dts/msm-iommu.dtsi
index e907de8..839199a 100755
--- a/arch/arm/boot/dts/msm-iommu.dtsi
+++ b/arch/arm/boot/dts/msm-iommu.dtsi
@@ -18,9 +18,40 @@
ranges;
reg = <0xfda64000 0x10000>;
vdd-supply = <&gdsc_jpeg>;
- qcom,iommu-smt-size = <16>;
status = "disabled";
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2050
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x2008
+ 0x200c
+ 0x2010
+ 0x2014>;
+
+ qcom,iommu-bfb-data = <0xffffffff
+ 0xffffffff
+ 0x4
+ 0x4
+ 0x0
+ 0x0
+ 0x10
+ 0x50
+ 0x0
+ 0x10
+ 0x20
+ 0x0
+ 0x0
+ 0x0
+ 0x0>;
+
qcom,iommu-ctx@fda6c000 {
reg = <0xfda6c000 0x1000>;
interrupts = <0 70 0>;
@@ -50,9 +81,47 @@
ranges;
reg = <0xfd928000 0x10000>;
vdd-supply = <&gdsc_mdss>;
- qcom,iommu-smt-size = <16>;
+ qcom,iommu-secure-id = <1>;
status = "disabled";
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2050
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2008
+ 0x200c
+ 0x2010
+ 0x2014
+ 0x2018
+ 0x201c
+ 0x2020>;
+
+ qcom,iommu-bfb-data = <0xffffffff
+ 0xffffffff
+ 0x00000004
+ 0x00000010
+ 0x00000000
+ 0x00000000
+ 0x00000034
+ 0x00000044
+ 0x0
+ 0x34
+ 0x74
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0>;
+
qcom,iommu-ctx@fd930000 {
reg = <0xfd930000 0x1000>;
interrupts = <0 47 0>;
@@ -75,10 +144,60 @@
ranges;
reg = <0xfdc84000 0x10000>;
vdd-supply = <&gdsc_venus>;
- qcom,iommu-smt-size = <16>;
+ qcom,iommu-secure-id = <0>;
qcom,needs-alt-core-clk;
status = "disabled";
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2050
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2008
+ 0x200c
+ 0x2010
+ 0x2014
+ 0x2018
+ 0x201c
+ 0x2020
+ 0x2024
+ 0x2028
+ 0x202c
+ 0x2030
+ 0x2034
+ 0x2038>;
+
+ qcom,iommu-bfb-data = <0xffffffff
+ 0xffffffff
+ 0x00000004
+ 0x00000008
+ 0x00000000
+ 0x00000000
+ 0x00000094
+ 0x000000b4
+ 0x0
+ 0x94
+ 0x114
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0>;
+
qcom,iommu-ctx@fdc8c000 {
reg = <0xfdc8c000 0x1000>;
interrupts = <0 42 0>;
@@ -108,10 +227,35 @@
ranges;
reg = <0xfdb10000 0x10000>;
vdd-supply = <&gdsc_oxili_cx>;
- qcom,iommu-smt-size = <32>;
qcom,needs-alt-core-clk;
status = "disabled";
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2050
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2008>;
+
+ qcom,iommu-bfb-data = <0xffffffff
+ 0xffffffff
+ 0x00000004
+ 0x00000010
+ 0x00000000
+ 0x00000000
+ 0x00000001
+ 0x00000021
+ 0x0
+ 0x1
+ 0x81
+ 0x0>;
+
qcom,iommu-ctx@fdb18000 {
reg = <0xfdb18000 0x1000>;
interrupts = <0 241 0>;
@@ -134,9 +278,46 @@
ranges;
reg = <0xfda44000 0x10000>;
vdd-supply = <&gdsc_vfe>;
- qcom,iommu-smt-size = <32>;
status = "disabled";
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2050
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x2008
+ 0x200c
+ 0x2010
+ 0x2014
+ 0x2018
+ 0x201c
+ 0x2020>;
+
+ qcom,iommu-bfb-data = <0xffffffff
+ 0xffffffff
+ 0x4
+ 0x8
+ 0x0
+ 0x0
+ 0x20
+ 0x78
+ 0x0
+ 0x20
+ 0x36
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0>;
+
qcom,iommu-ctx@fda4c000 {
reg = <0xfda4c000 0x1000>;
interrupts = <0 65 0>;
diff --git a/arch/arm/boot/dts/msm-pm8019.dtsi b/arch/arm/boot/dts/msm-pm8019.dtsi
index 3b06450..e70eb36 100755
--- a/arch/arm/boot/dts/msm-pm8019.dtsi
+++ b/arch/arm/boot/dts/msm-pm8019.dtsi
@@ -22,6 +22,21 @@
#address-cells = <1>;
#size-cells = <1>;
+ qcom,power_on@800 {
+ compatible = "qcom,qpnp-power-on";
+ reg = <0x800 0x100>;
+ interrupts = <0x0 0x8 0x2>;
+ interrupt-names = "cblpwr";
+ qcom,pon-dbc-delay = <15625>;
+ qcom,system-reset;
+
+ qcom,pon_1 {
+ qcom,pon-type = <2>;
+ qcom,pull-up = <1>;
+ linux,code = <116>;
+ };
+ };
+
clkdiv@5b00 {
reg = <0x5b00 0x100>;
compatible = "qcom,qpnp-clkdiv";
diff --git a/arch/arm/boot/dts/msm-pm8644.dtsi b/arch/arm/boot/dts/msm-pm8644.dtsi
new file mode 100644
index 0000000..17a6b0b
--- /dev/null
+++ b/arch/arm/boot/dts/msm-pm8644.dtsi
@@ -0,0 +1,722 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&spmi_bus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupt-controller;
+ #interrupt-cells = <3>;
+
+ qcom,pm8644@0 {
+ spmi-slave-container;
+ reg = <0x0>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ pm8644_gpios: gpios {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-pin";
+ gpio-controller;
+ #gpio-cells = <2>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ label = "pm8644-gpio";
+
+ gpio@c000 {
+ reg = <0xc000 0x100>;
+ qcom,pin-num = <1>;
+ };
+
+ gpio@c100 {
+ reg = <0xc100 0x100>;
+ qcom,pin-num = <2>;
+ };
+
+ gpio@c200 {
+ reg = <0xc200 0x100>;
+ qcom,pin-num = <3>;
+ };
+
+ gpio@c300 {
+ reg = <0xc300 0x100>;
+ qcom,pin-num = <4>;
+ };
+
+ gpio@c400 {
+ reg = <0xc400 0x100>;
+ qcom,pin-num = <5>;
+ };
+
+ gpio@c500 {
+ reg = <0xc500 0x100>;
+ qcom,pin-num = <6>;
+ };
+
+ gpio@c600 {
+ reg = <0xc600 0x100>;
+ qcom,pin-num = <7>;
+ };
+
+ gpio@c700 {
+ reg = <0xc700 0x100>;
+ qcom,pin-num = <8>;
+ };
+
+ gpio@c800 {
+ reg = <0xc800 0x100>;
+ qcom,pin-num = <9>;
+ };
+
+ gpio@c900 {
+ reg = <0xc900 0x100>;
+ qcom,pin-num = <10>;
+ };
+
+ gpio@ca00 {
+ reg = <0xca00 0x100>;
+ qcom,pin-num = <11>;
+ };
+
+ gpio@cb00 {
+ reg = <0xcb00 0x100>;
+ qcom,pin-num = <12>;
+ };
+
+ gpio@cc00 {
+ reg = <0xcc00 0x100>;
+ qcom,pin-num = <13>;
+ };
+
+ gpio@cd00 {
+ reg = <0xcd00 0x100>;
+ qcom,pin-num = <14>;
+ };
+
+ gpio@ce00 {
+ reg = <0xce00 0x100>;
+ qcom,pin-num = <15>;
+ };
+
+ gpio@cf00 {
+ reg = <0xcf00 0x100>;
+ qcom,pin-num = <16>;
+ };
+
+ gpio@d000 {
+ reg = <0xd000 0x100>;
+ qcom,pin-num = <17>;
+ };
+
+ gpio@d100 {
+ reg = <0xd100 0x100>;
+ qcom,pin-num = <18>;
+ };
+
+ gpio@d200 {
+ reg = <0xd200 0x100>;
+ qcom,pin-num = <19>;
+ };
+
+ gpio@d300 {
+ reg = <0xd300 0x100>;
+ qcom,pin-num = <20>;
+ };
+
+ gpio@d400 {
+ reg = <0xd400 0x100>;
+ qcom,pin-num = <21>;
+ };
+
+ gpio@d500 {
+ reg = <0xd500 0x100>;
+ qcom,pin-num = <22>;
+ };
+
+ gpio@d600 {
+ reg = <0xd600 0x100>;
+ qcom,pin-num = <23>;
+ };
+
+ gpio@d700 {
+ reg = <0xd700 0x100>;
+ qcom,pin-num = <24>;
+ };
+
+ gpio@d800 {
+ reg = <0xd800 0x100>;
+ qcom,pin-num = <25>;
+ };
+
+ gpio@d900 {
+ reg = <0xd900 0x100>;
+ qcom,pin-num = <26>;
+ };
+
+ gpio@da00 {
+ reg = <0xda00 0x100>;
+ qcom,pin-num = <27>;
+ };
+
+ gpio@db00 {
+ reg = <0xdb00 0x100>;
+ qcom,pin-num = <28>;
+ };
+
+ gpio@dc00 {
+ reg = <0xdc00 0x100>;
+ qcom,pin-num = <29>;
+ };
+
+ gpio@dd00 {
+ reg = <0xdd00 0x100>;
+ qcom,pin-num = <30>;
+ };
+
+ gpio@de00 {
+ reg = <0xde00 0x100>;
+ qcom,pin-num = <31>;
+ };
+
+ gpio@df00 {
+ reg = <0xdf00 0x100>;
+ qcom,pin-num = <32>;
+ };
+
+ gpio@e000 {
+ reg = <0xe000 0x100>;
+ qcom,pin-num = <33>;
+ };
+
+ gpio@e100 {
+ reg = <0xe100 0x100>;
+ qcom,pin-num = <34>;
+ };
+
+ gpio@e200 {
+ reg = <0xe200 0x100>;
+ qcom,pin-num = <35>;
+ };
+
+ gpio@e300 {
+ reg = <0xe300 0x100>;
+ qcom,pin-num = <36>;
+ };
+
+ gpio@e400 {
+ reg = <0xe400 0x100>;
+ qcom,pin-num = <37>;
+ };
+
+ gpio@e500 {
+ reg = <0xe500 0x100>;
+ qcom,pin-num = <38>;
+ };
+
+ gpio@e600 {
+ reg = <0xe600 0x100>;
+ qcom,pin-num = <39>;
+ };
+
+ gpio@e700 {
+ reg = <0xe700 0x100>;
+ qcom,pin-num = <40>;
+ };
+
+ gpio@e800 {
+ reg = <0xe800 0x100>;
+ qcom,pin-num = <41>;
+ };
+
+ gpio@e900 {
+ reg = <0xe900 0x100>;
+ qcom,pin-num = <42>;
+ };
+
+ gpio@ea00 {
+ reg = <0xea00 0x100>;
+ qcom,pin-num = <43>;
+ };
+ };
+
+ pm8644_mpps: mpps {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-pin";
+ gpio-controller;
+ #gpio-cells = <2>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ label = "pm8644-mpp";
+
+ mpp@a000 {
+ reg = <0xa000 0x100>;
+ qcom,pin-num = <1>;
+ };
+
+ mpp@a100 {
+ reg = <0xa100 0x100>;
+ qcom,pin-num = <2>;
+ };
+
+ mpp@a200 {
+ reg = <0xa200 0x100>;
+ qcom,pin-num = <3>;
+ };
+
+ mpp@a300 {
+ reg = <0xa300 0x100>;
+ qcom,pin-num = <4>;
+ };
+
+ mpp@a400 {
+ reg = <0xa400 0x100>;
+ qcom,pin-num = <5>;
+ };
+
+ mpp@a500 {
+ reg = <0xa500 0x100>;
+ qcom,pin-num = <6>;
+ };
+ };
+ };
+
+ qcom,pm8644@1 {
+ spmi-slave-container;
+ reg = <0x1>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ regulator@1400 {
+ regulator-name = "8644_s1";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x1400 0x300>;
+ status = "disabled";
+
+ qcom,ctl@1400 {
+ reg = <0x1400 0x100>;
+ };
+ qcom,ps@1500 {
+ reg = <0x1500 0x100>;
+ };
+ qcom,freq@1600 {
+ reg = <0x1600 0x100>;
+ };
+ };
+
+ regulator@1700 {
+ regulator-name = "8644_s2";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x1700 0x300>;
+ status = "disabled";
+
+ qcom,ctl@1700 {
+ reg = <0x1700 0x100>;
+ };
+ qcom,ps@1800 {
+ reg = <0x1800 0x100>;
+ };
+ qcom,freq@1900 {
+ reg = <0x1900 0x100>;
+ };
+ };
+
+ regulator@1a00 {
+ regulator-name = "8644_s3";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x1a00 0x300>;
+ status = "disabled";
+
+ qcom,ctl@1a00 {
+ reg = <0x1a00 0x100>;
+ };
+ qcom,ps@1b00 {
+ reg = <0x1b00 0x100>;
+ };
+ qcom,freq@1c00 {
+ reg = <0x1c00 0x100>;
+ };
+ };
+
+ regulator@1d00 {
+ regulator-name = "8644_s4";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x1d00 0x300>;
+ status = "disabled";
+
+ qcom,ctl@1d00 {
+ reg = <0x1d00 0x100>;
+ };
+ qcom,ps@1e00 {
+ reg = <0x1e00 0x100>;
+ };
+ qcom,freq@1f00 {
+ reg = <0x1f00 0x100>;
+ };
+ };
+
+ regulator@2000 {
+ regulator-name = "8644_s5";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x2000 0x300>;
+ status = "disabled";
+
+ qcom,ctl@2000 {
+ reg = <0x2000 0x100>;
+ };
+ qcom,ps@2100 {
+ reg = <0x2100 0x100>;
+ };
+ qcom,freq@2200 {
+ reg = <0x2200 0x100>;
+ };
+ };
+
+ regulator@2300 {
+ regulator-name = "8644_s6";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x2300 0x300>;
+ status = "disabled";
+
+ qcom,ctl@2300 {
+ reg = <0x2300 0x100>;
+ };
+ qcom,ps@2400 {
+ reg = <0x2400 0x100>;
+ };
+ qcom,freq@2500 {
+ reg = <0x2500 0x100>;
+ };
+ };
+
+ regulator@2600 {
+ regulator-name = "8644_s7";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x2600 0x300>;
+ status = "disabled";
+
+ qcom,ctl@2600 {
+ reg = <0x2600 0x100>;
+ };
+ qcom,ps@2700 {
+ reg = <0x2700 0x100>;
+ };
+ qcom,freq@2800 {
+ reg = <0x2800 0x100>;
+ };
+ };
+
+ regulator@2900 {
+ regulator-name = "8644_s8";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x2900 0x300>;
+ status = "disabled";
+
+ qcom,ctl@2900 {
+ reg = <0x2900 0x100>;
+ };
+ qcom,ps@2a00 {
+ reg = <0x2a00 0x100>;
+ };
+ qcom,freq@2b00 {
+ reg = <0x2b00 0x100>;
+ };
+ };
+
+ regulator@2c00 {
+ regulator-name = "8644_s9";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x2c00 0x300>;
+ status = "disabled";
+
+ qcom,ctl@2c00 {
+ reg = <0x2c00 0x100>;
+ };
+ qcom,ps@2d00 {
+ reg = <0x2d00 0x100>;
+ };
+ qcom,freq@2e00 {
+ reg = <0x2e00 0x100>;
+ };
+ };
+
+ regulator@2f00 {
+ regulator-name = "8644_s10";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x2f00 0x300>;
+ status = "disabled";
+
+ qcom,ctl@2f00 {
+ reg = <0x2f00 0x100>;
+ };
+ qcom,ps@3000 {
+ reg = <0x3000 0x100>;
+ };
+ qcom,freq@3100 {
+ reg = <0x3100 0x100>;
+ };
+ };
+
+ regulator@3200 {
+ regulator-name = "8644_s11";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x3200 0x300>;
+ status = "disabled";
+
+ qcom,ctl@3200 {
+ reg = <0x3200 0x100>;
+ };
+ qcom,ps@3300 {
+ reg = <0x3300 0x100>;
+ };
+ qcom,freq@3400 {
+ reg = <0x3400 0x100>;
+ };
+ };
+
+ regulator@4000 {
+ regulator-name = "8644_l1";
+ reg = <0x4000 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4100 {
+ regulator-name = "8644_l2";
+ reg = <0x4100 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4200 {
+ regulator-name = "8644_l3";
+ reg = <0x4200 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4300 {
+ regulator-name = "8644_l4";
+ reg = <0x4300 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4400 {
+ regulator-name = "8644_l5";
+ reg = <0x4400 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ qcom,force-type = <0x04 0x10>;
+ status = "disabled";
+ };
+
+ regulator@4500 {
+ regulator-name = "8644_l6";
+ reg = <0x4500 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4600 {
+ regulator-name = "8644_l7";
+ reg = <0x4600 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ qcom,force-type = <0x04 0x10>;
+ status = "disabled";
+ };
+
+ regulator@4700 {
+ regulator-name = "8644_l8";
+ reg = <0x4700 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4800 {
+ regulator-name = "8644_l9";
+ reg = <0x4800 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4900 {
+ regulator-name = "8644_l10";
+ reg = <0x4900 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4a00 {
+ regulator-name = "8644_l11";
+ reg = <0x4a00 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4b00 {
+ regulator-name = "8644_l12";
+ reg = <0x4b00 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4c00 {
+ regulator-name = "8644_l13";
+ reg = <0x4c00 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4d00 {
+ regulator-name = "8644_l14";
+ reg = <0x4d00 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4e00 {
+ regulator-name = "8644_l15";
+ reg = <0x4e00 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4f00 {
+ regulator-name = "8644_l16";
+ reg = <0x4f00 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5000 {
+ regulator-name = "8644_l17";
+ reg = <0x5000 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5100 {
+ regulator-name = "8644_l18";
+ reg = <0x5100 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5200 {
+ regulator-name = "8644_l19";
+ reg = <0x5200 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5300 {
+ regulator-name = "8644_l20";
+ reg = <0x5300 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5400 {
+ regulator-name = "8644_l21";
+ reg = <0x5400 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5500 {
+ regulator-name = "8644_l22";
+ reg = <0x5500 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5600 {
+ regulator-name = "8644_l23";
+ reg = <0x5600 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5700 {
+ regulator-name = "8644_l24";
+ reg = <0x5700 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5800 {
+ regulator-name = "8644_l25";
+ reg = <0x5800 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@8000 {
+ regulator-name = "8644_lvs1";
+ reg = <0x8000 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@8100 {
+ regulator-name = "8644_lvs2";
+ reg = <0x8100 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@8200 {
+ regulator-name = "8644_mvs1";
+ reg = <0x8200 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@8300 {
+ regulator-name = "8644_mvs2";
+ reg = <0x8300 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/msm-pm8841.dtsi b/arch/arm/boot/dts/msm-pm8841.dtsi
index ea83231..1e0e5dfa 100644
--- a/arch/arm/boot/dts/msm-pm8841.dtsi
+++ b/arch/arm/boot/dts/msm-pm8841.dtsi
@@ -22,6 +22,15 @@
#address-cells = <1>;
#size-cells = <1>;
+ qcom,temp-alarm@2400 {
+ compatible = "qcom,qpnp-temp-alarm";
+ reg = <0x2400 0x100>;
+ interrupts = <0x4 0x24 0x0>;
+ label = "pm8841_tz";
+ qcom,threshold-set = <0>;
+ qcom,default-temp = <37000>;
+ };
+
pm8841_mpps: mpps {
spmi-dev-container;
compatible = "qcom,qpnp-pin";
diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index f1e18cf..1d95407 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.dtsi
@@ -22,6 +22,15 @@
#address-cells = <1>;
#size-cells = <1>;
+ qcom,temp-alarm@2400 {
+ compatible = "qcom,qpnp-temp-alarm";
+ reg = <0x2400 0x100>;
+ interrupts = <0x0 0x24 0x0>;
+ label = "pm8941_tz";
+ qcom,channel-num = <8>;
+ qcom,threshold-set = <0>;
+ };
+
qcom,power-on@800 {
compatible = "qcom,qpnp-power-on";
reg = <0x800 0x100>;
@@ -102,11 +111,12 @@
qcom,cxo-freq = <19200000>;
};
- pm8941-chg {
+ pm8941_chg: qcom,charger {
spmi-dev-container;
compatible = "qcom,qpnp-charger";
#address-cells = <1>;
#size-cells = <1>;
+ status = "disabled";
qcom,chg-vddmax-mv = <4200>;
qcom,chg-vddsafe-mv = <4200>;
@@ -115,6 +125,7 @@
qcom,chg-ibatterm-ma = <200>;
qcom,chg-chgr@1000 {
+ status = "disabled";
reg = <0x1000 0x100>;
interrupts = <0x0 0x10 0x0>,
<0x0 0x10 0x1>,
@@ -136,6 +147,7 @@
};
qcom,chg-buck@1100 {
+ status = "disabled";
reg = <0x1100 0x100>;
interrupts = <0x0 0x11 0x0>,
<0x0 0x11 0x1>,
@@ -155,6 +167,7 @@
};
qcom,chg-bat-if@1200 {
+ status = "disabled";
reg = <0x1200 0x100>;
interrupts = <0x0 0x12 0x0>,
<0x0 0x12 0x1>,
@@ -170,17 +183,19 @@
};
qcom,chg-usb-chgpth@1300 {
+ status = "disabled";
reg = <0x1300 0x100>;
interrupts = <0 0x13 0x0>,
<0 0x13 0x1>,
<0x0 0x13 0x2>;
- interrupt-names = "usbin-valid",
- "coarse-det-usb",
+ interrupt-names = "coarse-det-usb",
+ "usbin-valid",
"chg-gone";
};
qcom,chg-dc-chgpth@1400 {
+ status = "disabled";
reg = <0x1400 0x100>;
interrupts = <0x0 0x14 0x0>,
<0x0 0x14 0x1>;
@@ -190,6 +205,7 @@
};
qcom,chg-boost@1500 {
+ status = "disabled";
reg = <0x1500 0x100>;
interrupts = <0x0 0x15 0x0>,
<0x0 0x15 0x1>;
@@ -199,6 +215,7 @@
};
qcom,chg-misc@1600 {
+ status = "disabled";
reg = <0x1600 0x100>;
};
};
@@ -1036,5 +1053,68 @@
qcom,label = "wled";
};
+ pwm@b100 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb100 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <0>;
+ };
+
+ pwm@b200 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb200 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <1>;
+ };
+
+ pwm@b300 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb300 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <2>;
+ };
+
+ pwm@b400 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb400 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <3>;
+ };
+
+ pwm@b500 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb500 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <4>;
+ };
+
+ pwm@b600 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb600 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <5>;
+ };
+
+ pwm@b700 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb700 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <6>;
+ };
+
+ pwm@b800 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb800 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <7>;
+ };
};
};
diff --git a/arch/arm/boot/dts/msm8226-sim.dts b/arch/arm/boot/dts/msm8226-sim.dts
index 4330849..7c25680 100644
--- a/arch/arm/boot/dts/msm8226-sim.dts
+++ b/arch/arm/boot/dts/msm8226-sim.dts
@@ -11,7 +11,7 @@
*/
/dts-v1/;
-/include/ "skeleton.dtsi"
+/include/ "msm8226.dtsi"
/include/ "msm8226-ion.dtsi"
/include/ "msm8226-camera.dtsi"
@@ -19,43 +19,8 @@
model = "Qualcomm MSM 8226 Simulator";
compatible = "qcom,msm8226-sim", "qcom,msm8226";
qcom,msm-id = <145 1 0>;
- interrupt-parent = <&intc>;
-
- chosen {
- bootargs ="root=/dev/ram rw init=/init console=ttyHSL0,115200n8 initrd=0x00000000,0x00000000 mem=512M@0x00000000";
- };
-
- intc: interrupt-controller@f9000000 {
- compatible = "qcom,msm-qgic2";
- interrupt-controller;
- #interrupt-cells = <3>;
- reg = <0xF9000000 0x1000>,
- <0xF9002000 0x1000>;
- };
-
- msmgpio: gpio@fd510000 {
- compatible = "qcom,msm-gpio";
- interrupt-controller;
- #interrupt-cells = <2>;
- reg = <0xfd510000 0x4000>;
- #gpio-cells = <2>;
- };
-
- timer {
- compatible = "qcom,msm-qtimer", "arm,armv7-timer";
- interrupts = <1 2 0 1 3 0>;
- clock-frequency = <19200000>;
- };
serial@f991f000 {
- compatible = "qcom,msm-lsuart-v14";
- reg = <0xf991f000 0x1000>;
- interrupts = <0 109 0>;
- };
-
- serial@f995e000 {
- compatible = "qcom,msm-lsuart-v14";
- reg = <0xf995e000 0x1000>;
- interrupts = <0 114 0>;
+ status = "ok";
};
};
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
new file mode 100644
index 0000000..6d2ffec
--- /dev/null
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -0,0 +1,72 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/include/ "skeleton.dtsi"
+
+/ {
+ model = "Qualcomm MSM 8226";
+ compatible = "qcom,msm8226";
+ interrupt-parent = <&intc>;
+
+ intc: interrupt-controller@f9000000 {
+ compatible = "qcom,msm-qgic2";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ reg = <0xF9000000 0x1000>,
+ <0xF9002000 0x1000>;
+ };
+
+ msmgpio: gpio@fd510000 {
+ compatible = "qcom,msm-gpio";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ reg = <0xfd510000 0x4000>;
+ #gpio-cells = <2>;
+ };
+
+ timer {
+ compatible = "qcom,msm-qtimer", "arm,armv7-timer";
+ interrupts = <1 2 0 1 3 0>;
+ clock-frequency = <19200000>;
+ };
+
+ serial@f991f000 {
+ compatible = "qcom,msm-lsuart-v14";
+ reg = <0xf991f000 0x1000>;
+ interrupts = <0 109 0>;
+ status = "disabled";
+ };
+
+ serial@f995e000 {
+ compatible = "qcom,msm-lsuart-v14";
+ reg = <0xf995e000 0x1000>;
+ interrupts = <0 114 0>;
+ status = "disabled";
+ };
+
+ usb@f9a55000 {
+ compatible = "qcom,hsusb-otg";
+ reg = <0xf9a55000 0x400>;
+ interrupts = <0 134 0>;
+ interrupt-names = "core_irq";
+
+ qcom,hsusb-otg-phy-type = <2>;
+ qcom,hsusb-otg-mode = <1>;
+ qcom,hsusb-otg-otg-control = <1>;
+ qcom,hsusb-otg-disable-reset;
+ };
+
+ android_usb {
+ compatible = "qcom,android-usb";
+ };
+
+};
diff --git a/arch/arm/boot/dts/msm8910-sim.dts b/arch/arm/boot/dts/msm8910-sim.dts
new file mode 100644
index 0000000..aae88b1
--- /dev/null
+++ b/arch/arm/boot/dts/msm8910-sim.dts
@@ -0,0 +1,25 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+/include/ "msm8910.dtsi"
+
+/ {
+ model = "Qualcomm MSM 8910 Simulator";
+ compatible = "qcom,msm8910-sim", "qcom,msm8910";
+ qcom,msm-id = <147 1 0>;
+
+ serial@f991f000 {
+ status = "ok";
+ };
+};
diff --git a/arch/arm/boot/dts/msm8910.dtsi b/arch/arm/boot/dts/msm8910.dtsi
new file mode 100644
index 0000000..83dabfb
--- /dev/null
+++ b/arch/arm/boot/dts/msm8910.dtsi
@@ -0,0 +1,65 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/include/ "skeleton.dtsi"
+
+/ {
+ model = "Qualcomm MSM 8910";
+ compatible = "qcom,msm8910";
+ interrupt-parent = <&intc>;
+
+ intc: interrupt-controller@f9000000 {
+ compatible = "qcom,msm-qgic2";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ reg = <0xf9000000 0x1000>,
+ <0xf9002000 0x1000>;
+ };
+
+ msmgpio: gpio@fd510000 {
+ compatible = "qcom,msm-gpio";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ reg = <0xfd510000 0x4000>;
+ #gpio-cells = <2>;
+ };
+
+ timer {
+ compatible = "qcom,msm-qtimer", "arm,armv7-timer";
+ interrupts = <1 2 0 1 3 0>;
+ clock-frequency = <19200000>;
+ };
+
+ serial@f991f000 {
+ compatible = "qcom,msm-lsuart-v14";
+ reg = <0xf991f000 0x1000>;
+ interrupts = <0 109 0>;
+ status = "disabled";
+ };
+
+ usb@f9a55000 {
+ compatible = "qcom,hsusb-otg";
+ reg = <0xf9a55000 0x400>;
+ interrupts = <0 134 0>;
+ interrupt-names = "core_irq";
+
+ qcom,hsusb-otg-phy-type = <2>;
+ qcom,hsusb-otg-mode = <1>;
+ qcom,hsusb-otg-otg-control = <1>;
+ qcom,hsusb-otg-disable-reset;
+ };
+
+ android_usb {
+ compatible = "qcom,android-usb";
+ };
+
+};
diff --git a/arch/arm/boot/dts/msm8974-cdp.dts b/arch/arm/boot/dts/msm8974-cdp.dts
index aff0adc..0e0f6cf 100644
--- a/arch/arm/boot/dts/msm8974-cdp.dts
+++ b/arch/arm/boot/dts/msm8974-cdp.dts
@@ -30,6 +30,10 @@
};
};
+ qcom,hdmi_tx@fd922100 {
+ status = "ok";
+ };
+
i2c@f9924000 {
atmel_mxt_ts@4a {
compatible = "atmel,mxt-ts";
@@ -112,6 +116,7 @@
gpio_keys {
compatible = "gpio-keys";
+ input-name = "gpio-keys";
camera_snapshot {
label = "camera_snapshot";
diff --git a/arch/arm/boot/dts/msm8974-fluid.dts b/arch/arm/boot/dts/msm8974-fluid.dts
new file mode 100644
index 0000000..891379f
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974-fluid.dts
@@ -0,0 +1,371 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+/include/ "msm8974.dtsi"
+/include/ "dsi-panel-toshiba-720p-video.dtsi"
+
+/ {
+ model = "Qualcomm MSM 8974 FLUID";
+ compatible = "qcom,msm8974-fluid", "qcom,msm8974";
+ qcom,msm-id = <126 3 0>;
+
+ serial@f991e000 {
+ status = "ok";
+ };
+
+ qcom,mdss_dsi@fd922800 {
+ qcom,mdss_dsi_toshiba_720p_video {
+ status = "ok";
+ };
+ };
+
+ qcom,hdmi_tx@fd922100 {
+ status = "ok";
+ };
+
+ i2c@f9924000 {
+ atmel_mxt_ts@4a {
+ compatible = "atmel,mxt-ts";
+ reg = <0x4a>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <61 0x2>;
+ vdd_ana-supply = <&pm8941_l18>;
+ vcc_i2c-supply = <&pm8941_lvs1>;
+ atmel,reset-gpio = <&msmgpio 60 0x00>;
+ atmel,irq-gpio = <&msmgpio 61 0x00>;
+ atmel,panel-coords = <0 0 760 1424>;
+ atmel,display-coords = <0 0 720 1280>;
+ atmel,i2c-pull-up = <1>;
+ atmel,cfg_1 {
+ atmel,family-id = <0x82>;
+ atmel,variant-id = <0x19>;
+ atmel,version = <0x10>;
+ atmel,build = <0xaa>;
+ atmel,config = [
+ /* Object 6, Instance = 0 */
+ 00 00 00 00 00 00
+ /* Object 38, Instance = 0 */
+ 15 00 02 10 08 0C 00 00
+ /* Object 7, Instance = 0 */
+ FF FF 32 03
+ /* Object 8, Instance = 0 */
+ 0F 00 0A 0A 00 00 0A 00 00 00
+ /* Object 9, Instance = 0 */
+ 83 00 00 18 0E 00 70 32 02 01
+ 00 03 01 01 05 0A 0A 0A 90 05
+ F8 02 00 00 0F 0F 00 00 48 2D
+ 07 0C 00 00 00 00
+ /* Object 15, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ 00
+ /* Object 18, Instance = 0 */
+ 00 00
+ /* Object 19, Instance = 0 */
+ 00 00 00 00 00 00
+ /* Object 23, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00
+ /* Object 25, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00
+ /* Object 40, Instance = 0 */
+ 00 00 00 00 00
+ /* Object 42, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ /* Object 46, Instance = 0 */
+ 00 00 10 10 00 00 03 00 00 01
+ /* Object 47, Instance = 0 */
+ 08 0A 28 0A 02 0A 00 8C 00 20
+ 00 00 00
+ /* Object 55, Instance = 0 */
+ 00 00 00 00 00 00
+ /* Object 56, Instance = 0 */
+ 03 00 01 18 05 05 05 05 05 05
+ 05 05 05 05 05 05 05 05 05 05
+ 05 05 05 05 05 05 05 05 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00
+ /* Object 57, Instance = 0 */
+ 00 00 00
+ /* Object 61, Instance = 0 */
+ 00 00 00 00 00
+ /* Object 61, Instance = 1 */
+ 00 00 00 00 00
+ /* Object 62, Instance = 0 */
+ 7F 03 00 16 00 00 00 00 00 00
+ 04 08 10 18 05 00 0A 05 05 50
+ 14 19 34 1A 64 00 00 04 40 00
+ 00 00 00 00 30 32 02 00 01 00
+ 05 00 00 00 00 00 00 00 00 00
+ 00 00 0C 00
+ ];
+ };
+ };
+ };
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ input-name = "gpio-keys";
+
+ camera_snapshot {
+ label = "camera_snapshot";
+ gpios = <&pm8941_gpios 3 0x1>;
+ linux,input-type = <1>;
+ linux,code = <0x2fe>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+
+ camera_focus {
+ label = "camera_focus";
+ gpios = <&pm8941_gpios 4 0x1>;
+ linux,input-type = <1>;
+ linux,code = <0x210>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+
+ vol_up {
+ label = "volume_up";
+ gpios = <&pm8941_gpios 5 0x1>;
+ linux,input-type = <1>;
+ linux,code = <115>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+ };
+
+ spi@f9923000 {
+ ethernet-switch@2 {
+ compatible = "micrel,ks8851";
+ reg = <2>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <94 0>;
+ spi-max-frequency = <4800000>;
+ rst-gpio = <&pm8941_mpps 6 0>;
+ vdd-io-supply = <&spi_eth_vreg>;
+ vdd-phy-supply = <&spi_eth_vreg>;
+ };
+ };
+};
+
+&sdcc1 {
+ qcom,sdcc-bus-width = <4>;
+};
+
+&sdcc2 {
+ #address-cells = <0>;
+ interrupt-parent = <&sdcc2>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 125 0
+ 1 &intc 0 220 0
+ 2 &msmgpio 62 0x3>;
+ interrupt-names = "core_irq", "bam_irq", "status_irq";
+ cd-gpios = <&msmgpio 62 0x1>;
+};
+
+&pm8941_gpios {
+ gpio@c000 { /* GPIO 1 */
+ };
+
+ gpio@c100 { /* GPIO 2 */
+ };
+
+ gpio@c200 { /* GPIO 3 */
+ qcom,mode = <0>;
+ qcom,pull = <0>;
+ qcom,vin-sel = <2>;
+ qcom,select = <0>;
+ };
+
+ gpio@c300 { /* GPIO 4 */
+ qcom,mode = <0>;
+ qcom,pull = <0>;
+ qcom,vin-sel = <2>;
+ qcom,select = <0>;
+ };
+
+ gpio@c400 { /* GPIO 5 */
+ qcom,mode = <0>;
+ qcom,pull = <0>;
+ qcom,vin-sel = <2>;
+ qcom,select = <0>;
+ };
+
+ gpio@c500 { /* GPIO 6 */
+ };
+
+ gpio@c600 { /* GPIO 7 */
+ };
+
+ gpio@c700 { /* GPIO 8 */
+ };
+
+ gpio@c800 { /* GPIO 9 */
+ };
+
+ gpio@c900 { /* GPIO 10 */
+ };
+
+ gpio@ca00 { /* GPIO 11 */
+ };
+
+ gpio@cb00 { /* GPIO 12 */
+ };
+
+ gpio@cc00 { /* GPIO 13 */
+ };
+
+ gpio@cd00 { /* GPIO 14 */
+ };
+
+ gpio@ce00 { /* GPIO 15 */
+ qcom,mode = <1>;
+ qcom,output-type = <0>;
+ qcom,pull = <5>;
+ qcom,vin-sel = <2>;
+ qcom,out-strength = <3>;
+ qcom,src-select = <2>;
+ qcom,master-en = <1>;
+ };
+
+ gpio@cf00 { /* GPIO 16 */
+ };
+
+ gpio@d000 { /* GPIO 17 */
+ };
+
+ gpio@d100 { /* GPIO 18 */
+ };
+
+ gpio@d200 { /* GPIO 19 */
+ qcom,mode = <1>; /* QPNP_PIN_MODE_DIG_OUT */
+ qcom,output-type = <0>; /* QPNP_PIN_OUT_BUF_CMOS */
+ qcom,pull = <5>; /* QPNP_PIN_PULL_NO */
+ qcom,vin-sel = <2>; /* QPNP_PIN_VIN2 */
+ qcom,out-strength = <2>; /* QPNP_PIN_OUT_STRENGTH_MED */
+ qcom,src-select = <0>; /* QPNP_PIN_SEL_FUNC_CONSTANT */
+ qcom,master-en = <1>;
+ };
+
+ gpio@d300 { /* GPIO 20 */
+ };
+
+ gpio@d400 { /* GPIO 21 */
+ };
+
+ gpio@d500 { /* GPIO 22 */
+ };
+
+ gpio@d600 { /* GPIO 23 */
+ };
+
+ gpio@d700 { /* GPIO 24 */
+ };
+
+ gpio@d800 { /* GPIO 25 */
+ };
+
+ gpio@d900 { /* GPIO 26 */
+ };
+
+ gpio@da00 { /* GPIO 27 */
+ };
+
+ gpio@db00 { /* GPIO 28 */
+ };
+
+ gpio@dc00 { /* GPIO 29 */
+ qcom,pull = <0>; /* set to default pull */
+ qcom,master-en = <1>;
+ qcom,vin-sel = <2>; /* select 1.8 V source */
+ };
+
+ gpio@dd00 { /* GPIO 30 */
+ };
+
+ gpio@de00 { /* GPIO 31 */
+ };
+
+ gpio@df00 { /* GPIO 32 */
+ };
+
+ gpio@e000 { /* GPIO 33 */
+ };
+
+ gpio@e100 { /* GPIO 34 */
+ };
+
+ gpio@e200 { /* GPIO 35 */
+ };
+
+ gpio@e300 { /* GPIO 36 */
+ };
+};
+
+&pm8941_mpps {
+
+ mpp@a000 { /* MPP 1 */
+ };
+
+ mpp@a100 { /* MPP 2 */
+ };
+
+ mpp@a200 { /* MPP 3 */
+ };
+
+ mpp@a300 { /* MPP 4 */
+ };
+
+ mpp@a400 { /* MPP 5 */
+ /* SPI_ETH config */
+ qcom,mode = <1>; /* DIG_OUT */
+ qcom,output-type = <0>; /* CMOS */
+ qcom,vin-sel = <2>; /* PM8941_S3 1.8V > 1.6V */
+ qcom,src-select = <0>; /* CONSTANT */
+ qcom,master-en = <1>; /* ENABLE MPP */
+ };
+
+ mpp@a500 { /* MPP 6 */
+ /* SPI_ETH_RST config */
+ qcom,mode = <1>; /* DIG_OUT */
+ qcom,output-type = <0>; /* CMOS */
+ qcom,vin-sel = <2>; /* PM8941_S3 1.8V > 1.6V */
+ qcom,src-select = <0>; /* CONSTANT */
+ qcom,master-en = <1>; /* ENABLE MPP */
+ };
+
+ mpp@a600 { /* MPP 7 */
+ };
+
+ mpp@a700 { /* MPP 8 */
+ };
+};
+
+&pm8841_mpps {
+
+ mpp@a000 { /* MPP 1 */
+ };
+
+ mpp@a100 { /* MPP 2 */
+ };
+
+ mpp@a200 { /* MPP 3 */
+ };
+
+ mpp@a300 { /* MPP 4 */
+ };
+};
diff --git a/arch/arm/boot/dts/msm8974-gpu.dtsi b/arch/arm/boot/dts/msm8974-gpu.dtsi
index 2312b02..4fe0eda 100644
--- a/arch/arm/boot/dts/msm8974-gpu.dtsi
+++ b/arch/arm/boot/dts/msm8974-gpu.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -25,7 +25,8 @@
qcom,idle-timeout = <83>; //<HZ/12>
qcom,nap-allowed = <1>;
- qcom,clk-map = <0x00000016>; //KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM_IFACE
+ qcom,strtstp-sleepwake;
+ qcom,clk-map = <0x0000006>; //KGSL_CLK_CORE | KGSL_CLK_IFACE
/* Bus Scale Settings */
qcom,grp3d-vectors = <0 0 0 0>, <2 1 0 0>,
@@ -85,40 +86,71 @@
compatible = "qcom,dcvs-core-info";
- qcom,core-max-time-us = <100000>;
- qcom,algo-slack-time-us = <39000>;
- qcom,algo-disable-pc-threshold = <86000>;
- qcom,algo-ss-window-size = <1000000>;
- qcom,algo-ss-util-pct = <95>;
+ qcom,num-cores = <1>;
+ qcom,sensors = <0>;
+
+ qcom,core-core-type = <1>;
+
+ qcom,algo-disable-pc-threshold = <0>;
+ qcom,algo-em-win-size-min-us = <100000>;
+ qcom,algo-em-win-size-max-us = <300000>;
qcom,algo-em-max-util-pct = <97>;
+ qcom,algo-group-id = <95>;
+ qcom,algo-max-freq-chg-time-us = <100000>;
+ qcom,algo-slack-mode-dynamic = <100000>;
+ qcom,algo-slack-weight-thresh-pct = <0>;
+ qcom,algo-slack-time-min-us = <39000>;
+ qcom,algo-slack-time-max-us = <39000>;
+ qcom,algo-ss-win-size-min-us = <1000000>;
+ qcom,algo-ss-win-size-max-us = <1000000>;
+ qcom,algo-ss-util-pct = <95>;
qcom,algo-ss-iobusy-conv = <100>;
+ qcom,energy-active-coeff-a = <2492>;
+ qcom,energy-active-coeff-b = <0>;
+ qcom,energy-active-coeff-c = <0>;
+ qcom,energy-leakage-coeff-a = <11>;
+ qcom,energy-leakage-coeff-b = <157150>;
+ qcom,energy-leakage-coeff-c = <0>;
+ qcom,energy-leakage-coeff-d = <0>;
+
+ qcom,power-current-temp = <25>;
+ qcom,power-num-freq = <4>;
+
qcom,dcvs-freq@0 {
reg = <0>;
qcom,freq = <0>;
- qcom,idle-energy = <0>;
- qcom,active-energy = <333932>;
+ qcom,voltage = <0>;
+ qcom,is_trans_level = <0>;
+ qcom,active-energy-offset = <100>;
+ qcom,leakage-energy-offset = <0>;
};
qcom,dcvs-freq@1 {
reg = <1>;
qcom,freq = <0>;
- qcom,idle-energy = <0>;
- qcom,active-energy = <497532>;
+ qcom,voltage = <0>;
+ qcom,is_trans_level = <0>;
+ qcom,active-energy-offset = <100>;
+ qcom,leakage-energy-offset = <0>;
};
qcom,dcvs-freq@2 {
reg = <2>;
qcom,freq = <0>;
- qcom,idle-energy = <0>;
- qcom,active-energy = <707610>;
+ qcom,voltage = <0>;
+ qcom,is_trans_level = <0>;
+ qcom,active-energy-offset = <100>;
+ qcom,leakage-energy-offset = <0>;
};
qcom,dcvs-freq@3 {
reg = <3>;
qcom,freq = <0>;
- qcom,idle-energy = <0>;
- qcom,active-energy = <844545>;
+ qcom,voltage = <0>;
+ qcom,is_trans_level = <0>;
+ qcom,active-energy-offset = <844545>;
+ qcom,leakage-energy-offset = <0>;
};
};
diff --git a/arch/arm/boot/dts/msm8974-ion.dtsi b/arch/arm/boot/dts/msm8974-ion.dtsi
index 1893ae4..9b5aaac 100644
--- a/arch/arm/boot/dts/msm8974-ion.dtsi
+++ b/arch/arm/boot/dts/msm8974-ion.dtsi
@@ -70,7 +70,7 @@
reg = <28>;
qcom,heap-align = <0x1000>;
qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
- qcom,memory-reservation-size = <0x2B4000>;
+ qcom,memory-reservation-size = <0x314000>;
};
};
};
diff --git a/arch/arm/boot/dts/msm8974-liquid.dts b/arch/arm/boot/dts/msm8974-liquid.dts
index 2abc1d5..6be571a 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dts
+++ b/arch/arm/boot/dts/msm8974-liquid.dts
@@ -22,27 +22,162 @@
serial@f991e000 {
status = "ok";
};
+
+ qcom,mdss_edp@fd923400 {
+ status = "ok";
+ };
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ input-name = "gpio-keys";
+
+ home {
+ label = "home";
+ gpios = <&pm8941_gpios 1 0x1>;
+ linux,input-type = <1>;
+ linux,code = <102>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+
+ vol_down {
+ label = "volume_down";
+ gpios = <&pm8941_gpios 2 0x1>;
+ linux,input-type = <1>;
+ linux,code = <114>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+
+ vol_up {
+ label = "volume_up";
+ gpios = <&pm8941_gpios 5 0x1>;
+ linux,input-type = <1>;
+ linux,code = <115>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+ };
+
+ qcom,hdmi_tx@fd922100 {
+ status = "ok";
+ };
+
+ i2c@f9924000 {
+ atmel_mxt_ts@4a {
+ compatible = "atmel,mxt-ts";
+ reg = <0x4a>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <61 0x2>;
+ vdd_ana-supply = <&pm8941_l22>;
+ vcc_i2c-supply = <&pm8941_s3>;
+ atmel,reset-gpio = <&msmgpio 60 0x00>;
+ atmel,irq-gpio = <&msmgpio 61 0x00>;
+ atmel,panel-coords = <0 0 1080 1920>;
+ atmel,display-coords = <0 0 1080 1920>;
+ atmel,i2c-pull-up = <1>;
+ atmel,cfg_1 {
+ atmel,family-id = <0xa2>;
+ atmel,variant-id = <0x00>;
+ atmel,version = <0x11>;
+ atmel,build = <0xaa>;
+ atmel,config = [
+ /* Object 6, Instance = 0 */
+ 00 00 00 00 00 00
+ /* Object 38, Instance = 0 */
+ 16 00 00 14 09 0C 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00
+ /* Object 7, Instance = 0 */
+ FF FF 0A 03
+ /* Object 8, Instance = 0 */
+ 5F 00 14 14 00 00 00 01 00 00
+ /* Object 9, Instance = 0 */
+ 8F 00 00 20 34 00 87 3C 08 03
+ 00 05 03 80 0A 14 14 0A 80 07
+ 38 04 00 00 00 00 00 00 00 00
+ 0F 0F 2E 33 02 00
+ /* Object 15, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ 00
+ /* Object 18, Instance = 0 */
+ 04 00
+ /* Object 24, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00
+ /* Object 25, Instance = 0 */
+ 00 00 54 6F F0 55 00 00 00 00
+ 00 00 00 00 00
+ /* Object 27, Instance = 0 */
+ 00 00 00 00 00 00 00
+ /* Object 40, Instance = 0 */
+ 00 14 14 14 14
+ /* Object 42, Instance = 0 */
+ 20 14 00 00 00 14 11 00 03 00
+ /* Object 43, Instance = 0 */
+ 09 00 01 01 91 00 80 00 00 00
+ 00 00
+ /* Object 46, Instance = 0 */
+ 00 00 10 10 00 00 01 00 00 0F
+ 0A
+ /* Object 47, Instance = 0 */
+ 00 14 23 02 05 1E 01 78 03 10
+ 00 00 0C 00 00 00 00 00 00 00
+ 00 00
+ /* Object 55, Instance = 0 */
+ 00 00 00 00 00 00 00
+ /* Object 56, Instance = 0 */
+ 02 00 01 30 13 14 14 14 15 15
+ 15 15 15 15 15 16 16 16 16 16
+ 16 16 16 16 16 15 14 14 14 14
+ 15 14 14 14 14 13 00 00 01 02
+ 05 05 00 00 00 00 00 00 00 00
+ 00
+ /* Object 57, Instance = 0 */
+ 00 00 00
+ /* Object 61, Instance = 0 */
+ 00 00 00 00 00
+ /* Object 62, Instance = 0 */
+ 00 01 03 01 00 00 00 00 00 0A
+ 0F 14 19 23 05 00 0A 05 05 69
+ 23 23 34 11 64 06 06 04 40 00
+ 00 00 00 00 69 4B 02 00 00 80
+ 0A 14 14 18 18 10 10 80 00 80
+ 00 00 0F 02 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00
+ /* Object 63, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00
+ ];
+ };
+ };
+ };
};
&pm8941_gpios {
gpio@c000 { /* GPIO 1 */
+ qcom,mode = <0>;
+ qcom,pull = <0>;
+ qcom,vin-sel = <2>;
+ qcom,select = <0>;
};
gpio@c100 { /* GPIO 2 */
+ qcom,mode = <0>;
+ qcom,pull = <0>;
+ qcom,vin-sel = <2>;
+ qcom,select = <0>;
};
gpio@c200 { /* GPIO 3 */
- qcom,mode = <0>;
- qcom,pull = <0>;
- qcom,vin-sel = <2>;
- qcom,select = <0>;
};
gpio@c300 { /* GPIO 4 */
- qcom,mode = <0>;
- qcom,pull = <0>;
- qcom,vin-sel = <2>;
- qcom,select = <0>;
};
gpio@c400 { /* GPIO 5 */
@@ -210,9 +345,21 @@
mpp@a100 { /* MPP 2 */
};
- mpp@a200 { /* MPP 3 */
+ mpp@a200 { /* HDMI_MUX_SEL MPP 3*/
+ status = "ok";
+ qcom,mode = <1>; /* DIG_OUT */
+ qcom,output-type = <0>; /* CMOS */
+ qcom,vin-sel = <2>; /* PM8841_S3A 1.8V */
+ qcom,src-select = <0>; /* CONSTANT */
+ qcom,master-en = <1>; /* ENABLE MPP */
};
- mpp@a300 { /* MPP 4 */
+ mpp@a300 { /* HDMI_MUX_EN MPP 4*/
+ status = "ok";
+ qcom,mode = <1>; /* DIG_OUT */
+ qcom,output-type = <0>; /* CMOS */
+ qcom,vin-sel = <0>; /* PM8841_VPH 3.4V */
+ qcom,src-select = <0>; /* CONSTANT */
+ qcom,master-en = <1>; /* ENABLE MPP */
};
};
diff --git a/arch/arm/boot/dts/msm8974-mdss.dtsi b/arch/arm/boot/dts/msm8974-mdss.dtsi
index ee5836c..e791348 100644
--- a/arch/arm/boot/dts/msm8974-mdss.dtsi
+++ b/arch/arm/boot/dts/msm8974-mdss.dtsi
@@ -18,6 +18,8 @@
reg-names = "mdp_phys", "vbif_phys";
interrupts = <0 72 0>;
vdd-supply = <&gdsc_mdss>;
+ qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+ qcom,memory-reservation-size = <0x800000>; /* size 8MB */
};
mdss_dsi: qcom,mdss_dsi@fd922800 {
@@ -58,4 +60,14 @@
qcom,mdss_pan_res = <1920 1080>;
qcom,mdss_pan_bpp = <24>;
};
+
+ mdss_edp: qcom,mdss_edp@fd923400 {
+ compatible = "qcom,mdss-edp";
+ reg = <0xfd923400 0x700>,
+ <0xfd8c2000 0x1000>;
+ reg-names = "edp_base", "mmss_cc_base";
+ vdda-supply = <&pm8941_l12>;
+ gpio-panel-en = <&msmgpio 58 0>;
+ status = "disable";
+ };
};
diff --git a/arch/arm/boot/dts/msm8974-mtp.dts b/arch/arm/boot/dts/msm8974-mtp.dts
index e183d04..f75ebbe 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dts
+++ b/arch/arm/boot/dts/msm8974-mtp.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -18,7 +18,7 @@
/ {
model = "Qualcomm MSM 8974 MTP";
compatible = "qcom,msm8974-mtp", "qcom,msm8974";
- qcom,msm-id = <126 8 0>, <126 3 0>;
+ qcom,msm-id = <126 8 0>;
serial@f991e000 {
status = "ok";
@@ -30,6 +30,10 @@
};
};
+ qcom,hdmi_tx@fd922100 {
+ status = "disabled";
+ };
+
i2c@f9924000 {
atmel_mxt_ts@4a {
compatible = "atmel,mxt-ts";
@@ -112,6 +116,7 @@
gpio_keys {
compatible = "gpio-keys";
+ input-name = "gpio-keys";
camera_snapshot {
label = "camera_snapshot";
@@ -168,6 +173,42 @@
cd-gpios = <&msmgpio 62 0x1>;
};
+&usb_otg {
+ qcom,hsusb-otg-otg-control = <2>;
+};
+
+&pm8941_chg {
+ status = "ok";
+
+ qcom,chg-chgr@1000 {
+ status = "ok";
+ };
+
+ qcom,chg-buck@1100 {
+ status = "ok";
+ };
+
+ qcom,chg-bat-if@1200 {
+ status = "ok";
+ };
+
+ qcom,chg-usb-chgpth@1300 {
+ status = "ok";
+ };
+
+ qcom,chg-dc-chgpth@1400 {
+ status = "ok";
+ };
+
+ qcom,chg-boost@1500 {
+ status = "ok";
+ };
+
+ qcom,chg-misc@1600 {
+ status = "ok";
+ };
+};
+
&pm8941_gpios {
gpio@c000 { /* GPIO 1 */
};
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index a00d37e..8d54585 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -42,6 +42,15 @@
reg = <0xfd510000 0x4000>;
};
+ wcd9xxx_intc: wcd9xxx-irq {
+ compatible = "qcom,wcd9xxx-irq";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <72 0>;
+ interrupt-names = "cdc-int";
+ };
+
timer {
compatible = "qcom,msm-qtimer", "arm,armv7-timer";
interrupts = <1 2 0 1 3 0>;
@@ -52,7 +61,7 @@
compatible = "qcom,msm-vidc";
reg = <0xfdc00000 0xff000>;
interrupts = <0 44 0>;
- vidc-cp-map = <0x1000000 0x40000000>;
+ vidc-cp-map = <0x1000000 0x3f000000>;
vidc-ns-map = <0x40000000 0x40000000>;
load-freq-tbl = <979200 410000000>,
<783360 410000000>,
@@ -85,7 +94,7 @@
status = "disabled";
};
- usb@f9a55000 {
+ usb_otg: usb@f9a55000 {
compatible = "qcom,hsusb-otg";
reg = <0xf9a55000 0x400>;
interrupts = <0 134 0 0 140 0>;
@@ -99,6 +108,7 @@
qcom,hsusb-otg-mode = <1>;
qcom,hsusb-otg-otg-control = <1>;
qcom,hsusb-otg-disable-reset;
+ qcom,hsusb-otg-pnoc-errata-fix;
qcom,msm_bus,name = "usb2";
qcom,msm_bus,num_cases = <2>;
@@ -239,6 +249,19 @@
qcom,bam-dma-res-pipes = <6>;
};
+ spi@f9966000 {
+ compatible = "qcom,spi-qup-v2";
+ cell-index = <7>;
+ reg = <0xf9966000 0x1000>;
+ interrupts = <0 104 0>;
+ spi-max-frequency = <19200000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ gpios = <&msmgpio 56 0>, /* CLK */
+ <&msmgpio 54 0>, /* MISO */
+ <&msmgpio 53 0>; /* MOSI */
+ cs-gpios = <&msmgpio 55 0>;
+ };
slim@fe12f000 {
cell-index = <1>;
@@ -255,6 +278,9 @@
compatible = "qcom,taiko-slim-pgd";
elemental-addr = [00 01 A0 00 17 02];
+ interrupt-parent = <&wcd9xxx_intc>;
+ interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28>;
+
qcom,cdc-reset-gpio = <&msmgpio 63 0>;
cdc-vdd-buck-supply = <&pm8941_s2>;
@@ -633,6 +659,7 @@
reg = <0xfe200000 0x00100>,
<0xfd485100 0x00010>;
reg-names = "qdsp6_base", "halt_base";
+ interrupts = <0 162 1>;
qcom,firmware-name = "adsp";
};
@@ -677,6 +704,11 @@
compatible = "qcom,msm-pcm-afe";
};
+ qcom,msm-dai-q6-hdmi {
+ compatible = "qcom,msm-dai-q6-hdmi";
+ qcom,msm-dai-q6-dev-id = <8>;
+ };
+
qcom,msm-dai-q6 {
compatible = "qcom,msm-dai-q6";
qcom,msm-dai-q6-sb-0-rx {
@@ -699,6 +731,16 @@
qcom,msm-dai-q6-dev-id = <12289>;
};
+ qcom,msm-dai-q6-int-fm-rx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <12292>;
+ };
+
+ qcom,msm-dai-q6-int-fm-tx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <12293>;
+ };
+
qcom,msm-dai-q6-be-afe-pcm-rx {
compatible = "qcom,msm-dai-q6-dev";
qcom,msm-dai-q6-dev-id = <224>;
@@ -748,10 +790,13 @@
qcom,msm-ocmem-audio {
compatible = "qcom,msm-ocmem-audio";
- qcom,msm-ocmem-audio-src-id = <11>;
- qcom,msm-ocmem-audio-dst-id = <604>;
- qcom,msm-ocmem-audio-ab = <32505856>;
- qcom,msm-ocmem-audio-ib = <32505856>;
+ qcom,msm_bus,name = "audio-ocmem";
+ qcom,msm_bus,num_cases = <2>;
+ qcom,msm_bus,active_only = <0>;
+ qcom,msm_bus,num_paths = <1>;
+ qcom,msm_bus,vectors =
+ <11 604 0 0>,
+ <11 604 32505856 32505856>;
};
qcom,mss@fc880000 {
@@ -775,6 +820,7 @@
reg = <0xfc820000 0x0020>,
<0x0d1fc000 0x4000>;
reg-names = "rmb_base", "metadata_base";
+ interrupts = <0 24 1>;
qcom,firmware-name = "modem";
qcom,depends-on = "mba";
@@ -786,6 +832,7 @@
<0xfc401700 0x4>,
<0xfd485300 0xc>;
reg-names = "pmu_base", "clk_base", "halt_base";
+ interrupts = <0 149 1>;
vdd_pronto_pll-supply = <&pm8941_l12>;
qcom,firmware-name = "wcnss";
@@ -870,6 +917,15 @@
qcom,qseecom@fe806000 {
compatible = "qcom,qseecom";
+ qcom,msm_bus,name = "qseecom-noc";
+ qcom,msm_bus,num_cases = <4>;
+ qcom,msm_bus,active_only = <0>;
+ qcom,msm_bus,num_paths = <1>;
+ qcom,msm_bus,vectors =
+ <55 512 0 0>,
+ <55 512 3936000000 393600000>,
+ <55 512 3936000000 393600000>,
+ <55 512 3936000000 393600000>;
};
qcom,wdt@f9017000 {
@@ -881,9 +937,9 @@
qcom,ipi-ping = <1>;
};
- qcom,tz-log@fe805720 {
+ qcom,tz-log@fc03000 {
compatible = "qcom,tz-log";
- reg = <0xfe805720 0x1000>;
+ reg = <0x0fc03000 0x1000>;
};
qcom,venus@fdce0000 {
@@ -915,9 +971,10 @@
tsens@fc4a8000 {
compatible = "qcom,msm-tsens";
reg = <0xfc4a8000 0x2000>,
- <0xfc4b80d0 0x5>;
+ <0xfc4b8000 0x1000>;
reg-names = "tsens_physical", "tsens_eeprom_physical";
interrupts = <0 184 0>;
+ qcom,calibration-less-mode;
qcom,sensors = <11>;
qcom,slope = <3200 3200 3200 3200 3200 3200 3200 3200 3200
3200 3200>;
diff --git a/arch/arm/boot/dts/msm8974_pm.dtsi b/arch/arm/boot/dts/msm8974_pm.dtsi
index 77f2532..c6cbca3 100644
--- a/arch/arm/boot/dts/msm8974_pm.dtsi
+++ b/arch/arm/boot/dts/msm8974_pm.dtsi
@@ -28,6 +28,8 @@
qcom,saw2-spm-dly= <0x20000400>;
qcom,saw2-spm-ctl = <0x1>;
qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+ qcom,saw2-spm-cmd-ret = [42 1b 00 d0 c0 a0 90 03 d0 98 a2 c0
+ 0b 00 42 1b 0f];
qcom,saw2-spm-cmd-spc = [00 20 80 10 90 a0 b0 03 3b 98 a2 b0 82
10 0b 30 06 26 30 0f];
qcom,saw2-spm-cmd-pc = [00 20 80 10 90 a0 b0 07 3b 98 a2 b0 82
@@ -49,6 +51,8 @@
qcom,saw2-spm-dly= <0x20000400>;
qcom,saw2-spm-ctl = <0x1>;
qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+ qcom,saw2-spm-cmd-ret = [42 1b 00 d0 c0 a0 90 03 d0 98 a2 c0
+ 0b 00 42 1b 0f];
qcom,saw2-spm-cmd-spc = [00 20 80 10 90 a0 b0 03 3b 98 a2 b0 82
10 0b 30 06 26 30 0f];
qcom,saw2-spm-cmd-pc = [00 20 80 10 90 a0 b0 07 3b 98 a2 b0 82
@@ -70,6 +74,8 @@
qcom,saw2-spm-dly= <0x20000400>;
qcom,saw2-spm-ctl = <0x1>;
qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+ qcom,saw2-spm-cmd-ret = [42 1b 00 d0 c0 a0 90 03 d0 98 a2 c0
+ 0b 00 42 1b 0f];
qcom,saw2-spm-cmd-spc = [00 20 80 10 90 a0 b0 03 3b 98 a2 b0 82
10 0b 30 06 26 30 0f];
qcom,saw2-spm-cmd-pc = [00 20 80 10 90 a0 b0 07 3b 98 a2 b0 82
@@ -91,6 +97,8 @@
qcom,saw2-spm-dly= <0x20000400>;
qcom,saw2-spm-ctl = <0x1>;
qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+ qcom,saw2-spm-cmd-ret = [42 1b 00 d0 c0 a0 90 03 d0 98 a2 c0
+ 0b 00 42 1b 0f];
qcom,saw2-spm-cmd-spc = [00 20 80 10 90 a0 b0 03 3b 98 a2 b0 82
10 0b 30 06 26 30 0f];
qcom,saw2-spm-cmd-pc = [00 20 80 10 90 a0 b0 07 3b 98 a2 b0 82
@@ -116,11 +124,11 @@
qcom,vctl-timeout-us = <50>;
qcom,vctl-port = <0x0>;
qcom,phase-port = <0x1>;
+ qcom,pfm-port = <0x2>;
qcom,saw2-spm-cmd-ret = [00 20 03 22 00 0f];
- qcom,saw2-spm-cmd-gdhs = [00 20 32 0b 42 07 44 22 50 02 32 50
- 0f];
- qcom,saw2-spm-cmd-pc = [00 10 32 b0 11 0b 42 07 01 b0 12 44 a0
- 50 02 32 a0 50 0f];
+ qcom,saw2-spm-cmd-gdhs = [00 20 32 42 07 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,lpm-resources {
@@ -131,6 +139,7 @@
qcom,lpm-resources@0 {
reg = <0x0>;
qcom,name = "vdd-dig";
+ qcom,resource-type = <0>;
qcom,type = <0x62706d73>; /* "smpb" */
qcom,id = <0x02>;
qcom,key = <0x6e726f63>; /* "corn" */
@@ -139,6 +148,7 @@
qcom,lpm-resources@1 {
reg = <0x1>;
qcom,name = "vdd-mem";
+ qcom,resource-type = <0>;
qcom,type = <0x62706d73>; /* "smpb" */
qcom,id = <0x01>;
qcom,key = <0x7675>; /* "uv" */
@@ -147,10 +157,17 @@
qcom,lpm-resources@2 {
reg = <0x2>;
qcom,name = "pxo";
+ qcom,resource-type = <0>;
qcom,type = <0x306b6c63>; /* "clk0" */
qcom,id = <0x00>;
qcom,key = <0x62616e45>; /* "Enab" */
};
+
+ qcom,lpm-resources@3 {
+ reg = <0x3>;
+ qcom,name = "l2";
+ qcom,resource-type = <1>;
+ };
};
qcom,lpm-levels {
@@ -175,6 +192,22 @@
qcom,lpm-level@1 {
reg = <0x1>;
+ qcom,mode = <4>; /* MSM_PM_SLEEP_MODE_RETENTION*/
+ qcom,xo = <1>; /* ON */
+ qcom,l2 = <3>; /* ACTIVE */
+ qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
+ qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
+ qcom,vdd-dig-upper-bound = <5>; /* MAX */
+ qcom,vdd-dig-lower-bound = <3>; /* ACTIVE */
+ qcom,latency-us = <1500>;
+ qcom,ss-power = <200>;
+ qcom,energy-overhead = <576000>;
+ qcom,time-overhead = <2000>;
+ };
+
+
+ qcom,lpm-level@2 {
+ reg = <0x2>;
qcom,mode = <2>; /* MSM_PM_SLEEP_MODE_STANDALONE_POWER_COLLAPSE */
qcom,xo = <1>; /* ON */
qcom,l2 = <3>; /* ACTIVE */
@@ -188,8 +221,8 @@
qcom,time-overhead = <2000>;
};
- qcom,lpm-level@2 {
- reg = <0x2>;
+ qcom,lpm-level@3 {
+ reg = <0x3>;
qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <1>; /* ON */
qcom,l2 = <1>; /* GDHS */
@@ -203,8 +236,8 @@
qcom,time-overhead = <8500>;
};
- qcom,lpm-level@3 {
- reg = <0x3>;
+ qcom,lpm-level@4 {
+ reg = <0x4>;
qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <1>; /* ON */
qcom,l2 = <0>; /* OFF */
@@ -218,8 +251,8 @@
qcom,time-overhead = <9000>;
};
- qcom,lpm-level@4 {
- reg = <0x4>;
+ qcom,lpm-level@5 {
+ reg = <0x5>;
qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <1>; /* ON */
qcom,l2 = <0>; /* OFF */
@@ -233,8 +266,8 @@
qcom,time-overhead = <10000>;
};
- qcom,lpm-level@5 {
- reg = <0x5>;
+ qcom,lpm-level@6 {
+ reg = <0x6>;
qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <0>; /* OFF */
qcom,l2 = <1>; /* GDHS */
@@ -248,8 +281,8 @@
qcom,time-overhead = <12000>;
};
- qcom,lpm-level@6 {
- reg = <0x6>;
+ qcom,lpm-level@7 {
+ reg = <0x7>;
qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <0>; /* OFF */
qcom,l2 = <0>; /* OFF */
@@ -263,8 +296,8 @@
qcom,time-overhead = <18000>;
};
- qcom,lpm-level@7 {
- reg = <0x7>;
+ qcom,lpm-level@8 {
+ reg = <0x8>;
qcom,mode= <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <0>; /* OFF */
qcom,l2 = <0>; /* OFF */
@@ -278,8 +311,8 @@
qcom,time-overhead = <23500>;
};
- qcom,lpm-level@8 {
- reg = <0x8>;
+ qcom,lpm-level@9 {
+ reg = <0x9>;
qcom,mode= <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <0>; /* OFF */
qcom,l2 = <0>; /* OFF */
diff --git a/arch/arm/boot/dts/msm9625-cdp.dts b/arch/arm/boot/dts/msm9625-cdp.dts
index aa1ec92..89c269e 100644
--- a/arch/arm/boot/dts/msm9625-cdp.dts
+++ b/arch/arm/boot/dts/msm9625-cdp.dts
@@ -17,7 +17,7 @@
/ {
model = "Qualcomm MSM 9625 CDP";
compatible = "qcom,msm9625-cdp", "qcom,msm9625";
- qcom,msm-id = <134 1 0>;
+ qcom,msm-id = <134 1 0>, <152 1 0>;
};
/* PM8019 GPIO and MPP configuration */
@@ -32,6 +32,14 @@
};
gpio@c300 { /* GPIO 4 */
+ /* ext_2p95v regulator enable config */
+ qcom,mode = <1>; /* Digital output */
+ qcom,output-type = <0>; /* CMOS */
+ qcom,invert = <0>; /* Output low */
+ qcom,out-strength = <1>; /* Low */
+ qcom,vin-sel = <2>; /* PM8019 L11 - 1.8V */
+ qcom,src-select = <0>; /* Constant */
+ qcom,master-en = <1>; /* Enable GPIO */
};
gpio@c400 { /* GPIO 5 */
diff --git a/arch/arm/boot/dts/msm9625-ion.dtsi b/arch/arm/boot/dts/msm9625-ion.dtsi
new file mode 100644
index 0000000..8183264
--- /dev/null
+++ b/arch/arm/boot/dts/msm9625-ion.dtsi
@@ -0,0 +1,35 @@
+/* Copyright (c) 2012, 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.
+ */
+
+/ {
+ qcom,ion {
+ compatible = "qcom,msm-ion";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,ion-heap@30 { /* SYSTEM HEAP */
+ reg = <30>;
+ };
+
+ qcom,ion-heap@25 { /* IOMMU HEAP */
+ reg = <25>;
+ };
+
+ qcom,ion-heap@28 { /* AUDIO HEAP */
+ compatible = "qcom,msm-ion-reserve";
+ reg = <28>;
+ qcom,heap-align = <0x1000>;
+ qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+ qcom,memory-reservation-size = <0xAF000>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/msm9625-mtp.dts b/arch/arm/boot/dts/msm9625-mtp.dts
index 3ec949f..a5673e5 100644
--- a/arch/arm/boot/dts/msm9625-mtp.dts
+++ b/arch/arm/boot/dts/msm9625-mtp.dts
@@ -17,7 +17,7 @@
/ {
model = "Qualcomm MSM 9625 MTP";
compatible = "qcom,msm9625-mtp", "qcom,msm9625";
- qcom,msm-id = <134 8 0>;
+ qcom,msm-id = <134 7 0>, <152 7 0>;
};
/* PM8019 GPIO and MPP configuration */
@@ -32,6 +32,14 @@
};
gpio@c300 { /* GPIO 4 */
+ /* ext_2p95v regulator enable config */
+ qcom,mode = <1>; /* Digital output */
+ qcom,output-type = <0>; /* CMOS */
+ qcom,invert = <0>; /* Output low */
+ qcom,out-strength = <1>; /* Low */
+ qcom,vin-sel = <2>; /* PM8019 L11 - 1.8V */
+ qcom,src-select = <0>; /* Constant */
+ qcom,master-en = <1>; /* Enable GPIO */
};
gpio@c400 { /* GPIO 5 */
diff --git a/arch/arm/boot/dts/msm9625-regulator.dtsi b/arch/arm/boot/dts/msm9625-regulator.dtsi
index c42af2c..dccc723 100644
--- a/arch/arm/boot/dts/msm9625-regulator.dtsi
+++ b/arch/arm/boot/dts/msm9625-regulator.dtsi
@@ -10,155 +10,252 @@
* GNU General Public License for more details.
*/
-&spmi_bus {
- qcom,pm8019@1 {
- pm8019_s1: regulator@1400 {
+&rpm_bus {
+ rpm-regulator-smpa1 {
+ status = "okay";
+ pm8019_s1: regulator-s1 {
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <1050000>;
- qcom,enable-time = <500>;
+ qcom,init-voltage = <1050000>;
status = "okay";
};
+ };
- pm8019_s2: regulator@1700 {
+ rpm-regulator-smpa2 {
+ status = "okay";
+ qcom,allow-atomic = <1>;
+ pm8019_s2: regulator-s2 {
regulator-min-microvolt = <1250000>;
regulator-max-microvolt = <1250000>;
+ qcom,init-voltage = <1250000>;
+ qcom,init-current = <100>;
qcom,system-load = <100000>;
- qcom,enable-time = <500>;
regulator-always-on;
status = "okay";
};
+ };
- pm8019_s3: regulator@1a00 {
- regulator-min-microvolt = <1100000>;
+ rpm-regulator-smpa3 {
+ status = "okay";
+ pm8019_s3: regulator-s3 {
+ regulator-min-microvolt = <1000000>;
regulator-max-microvolt = <1100000>;
+ qcom,init-voltage = <1100000>;
+ qcom,init-current = <100>;
qcom,system-load = <100000>;
- qcom,enable-time = <500>;
regulator-always-on;
status = "okay";
};
+ pm8019_s3_ao: regulator-s3-ao {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8019_s3_ao";
+ qcom,set = <1>;
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1100000>;
+ status = "okay";
+ };
+ };
- pm8019_s4: regulator@1d00 {
+ rpm-regulator-smpa4 {
+ status = "okay";
+ pm8019_s4: regulator-s4 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <2075000>;
+ qcom,init-voltage = <2075000>;
+ qcom,init-current = <100>;
qcom,system-load = <100000>;
- qcom,enable-time = <500>;
regulator-always-on;
status = "okay";
};
+ };
- pm8019_l1: regulator@4000 {
+ rpm-regulator-ldoa1 {
+ status = "okay";
+ pm8019_l1: regulator-l1 {
parent-supply = <&pm8019_s2>;
regulator-min-microvolt = <1225000>;
regulator-max-microvolt = <1225000>;
- qcom,enable-time = <200>;
+ qcom,init-voltage = <1225000>;
status = "okay";
};
+ };
- pm8019_l2: regulator@4100 {
+ rpm-regulator-ldoa2 {
+ status = "okay";
+ pm8019_l2: regulator-l2 {
parent-supply = <&pm8019_s4>;
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
- qcom,enable-time = <200>;
+ qcom,init-voltage = <1800000>;
status = "okay";
};
+ };
- pm8019_l3: regulator@4200 {
+ rpm-regulator-ldoa3 {
+ status = "okay";
+ pm8019_l3: regulator-l3 {
parent-supply = <&pm8019_s4>;
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
- qcom,enable-time = <200>;
+ qcom,init-voltage = <1800000>;
status = "okay";
};
+ };
- pm8019_l4: regulator@4300 {
+ rpm-regulator-ldoa4 {
+ status = "okay";
+ pm8019_l4: regulator-l4 {
regulator-min-microvolt = <3075000>;
regulator-max-microvolt = <3075000>;
- qcom,enable-time = <200>;
+ qcom,init-voltage = <3075000>;
status = "okay";
};
+ };
- pm8019_l5: regulator@4400 {
+ rpm-regulator-ldoa5 {
+ status = "okay";
+ pm8019_l5: regulator-l5 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <2850000>;
- qcom,enable-time = <200>;
+ qcom,init-voltage = <1800000>;
status = "okay";
};
+ };
- pm8019_l6: regulator@4500 {
+ rpm-regulator-ldoa6 {
+ status = "okay";
+ pm8019_l6: regulator-l6 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <2850000>;
- qcom,enable-time = <200>;
+ qcom,init-voltage = <1800000>;
status = "okay";
};
+ };
- pm8019_l7: regulator@4600 {
+ rpm-regulator-ldoa7 {
+ status = "okay";
+ pm8019_l7: regulator-l7 {
parent-supply = <&pm8019_s4>;
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
- qcom,enable-time = <200>;
+ qcom,init-voltage = <1800000>;
status = "okay";
};
+ };
- pm8019_l8: regulator@4700 {
+ rpm-regulator-ldoa8 {
+ status = "okay";
+ pm8019_l8: regulator-l8 {
parent-supply = <&pm8019_s4>;
regulator-min-microvolt = <2050000>;
regulator-max-microvolt = <2050000>;
- qcom,enable-time = <200>;
+ qcom,init-voltage = <2050000>;
status = "okay";
};
+ };
- pm8019_l9: regulator@4800 {
+ rpm-regulator-ldoa9 {
+ status = "okay";
+ pm8019_l9: regulator-l9 {
parent-supply = <&pm8019_s2>;
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <1200000>;
+ qcom,init-voltage = <1200000>;
+ qcom,init-current = <10>;
qcom,system-load = <10000>;
- qcom,enable-time = <200>;
regulator-always-on;
status = "okay";
};
+ };
- pm8019_l10: regulator@4900 {
+ rpm-regulator-ldoa10 {
+ status = "okay";
+ pm8019_l10: regulator-l10 {
parent-supply = <&pm8019_s3>;
- regulator-min-microvolt = <1050000>;
+ regulator-min-microvolt = <500000>;
regulator-max-microvolt = <1050000>;
- qcom,system-load = <10000>;
- qcom,enable-time = <200>;
- regulator-always-on;
status = "okay";
};
+ pm8019_l10_corner: regulator-l10-corner {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8019_l10_corner";
+ qcom,set = <3>;
+ regulator-min-microvolt = <1>;
+ regulator-max-microvolt = <7>;
+ qcom,use-voltage-corner;
+ status = "okay";
+ qcom,consumer-supplies = "vdd_dig", "";
+ };
+ pm8019_l10_corner_ao: regulator-l10-corner-ao {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8019_l10_corner_ao";
+ qcom,set = <1>;
+ regulator-min-microvolt = <1>;
+ regulator-max-microvolt = <7>;
+ qcom,use-voltage-corner;
+ status = "okay";
+ };
+ };
- pm8019_l11: regulator@4a00 {
+ rpm-regulator-ldoa11 {
+ status = "okay";
+ pm8019_l11: regulator-l11 {
parent-supply = <&pm8019_s4>;
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
+ qcom,init-voltage = <1800000>;
+ qcom,init-current = <10>;
qcom,system-load = <10000>;
- qcom,enable-time = <200>;
regulator-always-on;
status = "okay";
};
+ };
- pm8019_l12: regulator@4b00 {
+ rpm-regulator-ldoa12 {
+ status = "okay";
+ pm8019_l12: regulator-l12 {
parent-supply = <&pm8019_s3>;
- regulator-min-microvolt = <1050000>;
+ regulator-min-microvolt = <750000>;
regulator-max-microvolt = <1050000>;
- qcom,system-load = <10000>;
- qcom,enable-time = <200>;
- regulator-always-on;
status = "okay";
};
+ pm8019_l12_ao: regulator-l12-ao {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8019_l12_ao";
+ qcom,set = <1>;
+ parent-supply = <&pm8019_s3_ao>;
+ regulator-min-microvolt = <750000>;
+ regulator-max-microvolt = <1050000>;
+ status = "okay";
+ };
+ };
- pm8019_l13: regulator@4c00 {
+ rpm-regulator-ldoa13 {
+ status = "okay";
+ pm8019_l13: regulator-l13 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <2950000>;
- qcom,enable-time = <200>;
+ qcom,init-voltage = <2950000>;
status = "okay";
};
+ };
- pm8019_l14: regulator@4d00 {
+ rpm-regulator-ldoa14 {
+ status = "okay";
+ pm8019_l14: regulator-l14 {
regulator-min-microvolt = <2700000>;
regulator-max-microvolt = <2700000>;
- qcom,enable-time = <200>;
+ qcom,init-voltage = <2700000>;
status = "okay";
};
};
};
+
+/ {
+ ext_2p95v: regulator-isl80101 {
+ compatible = "regulator-fixed";
+ regulator-name = "ext_2p95v";
+ gpio = <&pm8019_gpios 4 0>;
+ enable-active-high;
+ };
+};
diff --git a/arch/arm/boot/dts/msm9625.dtsi b/arch/arm/boot/dts/msm9625.dtsi
index 7b0fc34..8cb7191 100644
--- a/arch/arm/boot/dts/msm9625.dtsi
+++ b/arch/arm/boot/dts/msm9625.dtsi
@@ -11,6 +11,7 @@
*/
/include/ "skeleton.dtsi"
+/include/ "msm9625-ion.dtsi"
/ {
model = "Qualcomm MSM 9625";
@@ -66,6 +67,26 @@
interrupts = <0 109 0>;
};
+ usb@f9a55000 {
+ compatible = "qcom,hsusb-otg";
+ reg = <0xf9a55000 0x400>;
+ interrupts = <0 134 0 0 140 0>;
+ interrupt-names = "core_irq", "async_irq";
+ HSUSB_VDDCX-supply = <&pm8019_l12>;
+ HSUSB_1p8-supply = <&pm8019_l2>;
+ HSUSB_3p3-supply = <&pm8019_l4>;
+
+ qcom,hsusb-otg-phy-type = <2>;
+ qcom,hsusb-otg-mode = <1>;
+ qcom,hsusb-otg-otg-control = <1>;
+ qcom,hsusb-otg-disable-reset;
+ };
+
+ android_usb@fc42b0c8 {
+ compatible = "qcom,android-usb";
+ reg = <0xfc42b0c8 0xc8>;
+ };
+
qcom,nand@f9ac0000 {
compatible = "qcom,msm-nand";
reg = <0xf9ac0000 0x1000>,
@@ -139,6 +160,84 @@
<0x0c50000c>, /* GPIO6 */
<0x0080000d>; /* PON */
};
+
+ i2c@f9925000 {
+ cell-index = <3>;
+ compatible = "qcom,i2c-qup";
+ reg = <0xf9925000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "qup_phys_addr";
+ interrupts = <0 97 0>;
+ interrupt-names = "qup_err_intr";
+ qcom,i2c-bus-freq = <100000>;
+ qcom,i2c-src-freq = <24000000>;
+ };
+
+ sdcc2: qcom,sdcc@f98a4000 {
+ cell-index = <2>; /* SDC2 SD card slot */
+ compatible = "qcom,msm-sdcc";
+ reg = <0xf98a4000 0x800>,
+ <0xf98a4800 0x100>,
+ <0xf9884000 0x7000>;
+ reg-names = "core_mem", "dml_mem", "bam_mem";
+
+ vdd-supply = <&ext_2p95v>;
+
+ vdd-io-supply = <&pm8019_l13>;
+ qcom,sdcc-vdd-io-always_on;
+ qcom,sdcc-vdd-io-lpm_sup;
+ qcom,sdcc-vdd-io-voltage_level = <1800000 2950000>;
+ qcom,sdcc-vdd-io-current_level = <6 22000>;
+
+ qcom,sdcc-pad-pull-on = <0x0 0x3 0x3>;
+ qcom,sdcc-pad-pull-off = <0x0 0x3 0x3>;
+ qcom,sdcc-pad-drv-on = <0x7 0x4 0x4>;
+ qcom,sdcc-pad-drv-off = <0x0 0x0 0x0>;
+
+ qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,sdcc-sup-voltages = <2950 2950>;
+ qcom,sdcc-bus-width = <4>;
+ qcom,sdcc-xpc;
+ qcom,sdcc-bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+ qcom,sdcc-current-limit = <800>;
+
+ interrupt-parent = <&sdcc2>;
+ #address-cells = <0>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 125 0
+ 1 &intc 0 220 0
+ 2 &msmgpio 66 0x3>;
+ interrupt-names = "core_irq", "bam_irq", "status_irq";
+ cd-gpios = <&msmgpio 66 0>;
+ };
+
+ sdcc3: qcom,sdcc@f9864000 {
+ cell-index = <3>; /* SDC3 SDIO slot */
+ compatible = "qcom,msm-sdcc";
+ reg = <0xf9864000 0x800>,
+ <0xf9864800 0x100>,
+ <0xf9844000 0x7000>;
+ reg-names = "core_mem", "dml_mem", "bam_mem";
+ interrupts = <0 127 0>, <0 223 0>;
+ interrupt-names = "core_irq", "bam_irq";
+
+ gpios = <&msmgpio 25 0>,
+ <&msmgpio 24 0>,
+ <&msmgpio 16 0>,
+ <&msmgpio 17 0>,
+ <&msmgpio 18 0>,
+ <&msmgpio 19 0>;
+ qcom,sdcc-gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
+
+ qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000>;
+ qcom,sdcc-sup-voltages = <2950 2950>;
+ qcom,sdcc-bus-width = <4>;
+ qcom,sdcc-bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50";
+ status = "disable";
+ };
};
/include/ "msm-pm8019-rpm-regulator.dtsi"
diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c
index 9dd4347..b4574aa 100644
--- a/arch/arm/common/gic.c
+++ b/arch/arm/common/gic.c
@@ -67,6 +67,7 @@
u32 __percpu *saved_ppi_enable;
u32 __percpu *saved_ppi_conf;
#endif
+ u32 saved_dist_isr[DIV_ROUND_UP(1020, 32)];
struct irq_domain *domain;
unsigned int gic_irqs;
#ifdef CONFIG_GIC_NON_BANKED
@@ -640,6 +641,11 @@
for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
gic_data[gic_nr].saved_spi_enable[i] =
readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
+ if (is_cpu_secure()) {
+ for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
+ gic_data[gic_nr].saved_dist_isr[i] =
+ readl_relaxed(dist_base + GIC_DIST_ISR + i * 4);
+ }
}
/*
@@ -682,6 +688,12 @@
writel_relaxed(gic_data[gic_nr].saved_spi_enable[i],
dist_base + GIC_DIST_ENABLE_SET + i * 4);
+ if (is_cpu_secure()) {
+ for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
+ writel_relaxed(gic_data[gic_nr].saved_dist_isr[i],
+ dist_base + GIC_DIST_ISR + i * 4);
+ }
+
writel_relaxed(saved_dist_ctrl, dist_base + GIC_DIST_CTRL);
}
@@ -1167,41 +1179,35 @@
/*
* Configure the GIC after we come out of power collapse.
* This function will configure some of the GIC registers so as to prepare the
- * core1 to receive an SPI(ACSR_MP_CORE_IPC1, (32 + 8)), which will bring
- * core1 out of GDFS.
+ * secondary cores to receive an SPI(ACSR_MP_CORE_IPC1/IPC2/IPC3, 40/92/93),
+ * which will bring cores out of GDFS.
*/
-void core1_gic_configure_and_raise(void)
+void gic_configure_and_raise(unsigned int irq, unsigned int cpu)
{
struct gic_chip_data *gic = &gic_data[0];
+ struct irq_data *d = irq_get_irq_data(irq);
void __iomem *base = gic_data_dist_base(gic);
- unsigned int value = 0;
+ unsigned int value = 0, byte_offset, offset, bit;
unsigned long flags;
+ offset = ((gic_irq(d) / 32) * 4);
+ bit = BIT(gic_irq(d) % 32);
+
raw_spin_lock_irqsave(&irq_controller_lock, flags);
- value = __raw_readl(base + GIC_DIST_ACTIVE_BIT + 0x4);
- value |= BIT(8);
- __raw_writel(value, base + GIC_DIST_ACTIVE_BIT + 0x4);
+ value = __raw_readl(base + GIC_DIST_ACTIVE_BIT + offset);
+ __raw_writel(value | bit, base + GIC_DIST_ACTIVE_BIT + offset);
mb();
- value = __raw_readl(base + GIC_DIST_TARGET + 0x24);
- value |= BIT(13);
- __raw_writel(value, base + GIC_DIST_TARGET + 0x24);
+ value = __raw_readl(base + GIC_DIST_TARGET + (gic_irq(d) / 4) * 4);
+ byte_offset = (gic_irq(d) % 4) * 8;
+ value |= 1 << (cpu + byte_offset);
+ __raw_writel(value, base + GIC_DIST_TARGET + (gic_irq(d) / 4) * 4);
mb();
- value = __raw_readl(base + GIC_DIST_TARGET + 0x28);
- value |= BIT(1);
- __raw_writel(value, base + GIC_DIST_TARGET + 0x28);
+ value = __raw_readl(base + GIC_DIST_ENABLE_SET + offset);
+ __raw_writel(value | bit, base + GIC_DIST_ENABLE_SET + offset);
mb();
- value = __raw_readl(base + GIC_DIST_ENABLE_SET + 0x4);
- value |= BIT(8);
- __raw_writel(value, base + GIC_DIST_ENABLE_SET + 0x4);
- mb();
-
- value = __raw_readl(base + GIC_DIST_PENDING_SET + 0x4);
- value |= BIT(8);
- __raw_writel(value, base + GIC_DIST_PENDING_SET + 0x4);
- mb();
raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
}
diff --git a/arch/arm/configs/msm7627a-perf_defconfig b/arch/arm/configs/msm7627a-perf_defconfig
index 9a0bfba..76650e0 100644
--- a/arch/arm/configs/msm7627a-perf_defconfig
+++ b/arch/arm/configs/msm7627a-perf_defconfig
@@ -58,6 +58,7 @@
CONFIG_MSM_RPC_PMIC=y
CONFIG_MSM_RPC_USB=y
CONFIG_MSM_RPC_PMAPP=y
+CONFIG_MSM_FIQ=y
CONFIG_ARM_THUMBEE=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
@@ -167,10 +168,10 @@
CONFIG_IP_NF_ARPTABLES=y
CONFIG_IP_NF_ARPFILTER=y
CONFIG_IP_NF_ARP_MANGLE=y
-CONFIG_IP6_NF_MANGLE=y
-CONFIG_IP6_NF_RAW=y
CONFIG_IP6_NF_IPTABLES=y
CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
CONFIG_ATM=y
CONFIG_L2TP=y
CONFIG_L2TP_DEBUGFS=y
@@ -252,6 +253,7 @@
# CONFIG_HW_RANDOM is not set
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_GPIO=y
# CONFIG_I2C_MSM is not set
CONFIG_I2C_QUP=y
CONFIG_DEBUG_GPIO=y
@@ -261,6 +263,7 @@
CONFIG_BATTERY_MSM=y
CONFIG_SENSORS_MSM_ADC=y
CONFIG_MARIMBA_CORE=y
+CONFIG_REGULATOR_ONSEMI_NCP6335D=y
CONFIG_REGULATOR_MSM_GPIO=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_CONTROLLER=y
@@ -378,4 +381,3 @@
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_USER=y
CONFIG_CRYPTO_TWOFISH=y
-CONFIG_CRC_CCITT=y
diff --git a/arch/arm/configs/msm7627a_defconfig b/arch/arm/configs/msm7627a_defconfig
index 60a2d72..8ab57de 100644
--- a/arch/arm/configs/msm7627a_defconfig
+++ b/arch/arm/configs/msm7627a_defconfig
@@ -60,6 +60,7 @@
CONFIG_MSM_RPC_PMIC=y
CONFIG_MSM_RPC_USB=y
CONFIG_MSM_RPC_PMAPP=y
+CONFIG_MSM_FIQ=y
CONFIG_ARM_THUMBEE=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
@@ -169,10 +170,10 @@
CONFIG_IP_NF_ARPTABLES=y
CONFIG_IP_NF_ARPFILTER=y
CONFIG_IP_NF_ARP_MANGLE=y
-CONFIG_IP6_NF_MANGLE=y
-CONFIG_IP6_NF_RAW=y
CONFIG_IP6_NF_IPTABLES=y
CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
CONFIG_ATM=y
CONFIG_L2TP=y
CONFIG_L2TP_DEBUGFS=y
@@ -254,6 +255,7 @@
# CONFIG_HW_RANDOM is not set
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_GPIO=y
# CONFIG_I2C_MSM is not set
CONFIG_I2C_QUP=y
CONFIG_DEBUG_GPIO=y
@@ -263,6 +265,7 @@
CONFIG_BATTERY_MSM=y
CONFIG_SENSORS_MSM_ADC=y
CONFIG_MARIMBA_CORE=y
+CONFIG_REGULATOR_ONSEMI_NCP6335D=y
CONFIG_REGULATOR_MSM_GPIO=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_CONTROLLER=y
diff --git a/arch/arm/configs/msm7630-perf_defconfig b/arch/arm/configs/msm7630-perf_defconfig
index 401654d..f2d25ac 100644
--- a/arch/arm/configs/msm7630-perf_defconfig
+++ b/arch/arm/configs/msm7630-perf_defconfig
@@ -280,8 +280,8 @@
CONFIG_FB_MSM_TRIPLE_BUFFER=y
CONFIG_FB_MSM_MDP40=y
CONFIG_FB_MSM_OVERLAY=y
-CONFIG_FB_MSM_OVERLAY0_WRITEBACK=y
CONFIG_FB_MSM_NO_MDP_PIPE_CTRL=y
+CONFIG_FB_MSM_OVERLAY0_WRITEBACK=y
CONFIG_FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM=y
CONFIG_FB_MSM_HDMI_ADV7520_PANEL=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
diff --git a/arch/arm/configs/msm8660-perf_defconfig b/arch/arm/configs/msm8660-perf_defconfig
index 957dbcf..2ee3f3b 100644
--- a/arch/arm/configs/msm8660-perf_defconfig
+++ b/arch/arm/configs/msm8660-perf_defconfig
@@ -35,8 +35,6 @@
CONFIG_DEFAULT_DEADLINE=y
CONFIG_ARCH_MSM=y
CONFIG_ARCH_MSM8X60=y
-CONFIG_MACH_MSM8X60_RUMI3=y
-CONFIG_MACH_MSM8X60_SIM=y
CONFIG_MACH_MSM8X60_SURF=y
CONFIG_MACH_MSM8X60_FFA=y
CONFIG_MACH_MSM8X60_FLUID=y
@@ -64,13 +62,13 @@
CONFIG_MSM_RMT_STORAGE_CLIENT=y
CONFIG_MSM_SDIO_SMEM=y
# CONFIG_MSM_HW3D is not set
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_PIL_MODEM=y
CONFIG_MSM_PIL_QDSP6V3=y
CONFIG_MSM_PIL_TZAPPS=y
CONFIG_MSM_PIL_DSPS=y
CONFIG_MSM_PIL_VIDC=y
-CONFIG_MSM_SUBSYSTEM_RESTART=y
-CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
diff --git a/arch/arm/configs/msm8660_defconfig b/arch/arm/configs/msm8660_defconfig
index 4e5479a..25c5207 100644
--- a/arch/arm/configs/msm8660_defconfig
+++ b/arch/arm/configs/msm8660_defconfig
@@ -34,8 +34,6 @@
CONFIG_DEFAULT_DEADLINE=y
CONFIG_ARCH_MSM=y
CONFIG_ARCH_MSM8X60=y
-CONFIG_MACH_MSM8X60_RUMI3=y
-CONFIG_MACH_MSM8X60_SIM=y
CONFIG_MACH_MSM8X60_SURF=y
CONFIG_MACH_MSM8X60_FFA=y
CONFIG_MACH_MSM8X60_FLUID=y
@@ -63,20 +61,19 @@
CONFIG_MSM_RMT_STORAGE_CLIENT=y
CONFIG_MSM_SDIO_SMEM=y
# CONFIG_MSM_HW3D is not set
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_PIL_MODEM=y
CONFIG_MSM_PIL_QDSP6V3=y
CONFIG_MSM_PIL_TZAPPS=y
CONFIG_MSM_PIL_DSPS=y
CONFIG_MSM_PIL_VIDC=y
-CONFIG_MSM_SUBSYSTEM_RESTART=y
-CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
CONFIG_MSM_WATCHDOG=y
CONFIG_MSM_DLOAD_MODE=y
CONFIG_MSM_ETM=y
-CONFIG_MSM_SLEEP_STATS=y
CONFIG_MSM_GSBI9_UART=y
CONFIG_STRICT_MEMORY_RWX=y
CONFIG_NO_HZ=y
diff --git a/arch/arm/configs/msm8910_defconfig b/arch/arm/configs/msm8910_defconfig
new file mode 100644
index 0000000..a9dadee
--- /dev/null
+++ b/arch/arm/configs/msm8910_defconfig
@@ -0,0 +1,110 @@
+# CONFIG_ARM_PATCH_PHYS_VIRT is not set
+CONFIG_EXPERIMENTAL=y
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SYSVIPC=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_CGROUP_SCHED=y
+# CONFIG_FAIR_GROUP_SCHED is not set
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_IPC_NS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_RD_BZIP2=y
+CONFIG_RD_LZMA=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_EMBEDDED=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=m
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_ARCH_MSM=y
+CONFIG_ARCH_MSM8910=y
+# CONFIG_MSM_STACKED_MEMORY is not set
+CONFIG_CPU_HAS_L2_PMU=y
+# CONFIG_MSM_FIQ_SUPPORT is not set
+# CONFIG_MSM_PROC_COMM is not set
+CONFIG_MSM_SMD=y
+CONFIG_MSM_DIRECT_SCLK_ACCESS=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_ARM_ARCH_TIMER=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+CONFIG_HIGHMEM=y
+CONFIG_VMALLOC_RESERVE=0x19000000
+CONFIG_USE_OF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_SUSPEND is not set
+CONFIG_NET=y
+CONFIG_UNIX=y
+CONFIG_NETFILTER=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+# CONFIG_ANDROID_PMEM is not set
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=m
+CONFIG_SERIO_LIBPS2=y
+CONFIG_SERIAL_MSM_HSL=y
+CONFIG_SERIAL_MSM_HSL_CONSOLE=y
+CONFIG_DIAG_CHAR=y
+CONFIG_HW_RANDOM=y
+CONFIG_DEBUG_GPIO=y
+CONFIG_GPIO_SYSFS=y
+# CONFIG_HWMON is not set
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
+CONFIG_USB_GADGET_DEBUG_FS=y
+CONFIG_USB_CI13XXX_MSM=y
+CONFIG_USB_G_ANDROID=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_FS=y
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_TIMER_STATS=y
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_USER=y
+CONFIG_KEYS=y
+CONFIG_CRYPTO_AUTHENC=y
+CONFIG_CRYPTO_CBC=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_MD4=y
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=y
+CONFIG_CRYPTO_SHA256=y
+CONFIG_CRYPTO_AES=y
+CONFIG_CRYPTO_ARC4=y
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRYPTO_DEFLATE=y
+# CONFIG_CRYPTO_HW is not set
+CONFIG_CRC_CCITT=y
+CONFIG_CRC16=y
+CONFIG_LIBCRC32C=y
diff --git a/arch/arm/configs/msm8960-perf_defconfig b/arch/arm/configs/msm8960-perf_defconfig
index 3fdc804..53e6260 100644
--- a/arch/arm/configs/msm8960-perf_defconfig
+++ b/arch/arm/configs/msm8960-perf_defconfig
@@ -39,8 +39,6 @@
CONFIG_ARCH_MSM8930=y
CONFIG_ARCH_APQ8064=y
CONFIG_MSM_KRAIT_TBB_ABORT_HANDLER=y
-CONFIG_MACH_MSM8960_SIM=y
-CONFIG_MACH_MSM8960_RUMI3=y
CONFIG_MACH_MSM8960_CDP=y
CONFIG_MACH_MSM8960_MTP=y
CONFIG_MACH_MSM8960_FLUID=y
@@ -50,8 +48,6 @@
CONFIG_MACH_MSM8930_FLUID=y
CONFIG_MACH_MSM8627_CDP=y
CONFIG_MACH_MSM8627_MTP=y
-CONFIG_MACH_APQ8064_SIM=y
-CONFIG_MACH_APQ8064_RUMI3=y
CONFIG_MACH_APQ8064_CDP=y
CONFIG_MACH_APQ8064_MTP=y
CONFIG_MACH_APQ8064_LIQUID=y
@@ -66,38 +62,35 @@
CONFIG_MSM_SMD_PKG4=y
CONFIG_MSM_PCIE=y
CONFIG_MSM_BAM_DMUX=y
+CONFIG_MSM_IPC_LOGGING=y
CONFIG_MSM_DSPS=y
CONFIG_MSM_IPC_ROUTER=y
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_AVS_HW=y
# CONFIG_MSM_HW3D is not set
-CONFIG_MSM_PIL_QDSP6V4=y
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
+CONFIG_MSM_PIL_LPASS_QDSP6V4=y
+CONFIG_MSM_PIL_MODEM_QDSP6V4=y
CONFIG_MSM_PIL_RIVA=y
CONFIG_MSM_PIL_TZAPPS=y
CONFIG_MSM_PIL_DSPS=y
CONFIG_MSM_PIL_VIDC=y
CONFIG_MSM_PIL_GSS=y
-CONFIG_MSM_SUBSYSTEM_RESTART=y
-CONFIG_MSM_SYSMON_COMM=y
-CONFIG_MSM_MODEM_8960=y
-CONFIG_MSM_LPASS_8960=y
-CONFIG_MSM_WCNSS_SSR_8960=y
-CONFIG_MSM_GSS_SSR_8064=y
CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_RPM_LOG=y
-CONFIG_MSM_RPM_RBCPR_STATS_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
+CONFIG_MSM_RPM_RBCPR_STATS_LOG=y
+CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_BUS_SCALING=y
CONFIG_MSM_BUS_RPM_MULTI_TIER_ENABLED=y
CONFIG_MSM_WATCHDOG=y
CONFIG_MSM_DLOAD_MODE=y
-CONFIG_MSM_SLEEP_STATS=y
CONFIG_MSM_EBI_ERP=y
CONFIG_MSM_CACHE_ERP=y
CONFIG_MSM_L1_ERR_PANIC=y
CONFIG_MSM_L1_ERR_LOG=y
CONFIG_MSM_L2_ERP_2BIT_PANIC=y
-CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_DCVS=y
CONFIG_MSM_HSIC_SYSMON=y
CONFIG_STRICT_MEMORY_RWX=y
@@ -231,7 +224,6 @@
CONFIG_NET_EMATCH_META=y
CONFIG_NET_EMATCH_TEXT=y
CONFIG_NET_CLS_ACT=y
-CONFIG_MARIMBA_CORE=y
CONFIG_BT=y
CONFIG_BT_RFCOMM=y
CONFIG_BT_RFCOMM_TTY=y
@@ -242,8 +234,8 @@
CONFIG_BT_HCISMD=y
CONFIG_BT_HCIUART=y
CONFIG_BT_HCIUART_H4=y
-CONFIG_BT_HCIUART_IBS=y
CONFIG_BT_HCIUART_ATH3K=y
+CONFIG_BT_HCIUART_IBS=y
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=m
# CONFIG_CFG80211_WEXT is not set
@@ -252,6 +244,7 @@
CONFIG_GENLOCK_MISCDEVICE=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
+CONFIG_TSPP=m
CONFIG_HAPTIC_ISA1200=y
CONFIG_PMIC8XXX_VIBRATOR=y
CONFIG_QSEECOM=y
@@ -284,6 +277,7 @@
CONFIG_USB_USBNET=y
CONFIG_MSM_RMNET_USB=y
CONFIG_WCNSS_CORE=y
+CONFIG_WCNSS_MEM_PRE_ALLOC=y
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_EVBUG=m
CONFIG_KEYBOARD_GPIO=y
@@ -296,11 +290,10 @@
CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_PMIC8XXX_PWRKEY=y
+CONFIG_INPUT_MPU3050=y
CONFIG_INPUT_UINPUT=y
CONFIG_STM_LIS3DH=y
-CONFIG_INPUT_MPU3050=y
# CONFIG_LEGACY_PTYS is not set
-CONFIG_MSM_IPC_LOGGING=y
CONFIG_N_SMUX=y
CONFIG_N_SMUX_LOOPBACK=y
CONFIG_SMUX_CTL=y
@@ -334,6 +327,7 @@
CONFIG_THERMAL_TSENS8960=y
CONFIG_THERMAL_PM8XXX=y
CONFIG_THERMAL_MONITOR=y
+CONFIG_MARIMBA_CORE=y
CONFIG_MFD_PM8921_CORE=y
CONFIG_MFD_PM8821_CORE=y
CONFIG_MFD_PM8038_CORE=y
@@ -347,7 +341,7 @@
CONFIG_MEDIA_CONTROLLER=y
CONFIG_VIDEO_DEV=y
CONFIG_VIDEO_V4L2_SUBDEV_API=y
-CONFIG_MSM_WFD=y
+CONFIG_DVB_CORE=m
CONFIG_USER_RC_INPUT=y
CONFIG_IR_GPIO_CIR=y
# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
@@ -372,8 +366,14 @@
CONFIG_MSM_CSI20_HEADER=y
CONFIG_S5K3L1YX=y
CONFIG_IMX091=y
+CONFIG_MSM_WFD=y
CONFIG_RADIO_IRIS=y
CONFIG_RADIO_IRIS_TRANSPORT=m
+# CONFIG_DVB_FE_CUSTOMISE is not set
+CONFIG_DVB_MPQ=m
+CONFIG_DVB_MPQ_DEMUX=m
+CONFIG_DVB_MPQ_VIDEO=m
+CONFIG_DVB_MPQ_TSPP1=y
CONFIG_ION=y
CONFIG_ION_MSM=y
CONFIG_MSM_KGSL=y
@@ -437,7 +437,6 @@
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_CI13XXX_MSM=y
CONFIG_USB_G_ANDROID=y
-CONFIG_USB_ANDROID_RMNET_CTRL_SMD=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
CONFIG_MMC_UNSAFE_RESUME=y
@@ -473,6 +472,7 @@
CONFIG_MOBICORE_SUPPORT=m
CONFIG_MOBICORE_API=m
CONFIG_MSM_QDSS=y
+CONFIG_CONTROL_TRACE=m
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
@@ -505,4 +505,3 @@
CONFIG_CRYPTO_DEV_QCE=m
CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_CRC_CCITT=y
-CONFIG_WCNSS_MEM_PRE_ALLOC=y
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index 4daaa12..5770859 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -38,8 +38,6 @@
CONFIG_ARCH_MSM8930=y
CONFIG_ARCH_APQ8064=y
CONFIG_MSM_KRAIT_TBB_ABORT_HANDLER=y
-CONFIG_MACH_MSM8960_SIM=y
-CONFIG_MACH_MSM8960_RUMI3=y
CONFIG_MACH_MSM8960_CDP=y
CONFIG_MACH_MSM8960_MTP=y
CONFIG_MACH_MSM8960_FLUID=y
@@ -49,8 +47,6 @@
CONFIG_MACH_MSM8930_FLUID=y
CONFIG_MACH_MSM8627_CDP=y
CONFIG_MACH_MSM8627_MTP=y
-CONFIG_MACH_APQ8064_SIM=y
-CONFIG_MACH_APQ8064_RUMI3=y
CONFIG_MACH_APQ8064_CDP=y
CONFIG_MACH_APQ8064_MTP=y
CONFIG_MACH_APQ8064_LIQUID=y
@@ -65,30 +61,28 @@
CONFIG_MSM_SMD_PKG4=y
CONFIG_MSM_PCIE=y
CONFIG_MSM_BAM_DMUX=y
+CONFIG_MSM_IPC_LOGGING=y
CONFIG_MSM_DSPS=y
CONFIG_MSM_IPC_ROUTER=y
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_AVS_HW=y
# CONFIG_MSM_HW3D is not set
-CONFIG_MSM_PIL_QDSP6V4=y
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
+CONFIG_MSM_PIL_LPASS_QDSP6V4=y
+CONFIG_MSM_PIL_MODEM_QDSP6V4=y
CONFIG_MSM_PIL_RIVA=y
CONFIG_MSM_PIL_TZAPPS=y
CONFIG_MSM_PIL_DSPS=y
CONFIG_MSM_PIL_VIDC=y
CONFIG_MSM_PIL_GSS=y
-CONFIG_MSM_SUBSYSTEM_RESTART=y
-CONFIG_MSM_SYSMON_COMM=y
-CONFIG_MSM_MODEM_8960=y
-CONFIG_MSM_LPASS_8960=y
-CONFIG_MSM_WCNSS_SSR_8960=y
-CONFIG_MSM_GSS_SSR_8064=y
CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
CONFIG_MSM_RPM_RBCPR_STATS_LOG=y
+CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_BUS_SCALING=y
CONFIG_MSM_BUS_RPM_MULTI_TIER_ENABLED=y
-CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_WATCHDOG=y
CONFIG_MSM_DLOAD_MODE=y
CONFIG_MSM_RTB=y
@@ -235,7 +229,6 @@
CONFIG_NET_EMATCH_META=y
CONFIG_NET_EMATCH_TEXT=y
CONFIG_NET_CLS_ACT=y
-CONFIG_MARIMBA_CORE=y
CONFIG_BT=y
CONFIG_BT_RFCOMM=y
CONFIG_BT_RFCOMM_TTY=y
@@ -245,8 +238,8 @@
CONFIG_BT_HIDP=y
CONFIG_BT_HCISMD=y
CONFIG_BT_HCIUART=y
-CONFIG_BT_HCIUART_ATH3K=y
CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_ATH3K=y
CONFIG_BT_HCIUART_IBS=y
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=m
@@ -256,6 +249,7 @@
CONFIG_GENLOCK_MISCDEVICE=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
+CONFIG_TSPP=m
CONFIG_HAPTIC_ISA1200=y
CONFIG_PMIC8XXX_VIBRATOR=y
CONFIG_QSEECOM=y
@@ -288,6 +282,7 @@
CONFIG_USB_USBNET=y
CONFIG_MSM_RMNET_USB=y
CONFIG_WCNSS_CORE=y
+CONFIG_WCNSS_MEM_PRE_ALLOC=y
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_EVBUG=m
CONFIG_KEYBOARD_GPIO=y
@@ -300,11 +295,10 @@
CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_PMIC8XXX_PWRKEY=y
+CONFIG_INPUT_MPU3050=y
CONFIG_INPUT_UINPUT=y
CONFIG_STM_LIS3DH=y
-CONFIG_INPUT_MPU3050=y
# CONFIG_LEGACY_PTYS is not set
-CONFIG_MSM_IPC_LOGGING=y
CONFIG_N_SMUX=y
CONFIG_N_SMUX_LOOPBACK=y
CONFIG_SMUX_CTL=y
@@ -338,6 +332,7 @@
CONFIG_THERMAL_TSENS8960=y
CONFIG_THERMAL_PM8XXX=y
CONFIG_THERMAL_MONITOR=y
+CONFIG_MARIMBA_CORE=y
CONFIG_MFD_PM8921_CORE=y
CONFIG_MFD_PM8821_CORE=y
CONFIG_MFD_PM8038_CORE=y
@@ -351,7 +346,7 @@
CONFIG_MEDIA_CONTROLLER=y
CONFIG_VIDEO_DEV=y
CONFIG_VIDEO_V4L2_SUBDEV_API=y
-CONFIG_MSM_WFD=y
+CONFIG_DVB_CORE=m
CONFIG_USER_RC_INPUT=y
CONFIG_IR_GPIO_CIR=y
# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
@@ -375,8 +370,14 @@
CONFIG_MSM_CSI20_HEADER=y
CONFIG_S5K3L1YX=y
CONFIG_IMX091=y
+CONFIG_MSM_WFD=y
CONFIG_RADIO_IRIS=y
CONFIG_RADIO_IRIS_TRANSPORT=m
+# CONFIG_DVB_FE_CUSTOMISE is not set
+CONFIG_DVB_MPQ=m
+CONFIG_DVB_MPQ_DEMUX=m
+CONFIG_DVB_MPQ_VIDEO=m
+CONFIG_DVB_MPQ_TSPP1=y
CONFIG_ION=y
CONFIG_ION_MSM=y
CONFIG_MSM_KGSL=y
@@ -439,7 +440,6 @@
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_CI13XXX_MSM=y
CONFIG_USB_G_ANDROID=y
-CONFIG_USB_ANDROID_RMNET_CTRL_SMD=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
CONFIG_MMC_UNSAFE_RESUME=y
@@ -476,6 +476,7 @@
CONFIG_MOBICORE_API=m
CONFIG_MSM_QDSS=y
CONFIG_MSM_QDSS_ETM_DEFAULT_ENABLE=y
+CONFIG_CONTROL_TRACE=m
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
@@ -513,7 +514,6 @@
CONFIG_FAULT_INJECTION_DEBUG_FS=y
CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
CONFIG_DEBUG_PAGEALLOC=y
-CONFIG_ENABLE_DEFAULT_TRACERS=y
CONFIG_CPU_FREQ_SWITCH_PROFILER=y
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_USER=y
@@ -523,4 +523,3 @@
CONFIG_CRYPTO_DEV_QCE=m
CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_CRC_CCITT=y
-CONFIG_WCNSS_MEM_PRE_ALLOC=y
diff --git a/arch/arm/configs/msm8974-perf_defconfig b/arch/arm/configs/msm8974-perf_defconfig
index 2f1833e..b2ee503 100644
--- a/arch/arm/configs/msm8974-perf_defconfig
+++ b/arch/arm/configs/msm8974-perf_defconfig
@@ -55,9 +55,6 @@
CONFIG_MSM_PIL_MBA=y
CONFIG_MSM_PIL_VENUS=y
CONFIG_MSM_PIL_PRONTO=y
-CONFIG_MSM_MODEM_SSR_8974=y
-CONFIG_MSM_ADSP_SSR_8974=y
-CONFIG_MSM_WCNSS_SSR_8974=y
CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
CONFIG_MSM_BUS_SCALING=y
@@ -73,6 +70,7 @@
CONFIG_MSM_L1_ERR_PANIC=y
CONFIG_MSM_L1_ERR_LOG=y
CONFIG_MSM_L2_ERP_2BIT_PANIC=y
+CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
CONFIG_STRICT_MEMORY_RWX=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
@@ -287,6 +285,7 @@
CONFIG_VIDEOBUF2_MSM_MEM=y
CONFIG_V4L_PLATFORM_DRIVERS=y
CONFIG_MSM_CAMERA_V4L2=y
+CONFIG_MT9M114=y
CONFIG_OV2720=y
CONFIG_MSM_CAMERA_SENSOR=y
CONFIG_MSM_ACTUATOR=y
@@ -298,7 +297,6 @@
CONFIG_MSM_CSI2_REGISTER=y
CONFIG_MSM_ISPIF=y
CONFIG_S5K3L1YX=y
-CONFIG_MT9M114=y
CONFIG_RADIO_IRIS=y
CONFIG_RADIO_IRIS_TRANSPORT=m
CONFIG_ION=y
@@ -402,4 +400,3 @@
CONFIG_CRYPTO_DEV_QCE=m
CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_CRC_CCITT=y
-CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index d6e84f6..d8d2eae 100644
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -55,9 +55,6 @@
CONFIG_MSM_PIL_MBA=y
CONFIG_MSM_PIL_VENUS=y
CONFIG_MSM_PIL_PRONTO=y
-CONFIG_MSM_MODEM_SSR_8974=y
-CONFIG_MSM_ADSP_SSR_8974=y
-CONFIG_MSM_WCNSS_SSR_8974=y
CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
CONFIG_MSM_BUS_SCALING=y
@@ -76,6 +73,7 @@
CONFIG_MSM_L2_ERP_2BIT_PANIC=y
CONFIG_MSM_CACHE_DUMP=y
CONFIG_MSM_CACHE_DUMP_ON_PANIC=y
+CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
CONFIG_STRICT_MEMORY_RWX=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
@@ -275,15 +273,18 @@
CONFIG_GPIO_QPNP_PIN_DEBUG=y
CONFIG_POWER_SUPPLY=y
# CONFIG_BATTERY_MSM is not set
+CONFIG_QPNP_CHARGER=y
CONFIG_QPNP_BMS=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_SENSORS_QPNP_ADC_CURRENT=y
CONFIG_THERMAL=y
CONFIG_THERMAL_TSENS8974=y
+CONFIG_THERMAL_QPNP=y
CONFIG_WCD9320_CODEC=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_STUB=y
CONFIG_REGULATOR_QPNP=y
+CONFIG_QPNP_PWM=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_CONTROLLER=y
CONFIG_VIDEO_DEV=y
@@ -398,7 +399,6 @@
CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
# CONFIG_DEBUG_PREEMPT is not set
CONFIG_DEBUG_SPINLOCK=y
-CONFIG_DEBUG_MUTEXES=y
CONFIG_DEBUG_ATOMIC_SLEEP=y
CONFIG_DEBUG_STACK_USAGE=y
CONFIG_DEBUG_INFO=y
@@ -409,7 +409,6 @@
CONFIG_FAULT_INJECTION_DEBUG_FS=y
CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
CONFIG_DEBUG_PAGEALLOC=y
-CONFIG_ENABLE_DEFAULT_TRACERS=y
CONFIG_CPU_FREQ_SWITCH_PROFILER=y
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_USER=y
@@ -422,4 +421,3 @@
CONFIG_CRYPTO_DEV_QCE=m
CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_CRC_CCITT=y
-CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
diff --git a/arch/arm/configs/msm9615_defconfig b/arch/arm/configs/msm9615_defconfig
index 2cc801e..81b853d 100644
--- a/arch/arm/configs/msm9615_defconfig
+++ b/arch/arm/configs/msm9615_defconfig
@@ -47,9 +47,8 @@
CONFIG_MSM_IPC_ROUTER=y
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_SUBSYSTEM_RESTART=y
-# CONFIG_MSM_SYSMON_COMM is not set
-CONFIG_MSM_MODEM_8960=y
-CONFIG_MSM_LPASS_8960=y
+CONFIG_MSM_PIL_LPASS_QDSP6V4=y
+CONFIG_MSM_PIL_MODEM_QDSP6V4=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
diff --git a/arch/arm/configs/msm9625_defconfig b/arch/arm/configs/msm9625_defconfig
index de93fa5..4e34ebd 100644
--- a/arch/arm/configs/msm9625_defconfig
+++ b/arch/arm/configs/msm9625_defconfig
@@ -35,6 +35,9 @@
CONFIG_CPU_HAS_L2_PMU=y
# CONFIG_MSM_FIQ_SUPPORT is not set
# CONFIG_MSM_PROC_COMM is not set
+CONFIG_MSM_SMD=y
+CONFIG_MSM_SMD_PKG4=y
+CONFIG_MSM_RPM_REGULATOR_SMD=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
CONFIG_MSM_WATCHDOG_V2=y
CONFIG_NO_HZ=y
@@ -45,10 +48,11 @@
CONFIG_HIGHMEM=y
CONFIG_VMALLOC_RESERVE=0x19000000
CONFIG_USE_OF=y
+CONFIG_CPU_IDLE=y
CONFIG_VFP=y
CONFIG_NEON=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
-# CONFIG_SUSPEND is not set
+CONFIG_PM_RUNTIME=y
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
@@ -87,13 +91,18 @@
# CONFIG_LEGACY_PTYS is not set
CONFIG_SERIAL_MSM_HSL=y
CONFIG_SERIAL_MSM_HSL_CONSOLE=y
+CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_QUP=y
CONFIG_SPI=y
CONFIG_SPI_QUP=y
CONFIG_SPI_SPIDEV=m
CONFIG_SPMI=y
CONFIG_SPMI_MSM_PMIC_ARB=y
CONFIG_MSM_QPNP_INT=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_DEBUG_GPIO=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_QPNP_PIN=y
@@ -101,8 +110,24 @@
# CONFIG_HWMON is not set
CONFIG_REGULATOR=y
CONFIG_REGULATOR_QPNP=y
-# CONFIG_HID_SUPPORT is not set
-# CONFIG_USB_SUPPORT is not set
+CONFIG_ION=y
+CONFIG_ION_MSM=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_CI13XXX_MSM=y
+CONFIG_USB_G_ANDROID=y
+CONFIG_MMC=y
+CONFIG_MMC_PERF_PROFILING=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_EMBEDDED_SDIO=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_TEST=m
+CONFIG_MMC_MSM=y
+CONFIG_MMC_MSM_SPS_SUPPORT=y
CONFIG_RTC_CLASS=y
# CONFIG_RTC_DRV_MSM is not set
CONFIG_RTC_DRV_QPNP=y
@@ -110,6 +135,7 @@
CONFIG_USB_BAM=y
CONFIG_SPS_SUPPORT_BAMDMA=y
CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_QPNP_POWER_ON=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_YAFFS_FS=y
diff --git a/arch/arm/include/asm/fiq.h b/arch/arm/include/asm/fiq.h
index ec4b8b8..006f577 100644
--- a/arch/arm/include/asm/fiq.h
+++ b/arch/arm/include/asm/fiq.h
@@ -39,6 +39,7 @@
extern void set_fiq_handler(void *start, unsigned int length);
extern void enable_fiq(int fiq);
extern void disable_fiq(int fiq);
+extern void fiq_set_type(int fiq, unsigned int type);
#else
static inline int claim_fiq(struct fiq_handler *f)
{
@@ -48,6 +49,7 @@
static inline void set_fiq_handler(void *start, unsigned int length) { }
static inline void enable_fiq(int fiq) { }
static inline void disable_fiq(int fiq) { }
+static inline void fiq_set_type(int fiq, unsigned int type) { }
#endif
/* helpers defined in fiqasm.S: */
diff --git a/arch/arm/include/asm/hardware/gic.h b/arch/arm/include/asm/hardware/gic.h
index ad12bcd..72c3c27 100644
--- a/arch/arm/include/asm/hardware/gic.h
+++ b/arch/arm/include/asm/hardware/gic.h
@@ -62,7 +62,7 @@
void msm_gic_save(void);
void msm_gic_restore(void);
-void core1_gic_configure_and_raise(void);
+void gic_configure_and_raise(unsigned int irq, unsigned int cpu);
#endif
#endif
diff --git a/arch/arm/kernel/fiq.c b/arch/arm/kernel/fiq.c
index c32f845..ca852c5 100644
--- a/arch/arm/kernel/fiq.c
+++ b/arch/arm/kernel/fiq.c
@@ -39,6 +39,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/irq.h>
#include <linux/seq_file.h>
#include <asm/cacheflush.h>
@@ -132,6 +133,11 @@
disable_irq(fiq + FIQ_START);
}
+void fiq_set_type(int fiq, unsigned int type)
+{
+ irq_set_irq_type(fiq + FIQ_START, type);
+}
+
EXPORT_SYMBOL(set_fiq_handler);
EXPORT_SYMBOL(__set_fiq_regs); /* defined in fiqasm.S */
EXPORT_SYMBOL(__get_fiq_regs); /* defined in fiqasm.S */
@@ -139,6 +145,7 @@
EXPORT_SYMBOL(release_fiq);
EXPORT_SYMBOL(enable_fiq);
EXPORT_SYMBOL(disable_fiq);
+EXPORT_SYMBOL(fiq_set_type);
void __init init_FIQ(void)
{
diff --git a/arch/arm/kernel/perf_event_msm.c b/arch/arm/kernel/perf_event_msm.c
index 90c9c9e..8f58adf 100644
--- a/arch/arm/kernel/perf_event_msm.c
+++ b/arch/arm/kernel/perf_event_msm.c
@@ -709,8 +709,6 @@
static struct arm_pmu scorpion_pmu = {
.handle_irq = armv7pmu_handle_irq,
- .request_pmu_irq = msm_request_irq,
- .free_pmu_irq = msm_free_irq,
.enable = scorpion_pmu_enable_event,
.disable = scorpion_pmu_disable_event,
.read_counter = armv7pmu_read_counter,
@@ -731,6 +729,9 @@
scorpion_pmu.name = "ARMv7 Scorpion";
scorpion_pmu.num_events = armv7_read_num_pmnc_events();
scorpion_pmu.pmu.attr_groups = msm_l1_pmu_attr_grps;
+ /* Unicore can't use the percpu IRQ API. */
+ scorpion_pmu.request_pmu_irq = armpmu_generic_request_irq;
+ scorpion_pmu.free_pmu_irq = armpmu_generic_free_irq;
scorpion_clear_pmuregs();
return &scorpion_pmu;
}
@@ -741,6 +742,8 @@
scorpion_pmu.name = "ARMv7 Scorpion-MP";
scorpion_pmu.num_events = armv7_read_num_pmnc_events();
scorpion_pmu.pmu.attr_groups = msm_l1_pmu_attr_grps;
+ scorpion_pmu.request_pmu_irq = msm_request_irq;
+ scorpion_pmu.free_pmu_irq = msm_free_irq;
scorpion_clear_pmuregs();
return &scorpion_pmu;
}
diff --git a/arch/arm/kernel/perf_event_msm_krait.c b/arch/arm/kernel/perf_event_msm_krait.c
index 8d8f47a..eec614b 100644
--- a/arch/arm/kernel/perf_event_msm_krait.c
+++ b/arch/arm/kernel/perf_event_msm_krait.c
@@ -540,8 +540,8 @@
int err = 0;
int cpu;
- err = request_percpu_irq(irq, *handle_irq, "krait-l1-armpmu",
- &cpu_hw_events);
+ err = request_percpu_irq(irq, *handle_irq, "l1-armpmu",
+ &cpu_hw_events);
if (!err) {
for_each_cpu(cpu, cpu_online_mask) {
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 157f159..8374296 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -22,7 +22,7 @@
config ARCH_MSM7X27
bool "MSM7x27"
select ARCH_MSM_ARM11 if MSM_SOC_REV_NONE
- select ARCH_HAS_BARRIERS if MSM_SOC_REV_NONE
+ select ARCH_HAS_BARRIERS
select ARCH_MSM_CORTEX_A5 if MSM_SOC_REV_A
select MSM_VIC
select CPU_V6 if MSM_SOC_REV_NONE
@@ -315,6 +315,7 @@
select GIC_SECURE
select ARCH_MSM_CORTEX_A5
select CPU_V7
+ select MIGHT_HAVE_CACHE_L2X0
select GPIO_MSM_V2
select MSM_GPIOMUX
select MSM_RPM
@@ -355,10 +356,31 @@
select MSM_SMP
select CPU_V7
select MSM_GPIOMUX
+ select MSM_RPM_SMD
+ select MSM_NATIVE_RESTART
+ select MSM_RESTART_V2
+ select MSM_SPM_V2
+ select MSM_PM8X60 if PM
+ select MSM_SCM if SMP
select MULTI_IRQ_HANDLER
select GPIO_MSM_V3
select MAY_HAVE_SPARSE_IRQ
select SPARSE_IRQ
+ select MSM_MULTIMEDIA_USE_ION
+
+config ARCH_MSM8910
+ bool "MSM8910"
+ select ARM_GIC
+ select GIC_SECURE
+ select SMP
+ select ARCH_MSM_CORTEXMP
+ select CPU_V7
+ select MSM_SCM if SMP
+ select MAY_HAVE_SPARSE_IRQ
+ select SPARSE_IRQ
+ select MULTI_IRQ_HANDLER
+ select GPIO_MSM_V3
+ select MSM_GPIOMUX
endmenu
choice
@@ -647,6 +669,18 @@
help
Support for the Qualcomm MSM8625 Reference Design.
+config MACH_QRD_SKUD_PRIME
+ depends on ARCH_MSM8625
+ depends on !MSM_STACKED_MEMORY
+ default y
+ bool "MSM8625 SKUD PRIME"
+ help
+ Support for the Qualcomm MSM8625 SKUD prime Reference Design.
+ Add support for a SKUD prime reference design based on MSM8x25
+ chipset. This device is much closer to a phone than regular form
+ factor devices, with new touch, display panel and other hardware
+ configurations.
+
config MACH_MSM7X30_SURF
depends on ARCH_MSM7X30
depends on !MSM_STACKED_MEMORY
@@ -926,6 +960,7 @@
default "0x00000000" if ARCH_MSM8974
default "0x00000000" if ARCH_MPQ8092
default "0x00000000" if ARCH_MSM8226
+ default "0x80200000" if ARCH_MSM8910
default "0x10000000" if ARCH_FSM9XXX
default "0x00200000" if ARCH_MSM9625
default "0x00200000" if !MSM_STACKED_MEMORY
@@ -1909,25 +1944,39 @@
config MSM_PIL_QDSP6V3
tristate "QDSP6v3 (Hexagon) Boot Support"
- depends on MSM_PIL
+ depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
help
Support for booting and shutting down QDSP6v3 processors (hexagon).
The QDSP6 is a low power DSP used in audio software applications.
-config MSM_PIL_QDSP6V4
- tristate "QDSP6v4 (Hexagon) Boot Support"
- depends on MSM_PIL
+config MSM_PIL_LPASS_QDSP6V4
+ tristate "LPASS QDSP6v4 (Hexagon) Boot Support"
+ depends on MSM_SUBSYSTEM_RESTART
help
- Support for booting and shutting down QDSP6v4 processors (hexagon).
- The QDSP6 is a low power DSP used in audio, modem firmware, and modem
- software applications.
+ Support for booting and shutting down QDSP6v4 processors (hexagon)
+ in low power audio subsystems. If you would like to record or
+ play audio then say Y here.
+
+ If unsure, say N.
+
+config MSM_PIL_MODEM_QDSP6V4
+ tristate "Modem QDSP6v4 (Hexagon) Boot Support"
+ depends on MSM_SUBSYSTEM_RESTART
+ help
+ Support for booting and shutting down QDSP6v4 processors (hexagon)
+ in modem subsystems. If you would like to make or receive phone
+ calls then say Y here.
+
+ If unsure, say N.
config MSM_PIL_LPASS_QDSP6V5
- tristate "LPASS QDSP6v5 (Hexagon) Boot Support"
- depends on MSM_PIL
- help
- Support for booting and shutting down QDSP6v5 (Hexagon) processors
- in low power audio subsystems.
+ tristate "LPASS QDSP6v5 (Hexagon) Boot Support"
+ depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
+ help
+ Support for booting and shutting down QDSP6v5 (Hexagon) processors
+ in low power audio subsystems. This driver also monitors the ADSP
+ SMSM status bits and the ADSP's watchdog interrupt and restarts the
+ ADSP if the processor encounters a fatal error.
config MSM_PIL_MSS_QDSP6V5
tristate "MSS QDSP6v5 (Hexagon) Boot Support"
@@ -1938,14 +1987,14 @@
config MSM_PIL_MBA
tristate "Support for modem self-authentication"
- depends on MSM_PIL_MSS_QDSP6V5
+ depends on MSM_PIL_MSS_QDSP6V5 && MSM_SUBSYSTEM_RESTART
help
Support for booting self-authenticating modems using the Modem Boot
Authenticator.
config MSM_PIL_RIVA
tristate "RIVA (WCNSS) Boot Support"
- depends on MSM_PIL
+ depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
help
Support for booting and shutting down the RIVA processor (WCNSS).
Riva is the wireless subsystem processor used in bluetooth, wireless
@@ -1963,7 +2012,7 @@
config MSM_PIL_DSPS
tristate "DSPS Boot Support"
- depends on MSM_PIL
+ depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
help
Support for booting and shutting down ARM7 DSPS processors.
@@ -1984,15 +2033,15 @@
Venus is the Video subsystem processor used for video codecs.
config MSM_PIL_GSS
- tristate "GSS (Coretx A5) Boot Support"
- depends on MSM_PIL
+ tristate "GSS (Cortex A5) Boot Support"
+ depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
help
Support for booting and shutting down Cortex A5 processors which run
GPS subsystem firmware.
config MSM_PIL_PRONTO
tristate "PRONTO (WCNSS) Boot Support"
- depends on MSM_PIL
+ depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
help
Support for booting and shutting down the PRONTO processor (WCNSS).
PRONTO is the wireless subsystem processor used in bluetooth, wireless
@@ -2002,65 +2051,6 @@
bool "Secure Channel Manager (SCM) support"
default n
-config MSM_MODEM_8960
- bool "MSM 8960 Modem driver"
- depends on (ARCH_MSM8960 || ARCH_MSM9615)
- help
- This option enables the modem driver for the MSM8960 and MSM9615, which monitors
- modem hardware watchdog interrupt lines and plugs into the subsystem
- restart and PIL drivers. For MSM9615, it only supports a full chip reset.
-
-config MSM_LPASS_8960
- tristate "MSM 8960 Lpass driver"
- depends on (ARCH_MSM8960 || ARCH_MSM9615)
- help
- This option enables the lpass driver for the MSM8960 and MSM9615. This monitors
- lpass hardware watchdog interrupt lines and plugs into the subsystem
- restart and PIL drivers. For MSM9615, it only supports a full chip reset.
-
-config MSM_WCNSS_SSR_8960
- tristate "MSM 8960 WCNSS restart module"
- depends on (ARCH_MSM8960)
- help
- This option enables the WCNSS restart module for MSM8960, which
- monitors WCNSS hardware watchdog interrupt lines and plugs WCNSS
- into the subsystem restart framework.
-
-config MSM_GSS_SSR_8064
- bool "MSM 8064 GSS restart driver"
- depends on (ARCH_APQ8064)
- help
- This option enables the gps subsystem restart driver for APQ8064, which monitors
- gss hardware watchdog interrupt lines and plugs into the subsystem
- restart and PIL drivers.
-
-config MSM_MODEM_SSR_8974
- bool "MSM 8974 Modem restart driver"
- depends on (ARCH_MSM8974)
- help
- This option enables the modem subsystem restart driver for the MSM8974.
- It monitors the modem SMSM status bits and the modem watchdog line and
- restarts the modem or the 8974 when the modem encounters a fatal error,
- depending on the restart level selected in the subsystem restart driver.
-
-config MSM_ADSP_SSR_8974
- bool "MSM 8974 adsp restart driver"
- depends on (ARCH_MSM8974)
- help
- This option enables the adsp restart driver for the MSM8974.
- It monitors the adsp SMSM status bits and the adsp watchdog line and
- restarts the adsp or the 8974 when the adsp encounters a fatal error,
- depending on the restart level selected in the subsystem restart driver.
-
-config MSM_WCNSS_SSR_8974
- tristate "MSM 8974 WCNSS restart module"
- depends on (ARCH_MSM8974)
- help
- This option enables the WCNSS restart module for MSM8974. It monitors
- WCNSS SMSM status bits and WCNSS hardware watchdog interrupt line; and
- depending on the restart level, it will restart WCNSS when a fatal error
- occurs at WCNSS.
-
config SCORPION_Uni_45nm_BUG
bool "Scorpion Uni 45nm(SC45U): Workaround for ICIMVAU and BPIMVA"
depends on ARCH_MSM7X30 || (ARCH_QSD8X50 && MSM_SOC_REV_A)
@@ -2651,4 +2641,15 @@
used to control debug image.
This support is currently required for MSM8974 to disable debug image
on PS HOLD reset
+
+config MSM_FIQ
+ bool "Enable FIQ for debugging"
+ depends on ARCH_MSM8625
+ select FIQ
+ select GIC_SECURE
+ help
+ Enable any line to be used as an FIQ. This will help debugging
+ if apps is not responding and holding lock with irqs disabled.
+ Modem will then generate an raise a FIQ on this line before sending
+ SMSM reset.
endif
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 1b42d2d..6a62f8c 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -6,6 +6,7 @@
endif
obj-y += clock.o clock-voter.o clock-dummy.o
obj-y += modem_notifier.o subsystem_map.o
+obj-$(CONFIG_USE_OF) += board-dt.o
obj-$(CONFIG_CPU_FREQ_MSM) += cpufreq.o
obj-$(CONFIG_DEBUG_FS) += nohlt.o clock-debug.o
obj-$(CONFIG_KEXEC) += msm_kexec.o
@@ -24,6 +25,9 @@
obj-y += acpuclock.o
obj-$(CONFIG_ARCH_MSM_KRAIT) += acpuclock-krait.o
+ifdef CONFIG_ARCH_MSM_KRAIT
+obj-$(CONFIG_DEBUG_FS) += acpuclock-krait-debug.o
+endif
obj-$(CONFIG_ARCH_MSM7X27) += acpuclock-7627.o clock-pll.o
obj-$(CONFIG_ARCH_MSM_SCORPION) += pmu.o
obj-$(CONFIG_ARCH_MSM_SCORPIONMP) += perf_event_msm_l2.o
@@ -45,8 +49,12 @@
ifdef CONFIG_ARCH_MSM8625
obj-$(CONFIG_SMP) += platsmp-8625.o
else
+ifdef CONFIG_ARCH_MSM8910
+ obj-$(CONFIG_SMP) += platsmp-8910.o
+else
obj-$(CONFIG_SMP) += platsmp.o
endif
+endif
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
obj-$(CONFIG_MSM_CPU_AVS) += avs.o
@@ -72,7 +80,8 @@
obj-$(CONFIG_MSM_PIL) += peripheral-loader.o
obj-$(CONFIG_MSM_PIL) += scm-pas.o
obj-$(CONFIG_MSM_PIL_QDSP6V3) += pil-q6v3.o
-obj-$(CONFIG_MSM_PIL_QDSP6V4) += pil-q6v4.o
+obj-$(CONFIG_MSM_PIL_LPASS_QDSP6V4) += pil-q6v4.o pil-q6v4-lpass.o
+obj-$(CONFIG_MSM_PIL_MODEM_QDSP6V4) += pil-q6v4.o pil-q6v4-mss.o
obj-$(CONFIG_MSM_PIL_LPASS_QDSP6V5) += pil-q6v5.o pil-q6v5-lpass.o
obj-$(CONFIG_MSM_PIL_MSS_QDSP6V5) += pil-q6v5.o pil-q6v5-mss.o
obj-$(CONFIG_MSM_PIL_MBA) += pil-mba.o
@@ -106,6 +115,7 @@
ifndef CONFIG_ARCH_MSM8226
ifndef CONFIG_ARCH_MSM9625
ifndef CONFIG_ARCH_MPQ8092
+ifndef CONFIG_ARCH_MSM8910
obj-y += nand_partitions.o
endif
endif
@@ -114,6 +124,7 @@
endif
endif
endif
+endif
obj-$(CONFIG_MSM_SDIO_TTY) += sdio_tty.o
obj-$(CONFIG_MSM_SMD_TTY) += smd_tty.o
obj-$(CONFIG_MSM_SMD_QMI) += smd_qmi.o
@@ -195,21 +206,15 @@
obj-y += subsystem_notif.o
obj-y += subsystem_restart.o
obj-y += ramdump.o
- obj-$(CONFIG_ARCH_MSM8X60) += lpass-8660.o
endif
obj-$(CONFIG_MSM_SYSMON_COMM) += sysmon.o
-obj-$(CONFIG_MSM_MODEM_8960) += modem-8960.o
-obj-$(CONFIG_MSM_MODEM_SSR_8974) += modem-ssr-8974.o
-obj-$(CONFIG_MSM_LPASS_8960) += lpass-8960.o
-obj-$(CONFIG_MSM_ADSP_SSR_8974) += adsp-8974.o
-obj-$(CONFIG_MSM_WCNSS_SSR_8960) += wcnss-ssr-8960.o
-obj-$(CONFIG_MSM_GSS_SSR_8064) += gss-8064.o
ifdef CONFIG_CPU_IDLE
obj-$(CONFIG_ARCH_APQ8064) += cpuidle.o
obj-$(CONFIG_ARCH_MSM8960) += cpuidle.o
obj-$(CONFIG_ARCH_MSM8X60) += cpuidle.o
obj-$(CONFIG_ARCH_MSM9615) += cpuidle.o
+ obj-$(CONFIG_ARCH_MSM9625) += cpuidle.o
obj-$(CONFIG_ARCH_MSM8974) += cpuidle.o
endif
@@ -252,6 +257,7 @@
obj-$(CONFIG_MACH_MSM8625_SURF) += board-msm7x27a.o board-7627a-all.o
obj-$(CONFIG_MACH_MSM8625_EVB) += board-qrd7627a.o board-7627a-all.o
obj-$(CONFIG_MACH_MSM8625_QRD7) += board-qrd7627a.o board-7627a-all.o
+obj-$(CONFIG_MACH_QRD_SKUD_PRIME) += board-qrd7627a.o board-7627a-all.o
obj-$(CONFIG_MACH_MSM8625_FFA) += board-msm7x27a.o board-7627a-all.o
obj-$(CONFIG_MACH_MSM8625_EVT) += board-msm7x27a.o board-7627a-all.o
obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o memory_topology.o
@@ -283,15 +289,17 @@
obj-$(CONFIG_MACH_MPQ8064_DTV) += board-8064-all.o board-8064-regulator.o
obj-$(CONFIG_ARCH_MSM9615) += board-9615.o devices-9615.o board-9615-regulator.o board-9615-gpiomux.o board-9615-storage.o board-9615-display.o
obj-$(CONFIG_ARCH_MSM9615) += clock-local.o clock-9615.o acpuclock-9615.o clock-rpm.o clock-pll.o
-obj-$(CONFIG_ARCH_MSM8974) += board-8974.o board-dt.o board-8974-gpiomux.o
+obj-$(CONFIG_ARCH_MSM8974) += board-8974.o board-8974-gpiomux.o
obj-$(CONFIG_ARCH_MSM8974) += acpuclock-8974.o
obj-$(CONFIG_ARCH_MSM8974) += clock-local2.o clock-pll.o clock-8974.o clock-rpm.o clock-voter.o clock-mdss-8974.o
obj-$(CONFIG_ARCH_MSM8974) += gdsc.o
obj-$(CONFIG_ARCH_MSM8974) += krait-regulator.o
obj-$(CONFIG_ARCH_MSM9625) += board-9625.o board-9625-gpiomux.o
+obj-$(CONFIG_ARCH_MSM9625) += clock-local2.o clock-pll.o clock-9625.o clock-rpm.o clock-voter.o
obj-$(CONFIG_ARCH_MSM8930) += acpuclock-8930.o acpuclock-8627.o acpuclock-8930aa.o
obj-$(CONFIG_ARCH_MPQ8092) += board-8092.o board-8092-gpiomux.o
obj-$(CONFIG_ARCH_MSM8226) += board-8226.o board-8226-gpiomux.o
+obj-$(CONFIG_ARCH_MSM8910) += board-8910.o
obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire.o board-sapphire-gpio.o
obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-keypad.o board-sapphire-panel.o
@@ -318,6 +326,7 @@
endif
ifdef CONFIG_MSM_RPM_SMD
obj-$(CONFIG_ARCH_MSM8974) += lpm_levels.o lpm_resources.o
+ obj-$(CONFIG_ARCH_MSM9625) += lpm_levels.o lpm_resources.o
endif
obj-$(CONFIG_MSM_MPM_OF) += mpm-of.o
obj-$(CONFIG_MSM_MPM) += mpm.o
@@ -349,9 +358,10 @@
obj-$(CONFIG_ARCH_MSM9625) += gpiomux-v2.o gpiomux.o
obj-$(CONFIG_ARCH_MPQ8092) += gpiomux-v2.o gpiomux.o
obj-$(CONFIG_ARCH_MSM8226) += gpiomux-v2.o gpiomux.o
+obj-$(CONFIG_ARCH_MSM8910) += gpiomux-v2.o gpiomux.o
obj-$(CONFIG_MSM_SLEEP_STATS_DEVICE) += idle_stats_device.o
-obj-$(CONFIG_MSM_DCVS) += msm_dcvs_scm.o msm_dcvs.o msm_dcvs_idle.o
+obj-$(CONFIG_MSM_DCVS) += msm_dcvs_scm.o msm_dcvs.o msm_mpdecision.o
obj-$(CONFIG_MSM_RUN_QUEUE_STATS) += msm_rq_stats.o
obj-$(CONFIG_MSM_SHOW_RESUME_IRQ) += msm_show_resume_irq.o
obj-$(CONFIG_BT_MSM_PINTEST) += btpintest.o
@@ -389,3 +399,5 @@
ifdef CONFIG_MSM_CPR
obj-$(CONFIG_DEBUG_FS) += msm_cpr-debug.o
endif
+obj-$(CONFIG_MSM_FIQ) += msm7k_fiq.o
+obj-$(CONFIG_MSM_FIQ) += msm7k_fiq_handler.o
diff --git a/arch/arm/mach-msm/Makefile.boot b/arch/arm/mach-msm/Makefile.boot
index 9234b2c..fa9ee54 100644
--- a/arch/arm/mach-msm/Makefile.boot
+++ b/arch/arm/mach-msm/Makefile.boot
@@ -65,3 +65,5 @@
# MPQ8092
zreladdr-$(CONFIG_ARCH_MPQ8092) := 0x00008000
+# MSM8910
+ zreladdr-$(CONFIG_ARCH_MSM8910) := 0x80208000
diff --git a/arch/arm/mach-msm/acpuclock-7627.c b/arch/arm/mach-msm/acpuclock-7627.c
index 5c4a923..dd27123 100644
--- a/arch/arm/mach-msm/acpuclock-7627.c
+++ b/arch/arm/mach-msm/acpuclock-7627.c
@@ -85,10 +85,12 @@
};
static struct pll_config pll4_cfg_tbl[] = {
- { 36, 1, 2 }, /* 700.8 MHz */
- { 52, 1, 2 }, /* 1008 MHz */
- { 63, 0, 1 }, /* 1209.6 MHz */
- { 73, 0, 1 }, /* 1401.6 MHz */
+ [0] = { 36, 1, 2 }, /* 700.8 MHz */
+ [1] = { 52, 1, 2 }, /* 1008 MHz */
+ [2] = { 63, 0, 1 }, /* 1209.6 MHz */
+ [3] = { 73, 0, 1 }, /* 1401.6 MHz */
+ [4] = { 60, 0, 1 }, /* 1152 MHz */
+ [5] = { 57, 1, 2 }, /* 1104 MHz */
};
struct clock_state {
@@ -266,8 +268,8 @@
static struct clkctl_acpu_speed pll0_960_pll1_196_pll2_1200_pll4_1209[] = {
{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 24576 },
{ 0, 65536, ACPU_PLL_1, 1, 3, 8192, 3, 1, 49152 },
- { 0, 98304, ACPU_PLL_1, 1, 1, 12288, 3, 2, 49152 },
- { 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 3, 98304 },
+ { 0, 98304, ACPU_PLL_1, 1, 1, 12288, 3, 1, 49152 },
+ { 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 1, 98304 },
{ 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 2, 122880 },
{ 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 3, 122880 },
{ 0, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 4, 160000 },
@@ -298,8 +300,8 @@
static struct clkctl_acpu_speed pll0_960_pll1_196_pll2_1200_pll4_1401[] = {
{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 24576 },
{ 0, 65536, ACPU_PLL_1, 1, 3, 8192, 3, 1, 49152 },
- { 0, 98304, ACPU_PLL_1, 1, 1, 12288, 3, 2, 49152 },
- { 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 3, 98304 },
+ { 0, 98304, ACPU_PLL_1, 1, 1, 12288, 3, 1, 49152 },
+ { 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 1, 98304 },
{ 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 2, 122880 },
{ 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 3, 122880 },
{ 0, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 4, 160000 },
@@ -310,7 +312,7 @@
{ 0 }
};
-/* 8625v2.0 PLL4 @ 1008MHz with GSM capable modem */
+/* 8625 PLL4 @ 1008MHz with GSM capable modem */
static struct clkctl_acpu_speed pll0_960_pll1_245_pll2_1200_pll4_1008_2p0[] = {
{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 30720 },
{ 0, 61440, ACPU_PLL_1, 1, 3, 7680, 3, 0, 61440 },
@@ -325,12 +327,12 @@
{ 0 }
};
-/* 8625v2.0 PLL4 @ 1008MHz with CDMA capable modem */
+/* 8625 PLL4 @ 1008MHz with CDMA capable modem */
static struct clkctl_acpu_speed pll0_960_pll1_196_pll2_1200_pll4_1008_2p0[] = {
{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 24576 },
{ 0, 65536, ACPU_PLL_1, 1, 3, 8192, 3, 1, 49152 },
- { 0, 98304, ACPU_PLL_1, 1, 1, 12288, 3, 2, 49152 },
- { 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 3, 98304 },
+ { 0, 98304, ACPU_PLL_1, 1, 1, 12288, 3, 1, 49152 },
+ { 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 1, 98304 },
{ 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 2, 122880 },
{ 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 3, 122880 },
{ 0, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 4, 160000 },
@@ -339,35 +341,58 @@
{ 0 }
};
-/* 8625 PLL4 @ 1152MHz with GSM capable modem */
+/* 8625 PLL4 @ 1104MHz with GSM capable modem with v2.0 plan */
+static struct clkctl_acpu_speed pll0_960_pll1_245_pll2_1200_pll4_1104[] = {
+ { 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 30720 },
+ { 1, 245760, ACPU_PLL_1, 1, 0, 30720, 3, 1, 61440 },
+ { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 2, 122880 },
+ { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 3, 122880 },
+ { 0, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 4, 160000 },
+ { 1, 700800, ACPU_PLL_4, 6, 0, 87500, 3, 4, 160000, &pll4_cfg_tbl[0]},
+ { 1, 1008000, ACPU_PLL_4, 6, 0, 126000, 3, 5, 200000, &pll4_cfg_tbl[1]},
+ { 1, 1104000, ACPU_PLL_4, 6, 0, 151200, 3, 6, 200000, &pll4_cfg_tbl[5]},
+ { 0 }
+};
+
+/* 8625 PLL4 @ 1104MHz with CDMA capable modem with v2.0 plan */
+static struct clkctl_acpu_speed pll0_960_pll1_196_pll2_1200_pll4_1104[] = {
+ { 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 24576 },
+ { 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 1, 98304 },
+ { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 2, 122880 },
+ { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 3, 122880 },
+ { 0, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 4, 160000 },
+ { 1, 700800, ACPU_PLL_4, 6, 0, 87500, 3, 4, 160000, &pll4_cfg_tbl[0]},
+ { 1, 1008000, ACPU_PLL_4, 6, 0, 126000, 3, 5, 200000, &pll4_cfg_tbl[1]},
+ { 1, 1104000, ACPU_PLL_4, 6, 0, 151200, 3, 6, 200000, &pll4_cfg_tbl[5]},
+ { 0 }
+};
+
+/* 8625 PLL4 @ 1152MHz with GSM capable modem with v2.0 plan */
static struct clkctl_acpu_speed pll0_960_pll1_245_pll2_1200_pll4_1152[] = {
{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 30720 },
- { 0, 61440, ACPU_PLL_1, 1, 3, 7680, 3, 1, 61440 },
- { 1, 122880, ACPU_PLL_1, 1, 1, 15360, 3, 2, 61440 },
- { 1, 245760, ACPU_PLL_1, 1, 0, 30720, 3, 3, 61440 },
- { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 122880 },
- { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 122880 },
- { 0, 576000, ACPU_PLL_4, 6, 1, 72000, 3, 6, 160000 },
- { 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 160000 },
- { 1, 1152000, ACPU_PLL_4, 6, 0, 144000, 3, 7, 200000},
+ { 1, 245760, ACPU_PLL_1, 1, 0, 30720, 3, 1, 61440 },
+ { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 2, 122880 },
+ { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 3, 122880 },
+ { 0, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 4, 160000 },
+ { 1, 700800, ACPU_PLL_4, 6, 0, 87500, 3, 4, 160000, &pll4_cfg_tbl[0]},
+ { 1, 1008000, ACPU_PLL_4, 6, 0, 126000, 3, 5, 200000, &pll4_cfg_tbl[1]},
+ { 1, 1152000, ACPU_PLL_4, 6, 0, 151200, 3, 6, 200000, &pll4_cfg_tbl[4]},
{ 0 }
};
-/* 8625 PLL4 @ 1115MHz with CDMA capable modem */
+/* 8625 PLL4 @ 1115MHz with CDMA capable modem with v2.0 plan */
static struct clkctl_acpu_speed pll0_960_pll1_196_pll2_1200_pll4_1152[] = {
{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 24576 },
- { 0, 65536, ACPU_PLL_1, 1, 3, 8192, 3, 1, 49152 },
- { 1, 98304, ACPU_PLL_1, 1, 1, 12288, 3, 2, 49152 },
- { 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 3, 98304 },
- { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 122880 },
- { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 122880 },
- { 0, 576000, ACPU_PLL_4, 6, 1, 72000, 3, 6, 160000 },
- { 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 160000 },
- { 1, 1152000, ACPU_PLL_4, 6, 0, 144000, 3, 7, 200000},
+ { 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 1, 98304 },
+ { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 2, 122880 },
+ { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 3, 122880 },
+ { 0, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 4, 160000 },
+ { 1, 700800, ACPU_PLL_4, 6, 0, 87500, 3, 4, 160000, &pll4_cfg_tbl[0]},
+ { 1, 1008000, ACPU_PLL_4, 6, 0, 126000, 3, 5, 200000, &pll4_cfg_tbl[1]},
+ { 1, 1152000, ACPU_PLL_4, 6, 0, 151200, 3, 6, 200000, &pll4_cfg_tbl[4]},
{ 0 }
};
-
/* 7625a PLL2 @ 1200MHz with GSM capable modem */
static struct clkctl_acpu_speed pll0_960_pll1_245_pll2_1200_25a[] = {
{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 30720 },
@@ -486,6 +511,8 @@
PLL_CONFIG(960, 196, 1200, 1209),
PLL_CONFIG(960, 245, 1200, 1152),
PLL_CONFIG(960, 196, 1200, 1152),
+ PLL_CONFIG(960, 245, 1200, 1104),
+ PLL_CONFIG(960, 196, 1200, 1104),
PLL_CONFIG(960, 245, 1200, 1401),
PLL_CONFIG(960, 196, 1200, 1401),
{ 0, 0, 0, 0, 0 }
@@ -754,7 +781,7 @@
acpuclk_set_div(tgt_s);
drv_state.current_speed = tgt_s;
/* Re-adjust lpj for the new clock speed. */
- update_jiffies(cpu, cur_s->lpj);
+ update_jiffies(cpu, tgt_s->lpj);
/* Disable the backup PLL */
if ((delta > drv_state.max_speed_delta_khz)
@@ -987,14 +1014,10 @@
/*
* 1008Mhz table selection based on the Lvalue of the PLL
- * is conflicting with the 7627AA and 8625 v1.0 1GHz parts
- * since v2.0 8625 chips are using different clock plan based
- * reprogramming method.
+ * is conflicting with the 7627AA 1GHz parts since 8625 chips
+ * are using different clock plan based reprogramming method.
*/
- if (cpu_is_msm8625() &&
- (SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 2) &&
- pll_mhz[ACPU_PLL_4] == 1008) {
-
+ if (cpu_is_msm8625() && pll_mhz[ACPU_PLL_4] == 1008) {
if (pll_mhz[ACPU_PLL_2] == 245)
acpu_freq_tbl =
pll0_960_pll1_245_pll2_1200_pll4_1008_2p0;
diff --git a/arch/arm/mach-msm/acpuclock-8064.c b/arch/arm/mach-msm/acpuclock-8064.c
index d0de62b..cda952f 100644
--- a/arch/arm/mach-msm/acpuclock-8064.c
+++ b/arch/arm/mach-msm/acpuclock-8064.c
@@ -18,6 +18,7 @@
#include <mach/msm_bus_board.h>
#include <mach/msm_bus.h>
+#include "mach/socinfo.h"
#include "acpuclock.h"
#include "acpuclock-krait.h"
@@ -94,6 +95,10 @@
},
};
+/*
+ * The correct maximum rate for 8064ab in 600 MHZ.
+ * We rely on the RPM rounding requests up here.
+*/
static struct msm_bus_paths bw_level_tbl[] __initdata = {
[0] = BW_MBPS(640), /* At least 80 MHz on bus. */
[1] = BW_MBPS(1064), /* At least 133 MHz on bus. */
@@ -110,8 +115,8 @@
.name = "acpuclk-8064",
};
-static struct l2_level l2_freq_tbl[] __initdata __initdata = {
- [0] = { { 384000, PLL_8, 0, 0x00 }, 1050000, 1050000, 1 },
+static struct l2_level l2_freq_tbl[] __initdata = {
+ [0] = { { 384000, PLL_8, 0, 0x00 }, 950000, 1050000, 1 },
[1] = { { 432000, HFPLL, 2, 0x20 }, 1050000, 1050000, 2 },
[2] = { { 486000, HFPLL, 2, 0x24 }, 1050000, 1050000, 2 },
[3] = { { 540000, HFPLL, 2, 0x28 }, 1050000, 1050000, 2 },
@@ -130,7 +135,7 @@
{ }
};
-static struct acpu_level acpu_freq_tbl_slow[] __initdata = {
+static struct acpu_level tbl_slow[] __initdata = {
{ 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 950000 },
{ 0, { 432000, HFPLL, 2, 0x20 }, L2(6), 975000 },
{ 1, { 486000, HFPLL, 2, 0x24 }, L2(6), 975000 },
@@ -156,7 +161,7 @@
{ 0, { 0 } }
};
-static struct acpu_level acpu_freq_tbl_nom[] __initdata = {
+static struct acpu_level tbl_nom[] __initdata = {
{ 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 900000 },
{ 0, { 432000, HFPLL, 2, 0x20 }, L2(6), 925000 },
{ 1, { 486000, HFPLL, 2, 0x24 }, L2(6), 925000 },
@@ -182,7 +187,7 @@
{ 0, { 0 } }
};
-static struct acpu_level acpu_freq_tbl_fast[] __initdata = {
+static struct acpu_level tbl_fast[] __initdata = {
{ 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 850000 },
{ 0, { 432000, HFPLL, 2, 0x20 }, L2(6), 875000 },
{ 1, { 486000, HFPLL, 2, 0x24 }, L2(6), 875000 },
@@ -208,12 +213,179 @@
{ 0, { 0 } }
};
-static struct pvs_table pvs_tables[NUM_PVS] __initdata = {
-[PVS_SLOW] = { acpu_freq_tbl_slow, sizeof(acpu_freq_tbl_slow), 0 },
-[PVS_NOMINAL] = { acpu_freq_tbl_nom, sizeof(acpu_freq_tbl_nom), 25000 },
-[PVS_FAST] = { acpu_freq_tbl_fast, sizeof(acpu_freq_tbl_fast), 25000 },
-/* TODO: update the faster table when data is available */
-[PVS_FASTER] = { acpu_freq_tbl_fast, sizeof(acpu_freq_tbl_fast), 25000 },
+static struct acpu_level tbl_PVS0_1700MHz[] __initdata = {
+ { 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 950000 },
+ { 1, { 486000, HFPLL, 2, 0x24 }, L2(6), 975000 },
+ { 1, { 594000, HFPLL, 1, 0x16 }, L2(6), 1000000 },
+ { 1, { 702000, HFPLL, 1, 0x1A }, L2(6), 1025000 },
+ { 1, { 810000, HFPLL, 1, 0x1E }, L2(6), 1075000 },
+ { 1, { 918000, HFPLL, 1, 0x22 }, L2(6), 1100000 },
+ { 1, { 1026000, HFPLL, 1, 0x26 }, L2(6), 1125000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 1175000 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1200000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1225000 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1237500 },
+ { 1, { 1512000, HFPLL, 1, 0x38 }, L2(15), 1250000 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1250000 },
+ { 1, { 1620000, HFPLL, 1, 0x3C }, L2(15), 1250000 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1250000 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS0_2000MHz[] __initdata = {
+ { 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 900000 },
+ { 1, { 486000, HFPLL, 2, 0x24 }, L2(6), 900000 },
+ { 1, { 594000, HFPLL, 1, 0x16 }, L2(6), 900000 },
+ { 1, { 702000, HFPLL, 1, 0x1A }, L2(6), 900000 },
+ { 1, { 810000, HFPLL, 1, 0x1E }, L2(6), 912500 },
+ { 1, { 918000, HFPLL, 1, 0x22 }, L2(6), 962500 },
+ { 1, { 1026000, HFPLL, 1, 0x26 }, L2(6), 987500 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 1012500 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1025000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1075000 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1112500 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1150000 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1200000 },
+ { 1, { 1782000, HFPLL, 1, 0x42 }, L2(15), 1262500 },
+ { 1, { 1890000, HFPLL, 1, 0x46 }, L2(15), 1300000 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS1_2000MHz[] __initdata = {
+ { 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 900000 },
+ { 1, { 486000, HFPLL, 2, 0x24 }, L2(6), 900000 },
+ { 1, { 594000, HFPLL, 1, 0x16 }, L2(6), 900000 },
+ { 1, { 702000, HFPLL, 1, 0x1A }, L2(6), 900000 },
+ { 1, { 810000, HFPLL, 1, 0x1E }, L2(6), 900000 },
+ { 1, { 918000, HFPLL, 1, 0x22 }, L2(6), 962500 },
+ { 1, { 1026000, HFPLL, 1, 0x26 }, L2(6), 987500 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 1000000 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1012500 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1062500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1087500 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1125000 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1187500 },
+ { 1, { 1782000, HFPLL, 1, 0x42 }, L2(15), 1237500 },
+ { 1, { 1890000, HFPLL, 1, 0x46 }, L2(15), 1275000 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS2_2000MHz[] __initdata = {
+ { 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 900000 },
+ { 1, { 486000, HFPLL, 2, 0x24 }, L2(6), 900000 },
+ { 1, { 594000, HFPLL, 1, 0x16 }, L2(6), 900000 },
+ { 1, { 702000, HFPLL, 1, 0x1A }, L2(6), 900000 },
+ { 1, { 810000, HFPLL, 1, 0x1E }, L2(6), 900000 },
+ { 1, { 918000, HFPLL, 1, 0x22 }, L2(6), 950000 },
+ { 1, { 1026000, HFPLL, 1, 0x26 }, L2(6), 975000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 987500 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1000000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1050000 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1075000 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1112500 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1162500 },
+ { 1, { 1782000, HFPLL, 1, 0x42 }, L2(15), 1212500 },
+ { 1, { 1890000, HFPLL, 1, 0x46 }, L2(15), 1250000 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS3_2000MHz[] __initdata = {
+ { 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 900000 },
+ { 1, { 486000, HFPLL, 2, 0x24 }, L2(6), 900000 },
+ { 1, { 594000, HFPLL, 1, 0x16 }, L2(6), 900000 },
+ { 1, { 702000, HFPLL, 1, 0x1A }, L2(6), 900000 },
+ { 1, { 810000, HFPLL, 1, 0x1E }, L2(6), 900000 },
+ { 1, { 918000, HFPLL, 1, 0x22 }, L2(6), 925000 },
+ { 1, { 1026000, HFPLL, 1, 0x26 }, L2(6), 950000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 962500 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 975000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1012500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1037500 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1075000 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1112500 },
+ { 1, { 1782000, HFPLL, 1, 0x42 }, L2(15), 1162500 },
+ { 1, { 1890000, HFPLL, 1, 0x46 }, L2(15), 1200000 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS4_2000MHz[] __initdata = {
+ { 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 900000 },
+ { 1, { 486000, HFPLL, 2, 0x24 }, L2(6), 900000 },
+ { 1, { 594000, HFPLL, 1, 0x16 }, L2(6), 900000 },
+ { 1, { 702000, HFPLL, 1, 0x1A }, L2(6), 900000 },
+ { 1, { 810000, HFPLL, 1, 0x1E }, L2(6), 900000 },
+ { 1, { 918000, HFPLL, 1, 0x22 }, L2(6), 900000 },
+ { 1, { 1026000, HFPLL, 1, 0x26 }, L2(6), 925000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 937500 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 950000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 975000 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1000000 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1037500 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1062500 },
+ { 1, { 1782000, HFPLL, 1, 0x42 }, L2(15), 1112500 },
+ { 1, { 1890000, HFPLL, 1, 0x46 }, L2(15), 1150000 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS5_2000MHz[] __initdata = {
+ { 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 900000 },
+ { 1, { 486000, HFPLL, 2, 0x24 }, L2(6), 900000 },
+ { 1, { 594000, HFPLL, 1, 0x16 }, L2(6), 900000 },
+ { 1, { 702000, HFPLL, 1, 0x1A }, L2(6), 900000 },
+ { 1, { 810000, HFPLL, 1, 0x1E }, L2(6), 900000 },
+ { 1, { 918000, HFPLL, 1, 0x22 }, L2(6), 900000 },
+ { 1, { 1026000, HFPLL, 1, 0x26 }, L2(6), 925000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 937500 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 950000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 962500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 987500 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1012500 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1037500 },
+ { 1, { 1782000, HFPLL, 1, 0x42 }, L2(15), 1087500 },
+ { 1, { 1890000, HFPLL, 1, 0x46 }, L2(15), 1125000 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS6_2000MHz[] __initdata = {
+ { 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 900000 },
+ { 1, { 486000, HFPLL, 2, 0x24 }, L2(6), 900000 },
+ { 1, { 594000, HFPLL, 1, 0x16 }, L2(6), 900000 },
+ { 1, { 702000, HFPLL, 1, 0x1A }, L2(6), 900000 },
+ { 1, { 810000, HFPLL, 1, 0x1E }, L2(6), 900000 },
+ { 1, { 918000, HFPLL, 1, 0x22 }, L2(6), 900000 },
+ { 1, { 1026000, HFPLL, 1, 0x26 }, L2(6), 925000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 937500 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 950000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 962500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 975000 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1000000 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1025000 },
+ { 1, { 1782000, HFPLL, 1, 0x42 }, L2(15), 1062500 },
+ { 1, { 1890000, HFPLL, 1, 0x46 }, L2(15), 1100000 },
+ { 0, { 0 } }
+};
+
+static struct pvs_table pvs_tables[NUM_SPEED_BINS][NUM_PVS] __initdata = {
+ [0][PVS_SLOW] = {tbl_slow, sizeof(tbl_slow), 0 },
+ [0][PVS_NOMINAL] = {tbl_nom, sizeof(tbl_nom), 25000 },
+ [0][PVS_FAST] = {tbl_fast, sizeof(tbl_fast), 25000 },
+ [0][PVS_FASTER] = {tbl_fast, sizeof(tbl_fast), 25000 },
+
+ [1][0] = { tbl_PVS0_1700MHz, sizeof(tbl_PVS0_1700MHz), 0 },
+ [1][1] = { tbl_PVS0_1700MHz, sizeof(tbl_PVS0_1700MHz), 0 },
+ [1][2] = { tbl_PVS0_1700MHz, sizeof(tbl_PVS0_1700MHz), 0 },
+ [1][3] = { tbl_PVS0_1700MHz, sizeof(tbl_PVS0_1700MHz), 0 },
+ [1][4] = { tbl_PVS0_1700MHz, sizeof(tbl_PVS0_1700MHz), 0 },
+ [1][5] = { tbl_PVS0_1700MHz, sizeof(tbl_PVS0_1700MHz), 0 },
+ [1][6] = { tbl_PVS0_1700MHz, sizeof(tbl_PVS0_1700MHz), 0 },
+
+ [2][0] = { tbl_PVS0_2000MHz, sizeof(tbl_PVS0_2000MHz), 0 },
+ [2][1] = { tbl_PVS1_2000MHz, sizeof(tbl_PVS1_2000MHz), 0 },
+ [2][2] = { tbl_PVS2_2000MHz, sizeof(tbl_PVS2_2000MHz), 0 },
+ [2][3] = { tbl_PVS3_2000MHz, sizeof(tbl_PVS3_2000MHz), 0 },
+ [2][4] = { tbl_PVS4_2000MHz, sizeof(tbl_PVS4_2000MHz), 0 },
+ [2][5] = { tbl_PVS5_2000MHz, sizeof(tbl_PVS5_2000MHz), 0 },
+ [2][6] = { tbl_PVS6_2000MHz, sizeof(tbl_PVS6_2000MHz), 0 },
};
static struct acpuclk_krait_params acpuclk_8064_params __initdata = {
@@ -230,6 +402,12 @@
static int __init acpuclk_8064_probe(struct platform_device *pdev)
{
+ if (cpu_is_apq8064ab() ||
+ SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 2) {
+ acpuclk_8064_params.hfpll_data->low_vdd_l_max = 37;
+ acpuclk_8064_params.hfpll_data->nom_vdd_l_max = 74;
+ }
+
return acpuclk_krait_init(&pdev->dev, &acpuclk_8064_params);
}
diff --git a/arch/arm/mach-msm/acpuclock-8627.c b/arch/arm/mach-msm/acpuclock-8627.c
index da49656..ac29cac 100644
--- a/arch/arm/mach-msm/acpuclock-8627.c
+++ b/arch/arm/mach-msm/acpuclock-8627.c
@@ -127,10 +127,10 @@
{ 0, { 0 } }
};
-static struct pvs_table pvs_tables[NUM_PVS] __initdata = {
- [PVS_SLOW] = { acpu_freq_tbl, sizeof(acpu_freq_tbl), 0 },
- [PVS_NOMINAL] = { acpu_freq_tbl, sizeof(acpu_freq_tbl), 25000 },
- [PVS_FAST] = { acpu_freq_tbl, sizeof(acpu_freq_tbl), 25000 },
+static struct pvs_table pvs_tables[NUM_SPEED_BINS][NUM_PVS] __initdata = {
+ [0][PVS_SLOW] = { acpu_freq_tbl, sizeof(acpu_freq_tbl), 0 },
+ [0][PVS_NOMINAL] = { acpu_freq_tbl, sizeof(acpu_freq_tbl), 25000 },
+ [0][PVS_FAST] = { acpu_freq_tbl, sizeof(acpu_freq_tbl), 25000 },
};
static struct acpuclk_krait_params acpuclk_8627_params __initdata = {
diff --git a/arch/arm/mach-msm/acpuclock-8930.c b/arch/arm/mach-msm/acpuclock-8930.c
index b4f2a1e..e46599a 100644
--- a/arch/arm/mach-msm/acpuclock-8930.c
+++ b/arch/arm/mach-msm/acpuclock-8930.c
@@ -213,10 +213,10 @@
{ 0, { 0 } }
};
-static struct pvs_table pvs_tables[NUM_PVS] __initdata = {
-[PVS_SLOW] = { acpu_freq_tbl_slow, sizeof(acpu_freq_tbl_slow), 0 },
-[PVS_NOMINAL] = { acpu_freq_tbl_nom, sizeof(acpu_freq_tbl_nom), 25000 },
-[PVS_FAST] = { acpu_freq_tbl_fast, sizeof(acpu_freq_tbl_fast), 25000 },
+static struct pvs_table pvs_tables[NUM_SPEED_BINS][NUM_PVS] __initdata = {
+[0][PVS_SLOW] = { acpu_freq_tbl_slow, sizeof(acpu_freq_tbl_slow), 0 },
+[0][PVS_NOMINAL] = { acpu_freq_tbl_nom, sizeof(acpu_freq_tbl_nom), 25000 },
+[0][PVS_FAST] = { acpu_freq_tbl_fast, sizeof(acpu_freq_tbl_fast), 25000 },
};
static struct acpuclk_krait_params acpuclk_8930_params __initdata = {
diff --git a/arch/arm/mach-msm/acpuclock-8930aa.c b/arch/arm/mach-msm/acpuclock-8930aa.c
index bcb00ea..9d2b6fc 100644
--- a/arch/arm/mach-msm/acpuclock-8930aa.c
+++ b/arch/arm/mach-msm/acpuclock-8930aa.c
@@ -189,10 +189,10 @@
{ 0, { 0 } }
};
-static struct pvs_table pvs_tables[NUM_PVS] __initdata = {
-[PVS_SLOW] = { acpu_freq_tbl_slow, sizeof(acpu_freq_tbl_slow), 0 },
-[PVS_NOMINAL] = { acpu_freq_tbl_nom, sizeof(acpu_freq_tbl_nom), 25000 },
-[PVS_FAST] = { acpu_freq_tbl_fast, sizeof(acpu_freq_tbl_fast), 25000 },
+static struct pvs_table pvs_tables[NUM_SPEED_BINS][NUM_PVS] __initdata = {
+[0][PVS_SLOW] = { acpu_freq_tbl_slow, sizeof(acpu_freq_tbl_slow), 0 },
+[0][PVS_NOMINAL] = { acpu_freq_tbl_nom, sizeof(acpu_freq_tbl_nom), 25000 },
+[0][PVS_FAST] = { acpu_freq_tbl_fast, sizeof(acpu_freq_tbl_fast), 25000 },
};
static struct acpuclk_krait_params acpuclk_8930aa_params __initdata = {
diff --git a/arch/arm/mach-msm/acpuclock-8960.c b/arch/arm/mach-msm/acpuclock-8960.c
index cf6a6c2..d7d3edd 100644
--- a/arch/arm/mach-msm/acpuclock-8960.c
+++ b/arch/arm/mach-msm/acpuclock-8960.c
@@ -195,10 +195,10 @@
{ 0, { 0 } }
};
-static struct pvs_table pvs_tables[NUM_PVS] __initdata = {
-[PVS_SLOW] = { acpu_freq_tbl_slow, sizeof(acpu_freq_tbl_slow), 0 },
-[PVS_NOMINAL] = { acpu_freq_tbl_nom, sizeof(acpu_freq_tbl_nom), 25000 },
-[PVS_FAST] = { acpu_freq_tbl_fast, sizeof(acpu_freq_tbl_fast), 25000 },
+static struct pvs_table pvs_tables[NUM_SPEED_BINS][NUM_PVS] __initdata = {
+[0][PVS_SLOW] = { acpu_freq_tbl_slow, sizeof(acpu_freq_tbl_slow), 0 },
+[0][PVS_NOMINAL] = { acpu_freq_tbl_nom, sizeof(acpu_freq_tbl_nom), 25000 },
+[0][PVS_FAST] = { acpu_freq_tbl_fast, sizeof(acpu_freq_tbl_fast), 25000 },
};
static struct acpuclk_krait_params acpuclk_8960_params __initdata = {
diff --git a/arch/arm/mach-msm/acpuclock-8960ab.c b/arch/arm/mach-msm/acpuclock-8960ab.c
index 91ccd37..ae1cd7b 100644
--- a/arch/arm/mach-msm/acpuclock-8960ab.c
+++ b/arch/arm/mach-msm/acpuclock-8960ab.c
@@ -135,10 +135,10 @@
{ 0, { 0 } }
};
-static struct pvs_table pvs_tables[NUM_PVS] __initdata = {
-[PVS_SLOW] = { acpu_freq_tbl_slow, sizeof(acpu_freq_tbl_slow), 0 },
-[PVS_NOMINAL] = { acpu_freq_tbl_slow, sizeof(acpu_freq_tbl_slow), 0 },
-[PVS_FAST] = { acpu_freq_tbl_slow, sizeof(acpu_freq_tbl_slow), 0 },
+static struct pvs_table pvs_tables[NUM_SPEED_BINS][NUM_PVS] __initdata = {
+[0][PVS_SLOW] = { acpu_freq_tbl_slow, sizeof(acpu_freq_tbl_slow), 0 },
+[0][PVS_NOMINAL] = { acpu_freq_tbl_slow, sizeof(acpu_freq_tbl_slow), 0 },
+[0][PVS_FAST] = { acpu_freq_tbl_slow, sizeof(acpu_freq_tbl_slow), 0 },
};
static struct acpuclk_krait_params acpuclk_8960ab_params __initdata = {
diff --git a/arch/arm/mach-msm/acpuclock-8974.c b/arch/arm/mach-msm/acpuclock-8974.c
index 4a755bd..098f854 100644
--- a/arch/arm/mach-msm/acpuclock-8974.c
+++ b/arch/arm/mach-msm/acpuclock-8974.c
@@ -170,10 +170,10 @@
{ 0, { 0 } }
};
-static struct pvs_table pvs_tables[NUM_PVS] __initdata = {
- [PVS_SLOW] = { acpu_freq_tbl, sizeof(acpu_freq_tbl) },
- [PVS_NOMINAL] = { acpu_freq_tbl, sizeof(acpu_freq_tbl) },
- [PVS_FAST] = { acpu_freq_tbl, sizeof(acpu_freq_tbl) },
+static struct pvs_table pvs_tables[NUM_SPEED_BINS][NUM_PVS] __initdata = {
+ [0][PVS_SLOW] = { acpu_freq_tbl, sizeof(acpu_freq_tbl) },
+ [0][PVS_NOMINAL] = { acpu_freq_tbl, sizeof(acpu_freq_tbl) },
+ [0][PVS_FAST] = { acpu_freq_tbl, sizeof(acpu_freq_tbl) },
};
static struct acpuclk_krait_params acpuclk_8974_params __initdata = {
diff --git a/arch/arm/mach-msm/acpuclock-krait-debug.c b/arch/arm/mach-msm/acpuclock-krait-debug.c
new file mode 100644
index 0000000..0ab70b4
--- /dev/null
+++ b/arch/arm/mach-msm/acpuclock-krait-debug.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/cpu.h>
+#include <linux/smp.h>
+
+#include <mach/msm_bus.h>
+#include <mach/msm-krait-l2-accessors.h>
+
+#include "acpuclock-krait.h"
+
+static struct drv_data *drv;
+static DEFINE_MUTEX(debug_lock);
+
+struct acg_action {
+ bool set;
+ bool enable;
+};
+static int l2_acg_en_val[MAX_SCALABLES];
+static struct dentry *base_dir;
+static struct dentry *sc_dir[MAX_SCALABLES];
+
+static void cpu_action(void *info)
+{
+ struct acg_action *action = info;
+
+ u32 val;
+ asm volatile ("mrc p15, 7, %[cpmr0], c15, c0, 5\n\t"
+ : [cpmr0]"=r" (val));
+ if (action->set) {
+ if (action->enable)
+ val &= ~BIT(0);
+ else
+ val |= BIT(0);
+ asm volatile ("mcr p15, 7, %[l2cpdr], c15, c0, 5\n\t"
+ : : [l2cpdr]"r" (val));
+ } else {
+ action->enable = !(val & BIT(0));
+ }
+}
+
+/* Disable auto clock-gating for a scalable. */
+static void disable_acg(int sc_id)
+{
+ u32 regval;
+
+ if (sc_id == L2) {
+ regval = get_l2_indirect_reg(drv->scalable[sc_id].l2cpmr_iaddr);
+ l2_acg_en_val[sc_id] = regval & (0x3 << 10);
+ regval |= (0x3 << 10);
+ set_l2_indirect_reg(drv->scalable[sc_id].l2cpmr_iaddr, regval);
+ } else {
+ struct acg_action action = { .set = true, .enable = false };
+ smp_call_function_single(sc_id, cpu_action, &action, 1);
+ }
+}
+
+/* Enable auto clock-gating for a scalable. */
+static void enable_acg(int sc_id)
+{
+ u32 regval;
+
+ if (sc_id == L2) {
+ regval = get_l2_indirect_reg(drv->scalable[sc_id].l2cpmr_iaddr);
+ regval &= ~(0x3 << 10);
+ regval |= l2_acg_en_val[sc_id];
+ set_l2_indirect_reg(drv->scalable[sc_id].l2cpmr_iaddr, regval);
+ } else {
+ struct acg_action action = { .set = true, .enable = true };
+ smp_call_function_single(sc_id, cpu_action, &action, 1);
+ }
+}
+
+/* Check if auto clock-gating for a scalable. */
+static bool acg_is_enabled(int sc_id)
+{
+ u32 regval;
+
+ if (sc_id == L2) {
+ regval = get_l2_indirect_reg(drv->scalable[sc_id].l2cpmr_iaddr);
+ return ((regval >> 10) & 0x3) != 0x3;
+ } else {
+ struct acg_action action = { .set = false };
+ smp_call_function_single(sc_id, cpu_action, &action, 1);
+ return action.enable;
+ }
+}
+
+/* Enable/Disable auto clock gating. */
+static int acg_set(void *data, u64 val)
+{
+ int ret = 0;
+ int sc_id = (int)data;
+
+ mutex_lock(&debug_lock);
+ get_online_cpus();
+ if (!sc_dir[sc_id]) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if (val == 0 && acg_is_enabled(sc_id))
+ disable_acg(sc_id);
+ else if (val == 1)
+ enable_acg(sc_id);
+out:
+ put_online_cpus();
+ mutex_unlock(&debug_lock);
+
+ return ret;
+}
+
+/* Get auto clock-gating state. */
+static int acg_get(void *data, u64 *val)
+{
+ int ret = 0;
+ int sc_id = (int)data;
+
+ mutex_lock(&debug_lock);
+ get_online_cpus();
+ if (!sc_dir[sc_id]) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ *val = acg_is_enabled(sc_id);
+out:
+ put_online_cpus();
+ mutex_unlock(&debug_lock);
+
+ return ret;
+}
+DEFINE_SIMPLE_ATTRIBUTE(acgd_fops, acg_get, acg_set, "%lld\n");
+
+/* Get the rate */
+static int rate_get(void *data, u64 *val)
+{
+ int sc_id = (int)data;
+ *val = drv->scalable[sc_id].cur_speed->khz;
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(rate_fops, rate_get, NULL, "%lld\n");
+
+/* Get the HFPLL's L-value. */
+static int hfpll_l_get(void *data, u64 *val)
+{
+ int sc_id = (int)data;
+ *val = drv->scalable[sc_id].cur_speed->pll_l_val;
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(hfpll_l_fops, hfpll_l_get, NULL, "%lld\n");
+
+/* Get the L2 rate vote. */
+static int l2_vote_get(void *data, u64 *val)
+{
+ int level, sc_id = (int)data;
+
+ level = drv->scalable[sc_id].l2_vote;
+ *val = drv->l2_freq_tbl[level].speed.khz;
+
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(l2_vote_fops, l2_vote_get, NULL, "%lld\n");
+
+/* Get the bandwidth vote. */
+static int bw_vote_get(void *data, u64 *val)
+{
+ struct l2_level *l;
+
+ l = container_of(drv->scalable[L2].cur_speed,
+ struct l2_level, speed);
+ *val = drv->bus_scale->usecase[l->bw_level].vectors->ib;
+
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(bw_vote_fops, bw_vote_get, NULL, "%lld\n");
+
+/* Get the name of the currently-selected clock source. */
+static int src_name_show(struct seq_file *m, void *unused)
+{
+ const char *const src_names[NUM_SRC_ID] = {
+ [PLL_0] = "PLL0",
+ [HFPLL] = "HFPLL",
+ [PLL_8] = "PLL8",
+ };
+ int src, sc_id = (int)m->private;
+
+ src = drv->scalable[sc_id].cur_speed->src;
+ if (src > ARRAY_SIZE(src_names))
+ return -EINVAL;
+
+ seq_printf(m, "%s\n", src_names[src]);
+
+ return 0;
+}
+
+static int src_name_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, src_name_show, inode->i_private);
+}
+
+static const struct file_operations src_name_fops = {
+ .open = src_name_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+/* Get speed_bin ID */
+static int speed_bin_get(void *data, u64 *val)
+{
+ *val = drv->speed_bin;
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(speed_bin_fops, speed_bin_get, NULL, "%lld\n");
+
+/* Get pvs_bin ID */
+static int pvs_bin_get(void *data, u64 *val)
+{
+ *val = drv->pvs_bin;
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(pvs_bin_fops, pvs_bin_get, NULL, "%lld\n");
+
+/* Get boost_uv */
+static int boost_get(void *data, u64 *val)
+{
+ *val = drv->boost_uv;
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(boost_fops, boost_get, NULL, "%lld\n");
+
+static void __cpuinit add_scalable_dir(int sc_id)
+{
+ char sc_name[8];
+
+ if (sc_id == L2)
+ snprintf(sc_name, sizeof(sc_name), "l2");
+ else
+ snprintf(sc_name, sizeof(sc_name), "cpu%d", sc_id);
+
+ sc_dir[sc_id] = debugfs_create_dir(sc_name, base_dir);
+ if (!sc_dir[sc_id])
+ return;
+
+ debugfs_create_file("auto_gating", S_IRUGO | S_IWUSR,
+ sc_dir[sc_id], (void *)sc_id, &acgd_fops);
+
+ debugfs_create_file("rate", S_IRUGO,
+ sc_dir[sc_id], (void *)sc_id, &rate_fops);
+
+ debugfs_create_file("hfpll_l", S_IRUGO,
+ sc_dir[sc_id], (void *)sc_id, &hfpll_l_fops);
+
+ debugfs_create_file("src", S_IRUGO,
+ sc_dir[sc_id], (void *)sc_id, &src_name_fops);
+
+ if (sc_id == L2)
+ debugfs_create_file("bw_ib_vote", S_IRUGO,
+ sc_dir[sc_id], (void *)sc_id, &bw_vote_fops);
+ else
+ debugfs_create_file("l2_vote", S_IRUGO,
+ sc_dir[sc_id], (void *)sc_id, &l2_vote_fops);
+}
+
+static void __cpuinit remove_scalable_dir(int sc_id)
+{
+ debugfs_remove_recursive(sc_dir[sc_id]);
+ sc_dir[sc_id] = NULL;
+}
+
+static int __cpuinit debug_cpu_callback(struct notifier_block *nfb,
+ unsigned long action, void *hcpu)
+{
+ int cpu = (int)hcpu;
+
+ switch (action & ~CPU_TASKS_FROZEN) {
+ case CPU_DOWN_FAILED:
+ case CPU_UP_PREPARE:
+ add_scalable_dir(cpu);
+ break;
+ case CPU_UP_CANCELED:
+ case CPU_DOWN_PREPARE:
+ remove_scalable_dir(cpu);
+ break;
+ default:
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block __cpuinitdata debug_cpu_notifier = {
+ .notifier_call = debug_cpu_callback,
+};
+
+void __init acpuclk_krait_debug_init(struct drv_data *drv_data)
+{
+ int cpu;
+ drv = drv_data;
+
+ base_dir = debugfs_create_dir("acpuclk", NULL);
+ if (!base_dir)
+ return;
+
+ debugfs_create_file("speed_bin", S_IRUGO, base_dir, NULL,
+ &speed_bin_fops);
+ debugfs_create_file("pvs_bin", S_IRUGO, base_dir, NULL, &pvs_bin_fops);
+ debugfs_create_file("boost_uv", S_IRUGO, base_dir, NULL, &boost_fops);
+
+ for_each_online_cpu(cpu)
+ add_scalable_dir(cpu);
+ add_scalable_dir(L2);
+
+ register_hotcpu_notifier(&debug_cpu_notifier);
+}
diff --git a/arch/arm/mach-msm/acpuclock-krait.c b/arch/arm/mach-msm/acpuclock-krait.c
index 79c01b2..d38396d 100644
--- a/arch/arm/mach-msm/acpuclock-krait.c
+++ b/arch/arm/mach-msm/acpuclock-krait.c
@@ -48,16 +48,7 @@
static DEFINE_MUTEX(driver_lock);
static DEFINE_SPINLOCK(l2_lock);
-static struct drv_data {
- struct acpu_level *acpu_freq_tbl;
- const struct l2_level *l2_freq_tbl;
- struct scalable *scalable;
- struct hfpll_data *hfpll_data;
- u32 bus_perf_client;
- struct msm_bus_scale_pdata *bus_scale;
- int boost_uv;
- struct device *dev;
-} drv;
+static struct drv_data drv;
static unsigned long acpuclk_krait_get_rate(int cpu)
{
@@ -946,58 +937,68 @@
}
}
-static int __init select_freq_plan(u32 pte_efuse_phys)
+static int __init get_speed_bin(u32 pte_efuse)
+{
+ uint32_t speed_bin;
+
+ speed_bin = pte_efuse & 0xF;
+ if (speed_bin == 0xF)
+ speed_bin = (pte_efuse >> 4) & 0xF;
+
+ if (speed_bin == 0xF) {
+ speed_bin = 0;
+ dev_warn(drv.dev, "SPEED BIN: Defaulting to %d\n", speed_bin);
+ } else {
+ dev_info(drv.dev, "SPEED BIN: %d\n", speed_bin);
+ }
+
+ return speed_bin;
+}
+
+static int __init get_pvs_bin(u32 pte_efuse)
+{
+ uint32_t pvs_bin;
+
+ pvs_bin = (pte_efuse >> 10) & 0x7;
+ if (pvs_bin == 0x7)
+ pvs_bin = (pte_efuse >> 13) & 0x7;
+
+ if (pvs_bin == 0x7) {
+ pvs_bin = 0;
+ dev_warn(drv.dev, "ACPU PVS: Defaulting to %d\n", pvs_bin);
+ } else {
+ dev_info(drv.dev, "ACPU PVS: %d\n", pvs_bin);
+ }
+
+ return pvs_bin;
+}
+
+static struct pvs_table * __init select_freq_plan(u32 pte_efuse_phys,
+ struct pvs_table (*pvs_tables)[NUM_PVS])
{
void __iomem *pte_efuse;
- u32 pte_efuse_val, pvs, tbl_idx;
- char *pvs_names[] = { "Slow", "Nominal", "Fast", "Faster", "Unknown" };
+ u32 pte_efuse_val;
pte_efuse = ioremap(pte_efuse_phys, 4);
- /* Select frequency tables. */
- if (pte_efuse) {
- pte_efuse_val = readl_relaxed(pte_efuse);
- pvs = (pte_efuse_val >> 10) & 0x7;
- iounmap(pte_efuse);
- if (pvs == 0x7)
- pvs = (pte_efuse_val >> 13) & 0x7;
-
- switch (pvs) {
- case 0x0:
- case 0x7:
- tbl_idx = PVS_SLOW;
- break;
- case 0x1:
- tbl_idx = PVS_NOMINAL;
- break;
- case 0x3:
- tbl_idx = PVS_FAST;
- break;
- case 0x4:
- tbl_idx = PVS_FASTER;
- break;
- default:
- tbl_idx = PVS_UNKNOWN;
- break;
- }
- } else {
- tbl_idx = PVS_UNKNOWN;
+ if (!pte_efuse) {
dev_err(drv.dev, "Unable to map QFPROM base\n");
- }
- if (tbl_idx == PVS_UNKNOWN) {
- tbl_idx = PVS_SLOW;
- dev_warn(drv.dev, "ACPU PVS: Defaulting to %s\n",
- pvs_names[tbl_idx]);
- } else {
- dev_info(drv.dev, "ACPU PVS: %s\n", pvs_names[tbl_idx]);
+ return NULL;
}
- return tbl_idx;
+ pte_efuse_val = readl_relaxed(pte_efuse);
+ iounmap(pte_efuse);
+
+ /* Select frequency tables. */
+ drv.speed_bin = get_speed_bin(pte_efuse_val);
+ drv.pvs_bin = get_pvs_bin(pte_efuse_val);
+
+ return &pvs_tables[drv.speed_bin][drv.pvs_bin];
}
static void __init drv_data_init(struct device *dev,
const struct acpuclk_krait_params *params)
{
- int tbl_idx;
+ struct pvs_table *pvs;
drv.dev = dev;
drv.scalable = kmemdup(params->scalable, params->scalable_size,
@@ -1020,12 +1021,12 @@
GFP_KERNEL);
BUG_ON(!drv.bus_scale->usecase);
- tbl_idx = select_freq_plan(params->pte_efuse_phys);
- drv.acpu_freq_tbl = kmemdup(params->pvs_tables[tbl_idx].table,
- params->pvs_tables[tbl_idx].size,
- GFP_KERNEL);
+ pvs = select_freq_plan(params->pte_efuse_phys, params->pvs_tables);
+ BUG_ON(!pvs->table);
+
+ drv.acpu_freq_tbl = kmemdup(pvs->table, pvs->size, GFP_KERNEL);
BUG_ON(!drv.acpu_freq_tbl);
- drv.boost_uv = params->pvs_tables[tbl_idx].boost_uv;
+ drv.boost_uv = pvs->boost_uv;
acpuclk_krait_data.power_collapse_khz = params->stby_khz;
acpuclk_krait_data.wait_for_irq_khz = params->stby_khz;
@@ -1081,5 +1082,7 @@
acpuclk_register(&acpuclk_krait_data);
register_hotcpu_notifier(&acpuclk_cpu_notifier);
+ acpuclk_krait_debug_init(&drv);
+
return 0;
}
diff --git a/arch/arm/mach-msm/acpuclock-krait.h b/arch/arm/mach-msm/acpuclock-krait.h
index 84a5b5e..245f276 100644
--- a/arch/arm/mach-msm/acpuclock-krait.h
+++ b/arch/arm/mach-msm/acpuclock-krait.h
@@ -39,6 +39,7 @@
PLL_0 = 0,
HFPLL,
PLL_8,
+ NUM_SRC_ID
};
/**
@@ -46,14 +47,18 @@
*/
enum pvs {
PVS_SLOW = 0,
- PVS_NOMINAL,
- PVS_FAST,
- PVS_FASTER,
- PVS_UNKNOWN,
- NUM_PVS
+ PVS_NOMINAL = 1,
+ PVS_FAST = 3,
+ PVS_FASTER = 4,
+ NUM_PVS = 7
};
/**
+ * The maximum number of speed bins.
+ */
+#define NUM_SPEED_BINS (16)
+
+/**
* enum scalables - IDs of frequency scalable hardware blocks.
*/
enum scalables {
@@ -62,6 +67,7 @@
CPU2,
CPU3,
L2,
+ MAX_SCALABLES
};
@@ -186,8 +192,8 @@
const bool has_droop_ctl;
const u32 droop_offset;
const u32 droop_val;
- const u32 low_vdd_l_max;
- const u32 nom_vdd_l_max;
+ u32 low_vdd_l_max;
+ u32 nom_vdd_l_max;
const u32 low_vco_l_max;
const int vdd[NUM_HFPLL_VDD];
};
@@ -237,7 +243,7 @@
* @scalable: Array of scalables.
* @scalable_size: Size of @scalable.
* @hfpll_data: HFPLL configuration data.
- * @pvs_tables: CPU frequency tables.
+ * @pvs_tables: 2D array of CPU frequency tables.
* @l2_freq_tbl: L2 frequency table.
* @l2_freq_tbl_size: Size of @l2_freq_tbl.
* @pte_efuse_phys: Physical address of PTE EFUSE.
@@ -248,7 +254,7 @@
struct scalable *scalable;
size_t scalable_size;
struct hfpll_data *hfpll_data;
- struct pvs_table *pvs_tables;
+ struct pvs_table (*pvs_tables)[NUM_PVS];
struct l2_level *l2_freq_tbl;
size_t l2_freq_tbl_size;
phys_addr_t pte_efuse_phys;
@@ -257,6 +263,32 @@
};
/**
+ * struct drv_data - Driver state
+ * @acpu_freq_tbl: CPU frequency table.
+ * @l2_freq_tbl: L2 frequency table.
+ * @scalable: Array of scalables (CPUs and L2).
+ * @hfpll_data: High-frequency PLL data.
+ * @bus_perf_client: Bus driver client handle.
+ * @bus_scale: Bus driver scaling data.
+ * @boost_uv: Voltage boost amount
+ * @speed_bin: Speed bin ID.
+ * @pvs_bin: PVS bin ID.
+ * @dev: Device.
+ */
+struct drv_data {
+ struct acpu_level *acpu_freq_tbl;
+ const struct l2_level *l2_freq_tbl;
+ struct scalable *scalable;
+ struct hfpll_data *hfpll_data;
+ u32 bus_perf_client;
+ struct msm_bus_scale_pdata *bus_scale;
+ int boost_uv;
+ int speed_bin;
+ int pvs_bin;
+ struct device *dev;
+};
+
+/**
* struct acpuclk_platform_data - PMIC configuration data.
* @uses_pm8917: Boolean indicates presence of pm8917.
*/
@@ -269,4 +301,14 @@
*/
extern int acpuclk_krait_init(struct device *dev,
const struct acpuclk_krait_params *params);
+
+#ifdef CONFIG_DEBUG_FS
+/**
+ * acpuclk_krait_debug_init - Initialize debugfs interface.
+ */
+extern void __init acpuclk_krait_debug_init(struct drv_data *drv);
+#else
+static inline void acpuclk_krait_debug_init(void) { }
+#endif
+
#endif
diff --git a/arch/arm/mach-msm/adsp-8974.c b/arch/arm/mach-msm/adsp-8974.c
deleted file mode 100644
index fa7d9d4..0000000
--- a/arch/arm/mach-msm/adsp-8974.c
+++ /dev/null
@@ -1,301 +0,0 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/reboot.h>
-#include <linux/workqueue.h>
-#include <linux/io.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/err.h>
-
-#include <mach/irqs.h>
-#include <mach/scm.h>
-#include <mach/peripheral-loader.h>
-#include <mach/subsystem_restart.h>
-#include <mach/subsystem_notif.h>
-
-#include "smd_private.h"
-#include "ramdump.h"
-#include "sysmon.h"
-
-#define SCM_Q6_NMI_CMD 0x1
-#define MODULE_NAME "adsp_8974"
-#define MAX_BUF_SIZE 0x51
-
-/* Interrupt line for WDOG bite*/
-#define ADSP_Q6SS_WDOG_EXPIRED 194
-
-/* Subsystem restart: QDSP6 data, functions */
-static void adsp_fatal_fn(struct work_struct *);
-static DECLARE_WORK(adsp_fatal_work, adsp_fatal_fn);
-
-struct adsp_ssr {
- void *adsp_ramdump_dev;
-} adsp_ssr;
-
-static struct adsp_ssr adsp_ssr_8974;
-static int q6_crash_shutdown;
-
-static int riva_notifier_cb(struct notifier_block *this, unsigned long code,
- void *ss_handle)
-{
- int ret;
- switch (code) {
- case SUBSYS_BEFORE_SHUTDOWN:
- pr_debug("%s: R-Notify: Shutdown started\n", __func__);
- ret = sysmon_send_event(SYSMON_SS_LPASS, "wcnss",
- SUBSYS_BEFORE_SHUTDOWN);
- if (ret < 0)
- pr_err("%s: sysmon_send_event error %d", __func__,
- ret);
- break;
- }
- return NOTIFY_DONE;
-}
-
-static void *ssr_notif_hdle;
-static struct notifier_block rnb = {
- .notifier_call = riva_notifier_cb,
-};
-
-static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
- void *ss_handle)
-{
- int ret;
- switch (code) {
- case SUBSYS_BEFORE_SHUTDOWN:
- pr_debug("%s: M-Notify: Shutdown started\n", __func__);
- ret = sysmon_send_event(SYSMON_SS_LPASS, "modem",
- SUBSYS_BEFORE_SHUTDOWN);
- if (ret < 0)
- pr_err("%s: sysmon_send_event error %d", __func__,
- ret);
- break;
- }
- return NOTIFY_DONE;
-}
-
-static void *ssr_modem_notif_hdle;
-static struct notifier_block mnb = {
- .notifier_call = modem_notifier_cb,
-};
-
-static void adsp_log_failure_reason(void)
-{
- char *reason;
- char buffer[MAX_BUF_SIZE];
- unsigned size;
-
- reason = smem_get_entry(SMEM_SSR_REASON_LPASS0, &size);
-
- if (!reason) {
- pr_err("%s: subsystem failure reason: (unknown, smem_get_entry failed).",
- MODULE_NAME);
- return;
- }
-
- if (reason[0] == '\0') {
- pr_err("%s: subsystem failure reason: (unknown, init value found)",
- MODULE_NAME);
- return;
- }
-
- size = size < MAX_BUF_SIZE ? size : (MAX_BUF_SIZE-1);
- memcpy(buffer, reason, size);
- buffer[size] = '\0';
- pr_err("%s: subsystem failure reason: %s", MODULE_NAME, buffer);
- memset((void *)reason, 0x0, size);
- wmb();
-}
-
-static void restart_adsp(void)
-{
- adsp_log_failure_reason();
- subsystem_restart("adsp");
-}
-
-static void adsp_fatal_fn(struct work_struct *work)
-{
- pr_err("%s %s: Watchdog bite received from Q6!\n", MODULE_NAME,
- __func__);
- restart_adsp();
-}
-
-static void adsp_smsm_state_cb(void *data, uint32_t old_state,
- uint32_t new_state)
-{
- /* Ignore if we're the one that set SMSM_RESET */
- if (q6_crash_shutdown)
- return;
-
- if (new_state & SMSM_RESET) {
- pr_debug("%s: ADSP SMSM state changed to SMSM_RESET, new_state= 0x%x, old_state = 0x%x\n",
- __func__, new_state, old_state);
- restart_adsp();
- }
-}
-
-static void send_q6_nmi(void)
-{
- /* Send NMI to QDSP6 via an SCM call. */
- scm_call_atomic1(SCM_SVC_UTIL, SCM_Q6_NMI_CMD, 0x1);
- pr_debug("%s: Q6 NMI was sent.\n", __func__);
-}
-
-static int adsp_shutdown(const struct subsys_desc *subsys)
-{
- send_q6_nmi();
-
- /* The write needs to go through before the q6 is shutdown. */
- mb();
-
- pil_force_shutdown("adsp");
- disable_irq_nosync(ADSP_Q6SS_WDOG_EXPIRED);
-
- return 0;
-}
-
-static int adsp_powerup(const struct subsys_desc *subsys)
-{
- int ret;
-
- if (get_restart_level() == RESET_SUBSYS_INDEPENDENT) {
- pr_debug("%s: Wait for ADSP power up!", __func__);
- msleep(10000);
- }
-
- ret = pil_force_boot("adsp");
- enable_irq(ADSP_Q6SS_WDOG_EXPIRED);
- return ret;
-}
-/* RAM segments - address and size for 8974 */
-static struct ramdump_segment q6_segment = {0xdc00000, 0x1800000};
-
-static int adsp_ramdump(int enable, const struct subsys_desc *subsys)
-{
- pr_debug("%s: enable[%d]\n", __func__, enable);
- if (enable)
- return do_ramdump(adsp_ssr_8974.adsp_ramdump_dev,
- &q6_segment, 1);
- else
- return 0;
-}
-
-static void adsp_crash_shutdown(const struct subsys_desc *subsys)
-{
- q6_crash_shutdown = 1;
- send_q6_nmi();
-}
-
-static irqreturn_t adsp_wdog_bite_irq(int irq, void *dev_id)
-{
- int ret;
-
- pr_debug("%s: rxed irq[0x%x]", __func__, irq);
- disable_irq_nosync(ADSP_Q6SS_WDOG_EXPIRED);
- ret = schedule_work(&adsp_fatal_work);
-
- return IRQ_HANDLED;
-}
-
-static struct subsys_device *adsp_8974_dev;
-
-static struct subsys_desc adsp_8974 = {
- .name = "adsp",
- .shutdown = adsp_shutdown,
- .powerup = adsp_powerup,
- .ramdump = adsp_ramdump,
- .crash_shutdown = adsp_crash_shutdown
-};
-
-static int __init adsp_restart_init(void)
-{
- adsp_8974_dev = subsys_register(&adsp_8974);
- if (IS_ERR(adsp_8974_dev))
- return PTR_ERR(adsp_8974_dev);
- return 0;
-}
-
-static int __init adsp_fatal_init(void)
-{
- int ret;
-
- ret = smsm_state_cb_register(SMSM_Q6_STATE, SMSM_RESET,
- adsp_smsm_state_cb, 0);
-
- if (ret < 0)
- pr_err("%s: Unable to register SMSM callback! (%d)\n",
- __func__, ret);
-
- ret = request_irq(ADSP_Q6SS_WDOG_EXPIRED, adsp_wdog_bite_irq,
- IRQF_TRIGGER_RISING, "q6_wdog", NULL);
-
- if (ret < 0) {
- pr_err("%s: Unable to request ADSP_Q6SS_WDOG_EXPIRED irq.",
- __func__);
- goto out;
- }
- ret = adsp_restart_init();
- if (ret < 0) {
- pr_err("%s: Unable to reg with adsp ssr. (%d)\n",
- __func__, ret);
- goto out;
- }
-
- adsp_ssr_8974.adsp_ramdump_dev = create_ramdump_device("adsp");
-
- if (!adsp_ssr_8974.adsp_ramdump_dev) {
- pr_err("%s: Unable to create ramdump device.\n",
- __func__);
- ret = -ENOMEM;
- goto out;
- }
- ssr_notif_hdle = subsys_notif_register_notifier("riva",
- &rnb);
- if (IS_ERR(ssr_notif_hdle) < 0) {
- ret = PTR_ERR(ssr_notif_hdle);
- pr_err("%s: subsys_register_notifier for Riva: err = %d\n",
- __func__, ret);
- free_irq(ADSP_Q6SS_WDOG_EXPIRED, NULL);
- goto out;
- }
-
- ssr_modem_notif_hdle = subsys_notif_register_notifier("modem",
- &mnb);
- if (IS_ERR(ssr_modem_notif_hdle) < 0) {
- ret = PTR_ERR(ssr_modem_notif_hdle);
- pr_err("%s: subsys_register_notifier for Modem: err = %d\n",
- __func__, ret);
- subsys_notif_unregister_notifier(ssr_notif_hdle, &rnb);
- free_irq(ADSP_Q6SS_WDOG_EXPIRED, NULL);
- goto out;
- }
-
- pr_info("%s: adsp ssr driver init'ed.\n", __func__);
-out:
- return ret;
-}
-
-static void __exit adsp_fatal_exit(void)
-{
- subsys_notif_unregister_notifier(ssr_notif_hdle, &rnb);
- subsys_notif_unregister_notifier(ssr_modem_notif_hdle, &mnb);
- subsys_unregister(adsp_8974_dev);
- free_irq(ADSP_Q6SS_WDOG_EXPIRED, NULL);
-}
-
-module_init(adsp_fatal_init);
-module_exit(adsp_fatal_exit);
-
-MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/bam_dmux.c b/arch/arm/mach-msm/bam_dmux.c
index d5e4638..7ba22f4 100644
--- a/arch/arm/mach-msm/bam_dmux.c
+++ b/arch/arm/mach-msm/bam_dmux.c
@@ -171,7 +171,6 @@
#define A2_NUM_PIPES 6
#define A2_SUMMING_THRESHOLD 4096
-#define A2_DEFAULT_DESCRIPTORS 32
#define A2_PHYS_BASE 0x124C2000
#define A2_PHYS_SIZE 0x2000
#define BUFFER_SIZE 2048
@@ -223,6 +222,7 @@
static void rx_timer_work_func(struct work_struct *work);
static DECLARE_WORK(rx_timer_work, rx_timer_work_func);
+static struct delayed_work queue_rx_work;
static struct workqueue_struct *bam_mux_rx_workqueue;
static struct workqueue_struct *bam_mux_tx_workqueue;
@@ -430,21 +430,27 @@
rx_len_cached = bam_rx_pool_len;
mutex_unlock(&bam_rx_pool_mutexlock);
- while (rx_len_cached < NUM_BUFFERS) {
+ while (bam_connection_is_active && rx_len_cached < NUM_BUFFERS) {
if (in_global_reset)
goto fail;
- info = kmalloc(sizeof(struct rx_pkt_info), GFP_KERNEL);
+ info = kmalloc(sizeof(struct rx_pkt_info),
+ GFP_NOWAIT | __GFP_NOWARN);
if (!info) {
- pr_err("%s: unable to alloc rx_pkt_info\n", __func__);
+ DMUX_LOG_KERR(
+ "%s: unable to alloc rx_pkt_info, will retry later\n",
+ __func__);
goto fail;
}
INIT_WORK(&info->work, handle_bam_mux_cmd);
- info->skb = __dev_alloc_skb(BUFFER_SIZE, GFP_KERNEL);
+ info->skb = __dev_alloc_skb(BUFFER_SIZE,
+ GFP_NOWAIT | __GFP_NOWARN);
if (info->skb == NULL) {
- DMUX_LOG_KERR("%s: unable to alloc skb\n", __func__);
+ DMUX_LOG_KERR(
+ "%s: unable to alloc skb, will retry later\n",
+ __func__);
goto fail_info;
}
ptr = skb_put(info->skb, BUFFER_SIZE);
@@ -488,11 +494,16 @@
fail:
if (rx_len_cached == 0) {
- DMUX_LOG_KERR("%s: RX queue failure\n", __func__);
- in_global_reset = 1;
+ DMUX_LOG_KERR("%s: rescheduling\n", __func__);
+ schedule_delayed_work(&queue_rx_work, msecs_to_jiffies(100));
}
}
+static void queue_rx_work_func(struct work_struct *work)
+{
+ queue_rx();
+}
+
static void bam_mux_process_data(struct sk_buff *rx_skb)
{
unsigned long flags;
@@ -2422,6 +2433,7 @@
init_completion(&bam_connection_completion);
init_completion(&dfab_unvote_completion);
INIT_DELAYED_WORK(&ul_timeout_work, ul_timeout);
+ INIT_DELAYED_WORK(&queue_rx_work, queue_rx_work_func);
wake_lock_init(&bam_wakelock, WAKE_LOCK_SUSPEND, "bam_dmux_wakelock");
rc = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_A2_POWER_CONTROL,
diff --git a/arch/arm/mach-msm/board-8064-camera.c b/arch/arm/mach-msm/board-8064-camera.c
index c79f82f..0a95e51 100644
--- a/arch/arm/mach-msm/board-8064-camera.c
+++ b/arch/arm/mach-msm/board-8064-camera.c
@@ -503,6 +503,34 @@
.i2c_mux_mode = MODE_L,
};
+static struct msm_camera_sensor_flash_data flash_imx135 = {
+ .flash_type = MSM_CAMERA_FLASH_NONE,
+};
+
+static struct msm_camera_csi_lane_params imx135_csi_lane_params = {
+ .csi_lane_assign = 0xE4,
+ .csi_lane_mask = 0xF,
+};
+
+static struct msm_camera_sensor_platform_info sensor_board_info_imx135 = {
+ .mount_angle = 90,
+ .cam_vreg = apq_8064_cam_vreg,
+ .num_vreg = ARRAY_SIZE(apq_8064_cam_vreg),
+ .gpio_conf = &apq8064_back_cam_gpio_conf,
+ .i2c_conf = &apq8064_back_cam_i2c_conf,
+ .csi_lane_params = &imx135_csi_lane_params,
+};
+
+static struct msm_camera_sensor_info msm_camera_sensor_imx135_data = {
+ .sensor_name = "imx135",
+ .pdata = &msm_camera_csi_device_data[0],
+ .flash_data = &flash_imx135,
+ .sensor_platform_info = &sensor_board_info_imx135,
+ .csi_if = 1,
+ .camera_type = BACK_CAMERA_2D,
+ .sensor_type = BAYER_SENSOR,
+};
+
static struct msm_camera_sensor_flash_data flash_imx074 = {
.flash_type = MSM_CAMERA_FLASH_LED,
.flash_src = &msm_flash_src
@@ -700,6 +728,10 @@
.platform_data = &msm_camera_sensor_imx074_data,
},
{
+ I2C_BOARD_INFO("imx135", 0x10),
+ .platform_data = &msm_camera_sensor_imx135_data,
+ },
+ {
I2C_BOARD_INFO("mt9m114", 0x48),
.platform_data = &msm_camera_sensor_mt9m114_data,
},
diff --git a/arch/arm/mach-msm/board-8064-display.c b/arch/arm/mach-msm/board-8064-display.c
index 56c3241..b717973 100644
--- a/arch/arm/mach-msm/board-8064-display.c
+++ b/arch/arm/mach-msm/board-8064-display.c
@@ -50,6 +50,7 @@
#define MSM_FB_OVERLAY1_WRITEBACK_SIZE (0)
#endif /* CONFIG_FB_MSM_OVERLAY1_WRITEBACK */
+#define AVTIMER_PHYSICAL_ADDRESS 0x28009008
static struct resource msm_fb_resources[] = {
{
@@ -246,7 +247,7 @@
static struct msm_panel_common_pdata mdp_pdata = {
.gpio = MDP_VSYNC_GPIO,
- .mdp_max_clk = 200000000,
+ .mdp_max_clk = 266667000,
.mdp_bus_scale_table = &mdp_bus_scale_pdata,
.mdp_rev = MDP_REV_44,
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
@@ -255,6 +256,7 @@
.mem_hid = MEMTYPE_EBI1,
#endif
.mdp_iommu_split_domain = 1,
+ .avtimer_phy = AVTIMER_PHYSICAL_ADDRESS,
};
void __init apq8064_mdp_writeback(struct memtype_reserve* reserve_table)
diff --git a/arch/arm/mach-msm/board-8064-gpiomux.c b/arch/arm/mach-msm/board-8064-gpiomux.c
index a08f45c..cb03d4b 100644
--- a/arch/arm/mach-msm/board-8064-gpiomux.c
+++ b/arch/arm/mach-msm/board-8064-gpiomux.c
@@ -31,6 +31,7 @@
.drv = GPIOMUX_DRV_8MA,
.func = GPIOMUX_FUNC_GPIO,
};
+#endif
/* The SPI configurations apply to GSBI 5*/
static struct gpiomux_setting gpio_spi_config = {
@@ -60,6 +61,7 @@
.pull = GPIOMUX_PULL_UP,
};
+#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE)
struct msm_gpiomux_config apq8064_ethernet_configs[] = {
{
.gpio = 43,
@@ -361,6 +363,22 @@
.pull = GPIOMUX_PULL_NONE,
};
+static struct gpiomux_setting audio_auxpcm[] = {
+/* Suspended state */
+ {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+ },
+/* Active state */
+ {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+ },
+};
+
+
static struct gpiomux_setting wcnss_5wire_suspend_cfg = {
.func = GPIOMUX_FUNC_GPIO,
.drv = GPIOMUX_DRV_2MA,
@@ -682,7 +700,6 @@
[GPIOMUX_SUSPENDED] = &gsbi1_uart_config,
},
},
-#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE)
{
.gpio = 51, /* GSBI5 QUP SPI_DATA_MOSI */
.settings = {
@@ -713,7 +730,6 @@
[GPIOMUX_SUSPENDED] = &gpio_spi_config,
},
},
-#endif
{
.gpio = 30, /* FP CS */
.settings = {
@@ -846,6 +862,37 @@
},
};
+static struct msm_gpiomux_config mpq8064_audio_auxpcm_configs[] __initdata = {
+ {
+ .gpio = 43,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &audio_auxpcm[0],
+ [GPIOMUX_ACTIVE] = &audio_auxpcm[1],
+ },
+ },
+ {
+ .gpio = 44,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &audio_auxpcm[0],
+ [GPIOMUX_ACTIVE] = &audio_auxpcm[1],
+ },
+ },
+ {
+ .gpio = 45,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &audio_auxpcm[0],
+ [GPIOMUX_ACTIVE] = &audio_auxpcm[1],
+ },
+ },
+ {
+ .gpio = 46,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &audio_auxpcm[0],
+ [GPIOMUX_ACTIVE] = &audio_auxpcm[1],
+ },
+ },
+};
+
/* External 3.3 V regulator enable */
static struct msm_gpiomux_config apq8064_ext_regulator_configs[] __initdata = {
{
@@ -1527,6 +1574,10 @@
if (machine_is_mpq8064_cdp() || machine_is_mpq8064_hrd() ||
machine_is_mpq8064_dtv()) {
+
+ msm_gpiomux_install(mpq8064_audio_auxpcm_configs,
+ ARRAY_SIZE(mpq8064_audio_auxpcm_configs));
+
msm_gpiomux_install(mpq8064_spkr_i2s_config,
ARRAY_SIZE(mpq8064_spkr_i2s_config));
}
diff --git a/arch/arm/mach-msm/board-8064-gpu.c b/arch/arm/mach-msm/board-8064-gpu.c
index 68debff..fad7092 100644
--- a/arch/arm/mach-msm/board-8064-gpu.c
+++ b/arch/arm/mach-msm/board-8064-gpu.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -24,26 +24,51 @@
#ifdef CONFIG_MSM_DCVS
static struct msm_dcvs_freq_entry grp3d_freq[] = {
- {0, 0, 333932},
- {0, 0, 497532},
- {0, 0, 707610},
- {0, 0, 844545},
+ {0, 900, 0, 0, 0},
+ {0, 950, 0, 0, 0},
+ {0, 950, 0, 0, 0},
+ {0, 1200, 1, 100, 100},
};
static struct msm_dcvs_core_info grp3d_core_info = {
- .freq_tbl = &grp3d_freq[0],
- .core_param = {
- .max_time_us = 100000,
- .num_freq = ARRAY_SIZE(grp3d_freq),
+ .freq_tbl = &grp3d_freq[0],
+ .num_cores = 1,
+ .sensors = (int[]){0},
+ .thermal_poll_ms = 60000,
+ .core_param = {
+ .core_type = MSM_DCVS_CORE_TYPE_GPU,
},
- .algo_param = {
- .slack_time_us = 39000,
- .disable_pc_threshold = 86000,
- .ss_window_size = 1000000,
- .ss_util_pct = 95,
- .em_max_util_pct = 97,
- .ss_iobusy_conv = 100,
+ .algo_param = {
+ .disable_pc_threshold = 0,
+ .em_win_size_min_us = 100000,
+ .em_win_size_max_us = 300000,
+ .em_max_util_pct = 97,
+ .group_id = 0,
+ .max_freq_chg_time_us = 100000,
+ .slack_mode_dynamic = 0,
+ .slack_time_min_us = 39000,
+ .slack_time_max_us = 39000,
+ .ss_win_size_min_us = 1000000,
+ .ss_win_size_max_us = 1000000,
+ .ss_util_pct = 95,
+ .ss_iobusy_conv = 100,
},
+
+ .energy_coeffs = {
+ .leakage_coeff_a = -17720,
+ .leakage_coeff_b = 37,
+ .leakage_coeff_c = 3329,
+ .leakage_coeff_d = -277,
+
+ .active_coeff_a = 2492,
+ .active_coeff_b = 0,
+ .active_coeff_c = 0
+ },
+
+ .power_param = {
+ .current_temp = 25,
+ .num_freq = ARRAY_SIZE(grp3d_freq),
+ }
};
#endif /* CONFIG_MSM_DCVS */
diff --git a/arch/arm/mach-msm/board-8064-pmic.c b/arch/arm/mach-msm/board-8064-pmic.c
index 3b47d2e..e64a672 100644
--- a/arch/arm/mach-msm/board-8064-pmic.c
+++ b/arch/arm/mach-msm/board-8064-pmic.c
@@ -115,7 +115,6 @@
/* Initial PM8921 GPIO configurations */
static struct pm8xxx_gpio_init pm8921_gpios[] __initdata = {
PM8921_GPIO_OUTPUT(14, 1, HIGH), /* HDMI Mux Selector */
- PM8921_GPIO_OUTPUT(23, 0, HIGH), /* touchscreen power FET */
PM8921_GPIO_OUTPUT_BUFCONF(25, 0, LOW, CMOS), /* DISP_RESET_N */
PM8921_GPIO_OUTPUT_FUNC(26, 0, PM_GPIO_FUNC_2), /* Bl: Off, PWM mode */
PM8921_GPIO_OUTPUT_VIN(30, 1, PM_GPIO_VIN_VPH), /* SMB349 susp line */
@@ -146,10 +145,13 @@
PM8921_GPIO_OUTPUT(37, 0, LOW), /* MUX1_SEL */
};
+static struct pm8xxx_gpio_init touchscreen_gpios[] __initdata = {
+ PM8921_GPIO_OUTPUT(23, 0, HIGH), /* touchscreen power FET */
+};
+
/* Initial PM8917 GPIO configurations */
static struct pm8xxx_gpio_init pm8917_gpios[] __initdata = {
PM8921_GPIO_OUTPUT(14, 1, HIGH), /* HDMI Mux Selector */
- PM8921_GPIO_OUTPUT(23, 0, HIGH), /* touchscreen power FET */
PM8921_GPIO_OUTPUT_BUFCONF(25, 0, LOW, CMOS), /* DISP_RESET_N */
PM8921_GPIO_OUTPUT(26, 1, HIGH), /* Backlight: on */
PM8921_GPIO_OUTPUT_BUFCONF(36, 1, LOW, OPEN_DRAIN),
@@ -210,6 +212,8 @@
apq8064_configure_gpios(pm8917_gpios, ARRAY_SIZE(pm8917_gpios));
if (machine_is_apq8064_cdp() || machine_is_apq8064_liquid()) {
+ apq8064_configure_gpios(touchscreen_gpios,
+ ARRAY_SIZE(touchscreen_gpios));
if (socinfo_get_pmic_model() != PMIC_MODEL_PM8917)
apq8064_configure_gpios(pm8921_cdp_kp_gpios,
ARRAY_SIZE(pm8921_cdp_kp_gpios));
@@ -395,7 +399,10 @@
.max_voltage = MAX_VOLTAGE_MV,
.min_voltage = 3200,
.uvd_thresh_voltage = 4050,
- .resume_voltage_delta = 100,
+ .alarm_low_mv = 3400,
+ .alarm_high_mv = 4000,
+ .resume_voltage_delta = 60,
+ .resume_charge_percent = 99,
.term_current = CHG_TERM_MA,
.cool_temp = 10,
.warm_temp = 40,
diff --git a/arch/arm/mach-msm/board-8064-regulator.c b/arch/arm/mach-msm/board-8064-regulator.c
index 6cdafbc..851f7d9 100644
--- a/arch/arm/mach-msm/board-8064-regulator.c
+++ b/arch/arm/mach-msm/board-8064-regulator.c
@@ -64,6 +64,7 @@
VREG_CONSUMERS(L8) = {
REGULATOR_SUPPLY("8921_l8", NULL),
REGULATOR_SUPPLY("cam_vana", "4-001a"),
+ REGULATOR_SUPPLY("cam_vana", "4-0010"),
REGULATOR_SUPPLY("cam_vana", "4-0048"),
REGULATOR_SUPPLY("cam_vana", "4-006c"),
REGULATOR_SUPPLY("cam_vana", "4-0034"),
@@ -84,6 +85,7 @@
};
VREG_CONSUMERS(L12) = {
REGULATOR_SUPPLY("cam_vdig", "4-001a"),
+ REGULATOR_SUPPLY("cam_vdig", "4-0010"),
REGULATOR_SUPPLY("cam_vdig", "4-0048"),
REGULATOR_SUPPLY("cam_vdig", "4-006c"),
REGULATOR_SUPPLY("cam_vdig", "4-0034"),
@@ -102,6 +104,7 @@
VREG_CONSUMERS(L16) = {
REGULATOR_SUPPLY("8921_l16", NULL),
REGULATOR_SUPPLY("cam_vaf", "4-001a"),
+ REGULATOR_SUPPLY("cam_vaf", "4-0010"),
REGULATOR_SUPPLY("cam_vaf", "4-0048"),
REGULATOR_SUPPLY("cam_vaf", "4-006c"),
REGULATOR_SUPPLY("cam_vaf", "4-0034"),
@@ -121,8 +124,6 @@
};
VREG_CONSUMERS(L23) = {
REGULATOR_SUPPLY("8921_l23", NULL),
- REGULATOR_SUPPLY("pll_vdd", "pil_qdsp6v4.1"),
- REGULATOR_SUPPLY("pll_vdd", "pil_qdsp6v4.2"),
REGULATOR_SUPPLY("HSUSB_1p8", "msm_ehci_host.0"),
REGULATOR_SUPPLY("HSUSB_1p8", "msm_ehci_host.1"),
};
@@ -141,15 +142,13 @@
};
VREG_CONSUMERS(L26) = {
REGULATOR_SUPPLY("8921_l26", NULL),
- REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.0"),
+ REGULATOR_SUPPLY("core_vdd", "pil-q6v4-lpass"),
};
VREG_CONSUMERS(L27) = {
REGULATOR_SUPPLY("8921_l27", NULL),
- REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.2"),
};
VREG_CONSUMERS(L28) = {
REGULATOR_SUPPLY("8921_l28", NULL),
- REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.1"),
};
VREG_CONSUMERS(L29) = {
REGULATOR_SUPPLY("8921_l29", NULL),
@@ -218,6 +217,7 @@
VREG_CONSUMERS(LVS5) = {
REGULATOR_SUPPLY("8921_lvs5", NULL),
REGULATOR_SUPPLY("cam_vio", "4-001a"),
+ REGULATOR_SUPPLY("cam_vio", "4-0010"),
REGULATOR_SUPPLY("cam_vio", "4-0048"),
REGULATOR_SUPPLY("cam_vio", "4-006c"),
REGULATOR_SUPPLY("cam_vio", "4-0034"),
@@ -771,4 +771,10 @@
= ARRAY_SIZE(vreg_consumers_8917_S1);
}
}
+
+ /*
+ * Switch to 8960_PM8917 rpm-regulator version so that TCXO workaround
+ * is applied to PM8917 regulators L25, L26, L27, and L28.
+ */
+ apq8064_rpm_regulator_pdata.version = RPM_VREG_VERSION_8960_PM8917;
}
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index 1efc64b..9e5e4f5 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -450,59 +450,27 @@
{
#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
unsigned int i;
- unsigned int reusable_count = 0;
unsigned int fixed_size = 0;
unsigned int fixed_low_size, fixed_middle_size, fixed_high_size;
unsigned long fixed_low_start, fixed_middle_start, fixed_high_start;
- apq8064_fmem_pdata.size = 0;
- apq8064_fmem_pdata.reserved_size_low = 0;
- apq8064_fmem_pdata.reserved_size_high = 0;
- apq8064_fmem_pdata.align = PAGE_SIZE;
fixed_low_size = 0;
fixed_middle_size = 0;
fixed_high_size = 0;
- /* We only support 1 reusable heap. Check if more than one heap
- * is specified as reusable and set as non-reusable if found.
- */
- for (i = 0; i < apq8064_ion_pdata.nr; ++i) {
- const struct ion_platform_heap *heap =
- &(apq8064_ion_pdata.heaps[i]);
-
- if (heap->type == (enum ion_heap_type) ION_HEAP_TYPE_CP
- && heap->extra_data) {
- struct ion_cp_heap_pdata *data = heap->extra_data;
-
- reusable_count += (data->reusable) ? 1 : 0;
-
- if (data->reusable && reusable_count > 1) {
- pr_err("%s: Too many heaps specified as "
- "reusable. Heap %s was not configured "
- "as reusable.\n", __func__, heap->name);
- data->reusable = 0;
- }
- }
- }
-
for (i = 0; i < apq8064_ion_pdata.nr; ++i) {
const struct ion_platform_heap *heap =
&(apq8064_ion_pdata.heaps[i]);
if (heap->extra_data) {
int fixed_position = NOT_FIXED;
- int mem_is_fmem = 0;
switch ((int)heap->type) {
case ION_HEAP_TYPE_CP:
- mem_is_fmem = ((struct ion_cp_heap_pdata *)
- heap->extra_data)->mem_is_fmem;
fixed_position = ((struct ion_cp_heap_pdata *)
heap->extra_data)->fixed_position;
break;
case ION_HEAP_TYPE_CARVEOUT:
- mem_is_fmem = ((struct ion_co_heap_pdata *)
- heap->extra_data)->mem_is_fmem;
fixed_position = ((struct ion_co_heap_pdata *)
heap->extra_data)->fixed_position;
break;
@@ -521,21 +489,12 @@
fixed_middle_size += heap->size;
else if (fixed_position == FIXED_HIGH)
fixed_high_size += heap->size;
-
- if (mem_is_fmem)
- apq8064_fmem_pdata.size += heap->size;
}
}
if (!fixed_size)
return;
- if (apq8064_fmem_pdata.size) {
- apq8064_fmem_pdata.reserved_size_low = fixed_low_size +
- HOLE_SIZE;
- apq8064_fmem_pdata.reserved_size_high = fixed_high_size;
- }
-
/* Since the fixed area may be carved out of lowmem,
* make sure the length is a multiple of 1M.
*/
@@ -605,6 +564,11 @@
#endif
}
+static void __init reserve_mpdcvs_memory(void)
+{
+ apq8064_reserve_table[MEMTYPE_EBI1].size += SZ_32K;
+}
+
static void __init apq8064_calculate_reserve_sizes(void)
{
size_pmem_devices();
@@ -613,6 +577,7 @@
reserve_mdp_memory();
reserve_rtb_memory();
reserve_cache_dump_memory();
+ reserve_mpdcvs_memory();
}
static struct reserve_info apq8064_reserve_info __initdata = {
@@ -703,19 +668,6 @@
apq8064_set_display_params(prim_panel_name, ext_panel_name,
ext_resolution);
msm_reserve();
- if (apq8064_fmem_pdata.size) {
-#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
- if (reserve_info->fixed_area_size) {
- apq8064_fmem_pdata.phys =
- reserve_info->fixed_area_start + MSM_MM_FW_SIZE;
- pr_info("mm fw at %lx (fixed) size %x\n",
- reserve_info->fixed_area_start, MSM_MM_FW_SIZE);
- pr_info("fmem start %lx (fixed) size %lx\n",
- apq8064_fmem_pdata.phys,
- apq8064_fmem_pdata.size);
- }
-#endif
- }
}
static void __init place_movable_zone(void)
@@ -927,7 +879,8 @@
static void __init apq8064_ehci_host_init(void)
{
if (machine_is_apq8064_liquid() || machine_is_mpq8064_cdp() ||
- machine_is_mpq8064_hrd() || machine_is_mpq8064_dtv()) {
+ machine_is_mpq8064_hrd() || machine_is_mpq8064_dtv() ||
+ machine_is_apq8064_cdp()) {
if (machine_is_apq8064_liquid())
msm_ehci_host_pdata3.dock_connect_irq =
PM8921_MPP_IRQ(PM8921_IRQ_BASE, 9);
@@ -1769,6 +1722,27 @@
},
};
+static struct msm_bus_vectors qseecom_enable_dfab_sfpb_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_ADM_PORT0,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 70000000UL,
+ .ib = 70000000UL,
+ },
+ {
+ .src = MSM_BUS_MASTER_ADM_PORT1,
+ .dst = MSM_BUS_SLAVE_GSBI1_UART,
+ .ab = 2480000000UL,
+ .ib = 2480000000UL,
+ },
+ {
+ .src = MSM_BUS_MASTER_SPDM,
+ .dst = MSM_BUS_SLAVE_SPDM,
+ .ib = (64 * 8) * 1000000UL,
+ .ab = (64 * 8) * 100000UL,
+ },
+};
+
static struct msm_bus_paths qseecom_hw_bus_scale_usecases[] = {
{
ARRAY_SIZE(qseecom_clks_init_vectors),
@@ -1782,6 +1756,10 @@
ARRAY_SIZE(qseecom_enable_sfpb_vectors),
qseecom_enable_sfpb_vectors,
},
+ {
+ ARRAY_SIZE(qseecom_enable_dfab_sfpb_vectors),
+ qseecom_enable_dfab_sfpb_vectors,
+ },
};
static struct msm_bus_scale_pdata qseecom_bus_pdata = {
@@ -2404,6 +2382,7 @@
static struct gpio_ir_recv_platform_data gpio_ir_recv_pdata = {
.gpio_nr = 88,
.active_low = 1,
+ .can_wakeup = true,
};
static struct platform_device gpio_ir_recv_pdev = {
@@ -2442,7 +2421,6 @@
&apq8064_device_ext_3p3v_vreg,
&apq8064_device_ssbi_pmic1,
&apq8064_device_ssbi_pmic2,
- &apq8064_device_ext_ts_sw_vreg,
};
static struct platform_device *pm8917_common_devices[] __initdata = {
@@ -2450,7 +2428,6 @@
&apq8064_device_ext_3p3v_vreg,
&apq8064_device_ssbi_pmic1,
&apq8064_device_ssbi_pmic2,
- &apq8064_device_ext_ts_sw_vreg,
};
static struct platform_device *common_devices[] __initdata = {
@@ -2532,6 +2509,7 @@
&apq8064_rpm_device,
&apq8064_rpm_log_device,
&apq8064_rpm_stat_device,
+ &apq8064_rpm_master_stat_device,
&apq_device_tz_log,
&msm_bus_8064_apps_fabric,
&msm_bus_8064_sys_fabric,
@@ -2544,7 +2522,7 @@
&msm_pil_vidc,
&msm_gss,
&apq8064_rtb_device,
- &apq8064_cpu_idle_device,
+ &apq8064_msm_gov_device,
&apq8064_device_cache_erp,
&msm8960_device_ebi1_ch0_erp,
&msm8960_device_ebi1_ch1_erp,
@@ -2568,6 +2546,7 @@
#ifdef CONFIG_BATTERY_BCL
&battery_bcl_device,
#endif
+ &apq8064_msm_mpd_device,
};
static struct platform_device *cdp_devices[] __initdata = {
@@ -3347,6 +3326,8 @@
else
platform_add_devices(pm8917_common_devices,
ARRAY_SIZE(pm8917_common_devices));
+ if (machine_is_apq8064_cdp() || machine_is_apq8064_liquid())
+ platform_device_register(&apq8064_device_ext_ts_sw_vreg);
platform_add_devices(common_devices, ARRAY_SIZE(common_devices));
if (!(machine_is_mpq8064_cdp() || machine_is_mpq8064_hrd() ||
machine_is_mpq8064_dtv()))
@@ -3419,6 +3400,8 @@
apq8064_common_init();
if (machine_is_mpq8064_cdp() || machine_is_mpq8064_hrd() ||
machine_is_mpq8064_dtv()) {
+ gpio_ir_recv_pdata.swfi_latency =
+ msm_rpmrs_levels[0].latency_us;
enable_avc_i2c_bus();
msm_rotator_set_split_iommu_domain();
platform_add_devices(mpq_devices, ARRAY_SIZE(mpq_devices));
diff --git a/arch/arm/mach-msm/board-8092.c b/arch/arm/mach-msm/board-8092.c
index 0471ff4..bd1762d 100644
--- a/arch/arm/mach-msm/board-8092.c
+++ b/arch/arm/mach-msm/board-8092.c
@@ -23,7 +23,8 @@
#include <asm/mach/time.h>
#include <mach/socinfo.h>
#include <mach/board.h>
-
+#include <mach/msm_memtypes.h>
+#include <mach/qpnp-int.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/irq.h>
@@ -34,6 +35,7 @@
static struct of_device_id irq_match[] __initdata = {
{ .compatible = "qcom,msm-qgic2", .data = gic_of_init, },
{ .compatible = "qcom,msm-gpio", .data = msm_gpio_of_init, },
+ { .compatible = "qcom,spmi-pmic-arb", .data = qpnpint_of_init, },
{}
};
@@ -47,6 +49,38 @@
.size = ARRAY_SIZE(msm_clocks_dummy),
};
+static struct memtype_reserve mpq8092_reserve_table[] __initdata = {
+ [MEMTYPE_SMI] = {
+ },
+ [MEMTYPE_EBI0] = {
+ .flags = MEMTYPE_FLAGS_1M_ALIGN,
+ },
+ [MEMTYPE_EBI1] = {
+ .flags = MEMTYPE_FLAGS_1M_ALIGN,
+ },
+};
+
+static int mpq8092_paddr_to_memtype(unsigned int paddr)
+{
+ return MEMTYPE_EBI1;
+}
+
+static struct reserve_info mpq8092_reserve_info __initdata = {
+ .memtype_reserve_table = mpq8092_reserve_table,
+ .paddr_to_memtype = mpq8092_paddr_to_memtype,
+};
+
+static void __init mpq8092_early_memory(void)
+{
+ reserve_info = &mpq8092_reserve_info;
+ of_scan_flat_dt(dt_scan_for_memory_reserve, mpq8092_reserve_table);
+}
+
+static void __init mpq8092_dt_reserve(void)
+{
+ msm_reserve();
+}
+
void __init mpq8092_init_irq(void)
{
of_irq_init(irq_match);
@@ -77,6 +111,8 @@
static struct of_dev_auxdata mpq8092_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("qcom,msm-lsuart-v14", 0xF991F000, \
"msm_serial_hsl.0", NULL),
+ OF_DEV_AUXDATA("qcom,spmi-pmic-arb", 0xFC4C0000, \
+ "spmi-pmic-arb.0", NULL),
{}
};
@@ -107,4 +143,6 @@
.handle_irq = gic_handle_irq,
.timer = &mpq8092_dt_timer,
.dt_compat = mpq8092_dt_match,
+ .reserve = mpq8092_dt_reserve,
+ .init_very_early = mpq8092_early_memory,
MACHINE_END
diff --git a/arch/arm/mach-msm/board-8226.c b/arch/arm/mach-msm/board-8226.c
index 9545c7a..b27382f 100644
--- a/arch/arm/mach-msm/board-8226.c
+++ b/arch/arm/mach-msm/board-8226.c
@@ -44,8 +44,10 @@
#include "clock.h"
static struct clk_lookup msm_clocks_dummy[] = {
- CLK_DUMMY("core_clk", BLSP1_UART_CLK, "msm_serial_hsl.0", OFF),
- CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "msm_serial_hsl.0", OFF),
+ CLK_DUMMY("core_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
+ CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
+ CLK_DUMMY("iface_clk", HSUSB_IFACE_CLK, "f9a55000.usb", OFF),
+ CLK_DUMMY("core_clk", HSUSB_CORE_CLK, "f9a55000.usb", OFF),
};
struct clock_init_data msm_dummy_clock_init_data __initdata = {
@@ -59,12 +61,6 @@
{}
};
-static struct of_dev_auxdata msm8226_auxdata_lookup[] __initdata = {
- OF_DEV_AUXDATA("qcom,msm-lsuart-v14", 0xF991F000, \
- "msm_serial_hsl.0", NULL),
- {}
-};
-
static void __init msm8226_dt_timer_init(void)
{
arch_timer_of_register();
@@ -79,24 +75,21 @@
of_irq_init(irq_match);
}
-void __init msm8226_init(struct of_dev_auxdata **adata)
+void __init msm8226_init(void)
{
msm8226_init_gpiomux();
msm_clock_init(&msm_dummy_clock_init_data);
-
- *adata = msm8226_auxdata_lookup;
}
void __init msm8226_dt_init(void)
{
- struct of_dev_auxdata *adata = NULL;
- msm8226_init(&adata);
+ msm8226_init();
if (socinfo_init() < 0)
pr_err("%s: socinfo_init() failed\n", __func__);
- of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
+ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}
diff --git a/arch/arm/mach-msm/board-8910.c b/arch/arm/mach-msm/board-8910.c
new file mode 100644
index 0000000..1c92494f
--- /dev/null
+++ b/arch/arm/mach-msm/board-8910.c
@@ -0,0 +1,96 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/of_fdt.h>
+#include <linux/of_irq.h>
+#include <linux/memory.h>
+#include <asm/mach/map.h>
+#include <asm/arch_timer.h>
+#include <asm/hardware/gic.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+#include <mach/board.h>
+#include <mach/gpiomux.h>
+#include <mach/msm_iomap.h>
+#ifdef CONFIG_ION_MSM
+#include <mach/ion.h>
+#endif
+#include <mach/socinfo.h>
+#include <mach/board.h>
+#include <mach/clk-provider.h>
+#include "clock.h"
+
+static struct clk_lookup msm_clocks_dummy[] = {
+ CLK_DUMMY("core_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
+ CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
+ CLK_DUMMY("iface_clk", HSUSB_IFACE_CLK, "f9a55000.usb", OFF),
+ CLK_DUMMY("core_clk", HSUSB_CORE_CLK, "f9a55000.usb", OFF),
+};
+
+struct clock_init_data msm_dummy_clock_init_data __initdata = {
+ .table = msm_clocks_dummy,
+ .size = ARRAY_SIZE(msm_clocks_dummy),
+};
+
+static struct of_device_id irq_match[] __initdata = {
+ { .compatible = "qcom,msm-qgic2", .data = gic_of_init, },
+ { .compatible = "qcom,msm-gpio", .data = msm_gpio_of_init, },
+ {},
+};
+
+static void __init msm8910_dt_timer_init(void)
+{
+ arch_timer_of_register();
+}
+
+static struct sys_timer msm8910_dt_timer = {
+ .init = msm8910_dt_timer_init
+};
+
+void __init msm8910_init_irq(void)
+{
+ of_irq_init(irq_match);
+}
+
+void __init msm8910_init(void)
+{
+ msm_clock_init(&msm_dummy_clock_init_data);
+
+ if (socinfo_init() < 0)
+ pr_err("%s: socinfo_init() failed\n", __func__);
+
+ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+}
+
+static const char *msm8910_dt_match[] __initconst = {
+ "qcom,msm8910",
+ NULL
+};
+
+DT_MACHINE_START(MSM8910_DT, "Qualcomm MSM 8910 (Flattened Device Tree)")
+ .map_io = msm_map_msm8910_io,
+ .init_irq = msm8910_init_irq,
+ .init_machine = msm8910_init,
+ .handle_irq = gic_handle_irq,
+ .timer = &msm8910_dt_timer,
+ .dt_compat = msm8910_dt_match,
+MACHINE_END
diff --git a/arch/arm/mach-msm/board-8930-gpiomux.c b/arch/arm/mach-msm/board-8930-gpiomux.c
index fcb5abd..cf44e08 100644
--- a/arch/arm/mach-msm/board-8930-gpiomux.c
+++ b/arch/arm/mach-msm/board-8930-gpiomux.c
@@ -69,7 +69,7 @@
static struct gpiomux_setting cdc_mclk = {
.func = GPIOMUX_FUNC_1,
- .drv = GPIOMUX_DRV_8MA,
+ .drv = GPIOMUX_DRV_2MA,
.pull = GPIOMUX_PULL_NONE,
};
diff --git a/arch/arm/mach-msm/board-8930-pmic.c b/arch/arm/mach-msm/board-8930-pmic.c
index 59e4ba1..8687b2a 100644
--- a/arch/arm/mach-msm/board-8930-pmic.c
+++ b/arch/arm/mach-msm/board-8930-pmic.c
@@ -320,7 +320,10 @@
.max_voltage = MAX_VOLTAGE_MV,
.min_voltage = 3200,
.uvd_thresh_voltage = 4050,
- .resume_voltage_delta = 100,
+ .alarm_low_mv = 3400,
+ .alarm_high_mv = 4000,
+ .resume_voltage_delta = 60,
+ .resume_charge_percent = 99,
.term_current = CHG_TERM_MA,
.cool_temp = 10,
.warm_temp = 40,
diff --git a/arch/arm/mach-msm/board-8930-regulator-pm8038.c b/arch/arm/mach-msm/board-8930-regulator-pm8038.c
index 208f15b..16a82b4 100644
--- a/arch/arm/mach-msm/board-8930-regulator-pm8038.c
+++ b/arch/arm/mach-msm/board-8930-regulator-pm8038.c
@@ -117,7 +117,7 @@
};
VREG_CONSUMERS(L16) = {
REGULATOR_SUPPLY("8038_l16", NULL),
- REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.2"),
+ REGULATOR_SUPPLY("sw_core_vdd", "pil-q6v4-modem"),
};
VREG_CONSUMERS(L17) = {
REGULATOR_SUPPLY("8038_l17", NULL),
@@ -127,7 +127,7 @@
};
VREG_CONSUMERS(L19) = {
REGULATOR_SUPPLY("8038_l19", NULL),
- REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.1"),
+ REGULATOR_SUPPLY("fw_core_vdd", "pil-q6v4-modem"),
};
VREG_CONSUMERS(L20) = {
REGULATOR_SUPPLY("8038_l20", NULL),
@@ -151,8 +151,7 @@
REGULATOR_SUPPLY("hdmi_avdd", "hdmi_msm.0"),
REGULATOR_SUPPLY("hdmi_vcc", "hdmi_msm.0"),
REGULATOR_SUPPLY("pll_vdd", "pil_riva"),
- REGULATOR_SUPPLY("pll_vdd", "pil_qdsp6v4.1"),
- REGULATOR_SUPPLY("pll_vdd", "pil_qdsp6v4.2"),
+ REGULATOR_SUPPLY("pll_vdd", "pil-q6v4-modem"),
};
VREG_CONSUMERS(L24) = {
REGULATOR_SUPPLY("8038_l24", NULL),
@@ -166,7 +165,7 @@
};
VREG_CONSUMERS(L27) = {
REGULATOR_SUPPLY("8038_l27", NULL),
- REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.0"),
+ REGULATOR_SUPPLY("core_vdd", "pil-q6v4-lpass"),
};
VREG_CONSUMERS(S1) = {
REGULATOR_SUPPLY("8038_s1", NULL),
diff --git a/arch/arm/mach-msm/board-8930-regulator-pm8917.c b/arch/arm/mach-msm/board-8930-regulator-pm8917.c
index db40e5d..8898b50 100644
--- a/arch/arm/mach-msm/board-8930-regulator-pm8917.c
+++ b/arch/arm/mach-msm/board-8930-regulator-pm8917.c
@@ -547,11 +547,11 @@
RPM_SMPS(S2, 0, 1, 0, 1300000, 1300000, NULL, 0, 1p60, NONE, NONE),
RPM_SMPS(S3, 0, 1, 1, 500000, 1150000, NULL, 100000, 4p80, AUTO, LPM),
RPM_SMPS(S4, 1, 1, 0, 1800000, 1800000, NULL, 100000, 1p60, AUTO, LPM),
- RPM_SMPS(S7, 0, 1, 0, 1150000, 1150000, NULL, 100000, 3p20, NONE, NONE),
+ RPM_SMPS(S7, 0, 1, 0, 1150000, 1150000, NULL, 100000, 3p20, AUTO, AUTO),
RPM_SMPS(S8, 1, 1, 1, 2050000, 2050000, NULL, 100000, 1p60, NONE, NONE),
/* ID a_on pd ss min_uV max_uV supply sys_uA init_ip */
- RPM_LDO(L1, 1, 1, 0, 1050000, 1050000, "8917_s4", 0, 10000),
+ RPM_LDO(L1, 0, 1, 0, 1050000, 1050000, "8917_s4", 0, 10000),
RPM_LDO(L2, 0, 1, 0, 1200000, 1200000, "8917_s4", 0, 0),
RPM_LDO(L3, 0, 1, 0, 3075000, 3075000, NULL, 0, 0),
RPM_LDO(L4, 1, 1, 0, 1800000, 1800000, NULL, 10000, 10000),
diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c
index b84e66e..05d2fe1 100644
--- a/arch/arm/mach-msm/board-8930.c
+++ b/arch/arm/mach-msm/board-8930.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -498,59 +498,27 @@
{
#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
unsigned int i;
- unsigned int reusable_count = 0;
unsigned int fixed_size = 0;
unsigned int fixed_low_size, fixed_middle_size, fixed_high_size;
unsigned long fixed_low_start, fixed_middle_start, fixed_high_start;
- msm8930_fmem_pdata.size = 0;
- msm8930_fmem_pdata.reserved_size_low = 0;
- msm8930_fmem_pdata.reserved_size_high = 0;
- msm8930_fmem_pdata.align = PAGE_SIZE;
fixed_low_size = 0;
fixed_middle_size = 0;
fixed_high_size = 0;
- /* We only support 1 reusable heap. Check if more than one heap
- * is specified as reusable and set as non-reusable if found.
- */
- for (i = 0; i < msm8930_ion_pdata.nr; ++i) {
- const struct ion_platform_heap *heap =
- &(msm8930_ion_pdata.heaps[i]);
-
- if (heap->type == (enum ion_heap_type) ION_HEAP_TYPE_CP
- && heap->extra_data) {
- struct ion_cp_heap_pdata *data = heap->extra_data;
-
- reusable_count += (data->reusable) ? 1 : 0;
-
- if (data->reusable && reusable_count > 1) {
- pr_err("%s: Too many heaps specified as "
- "reusable. Heap %s was not configured "
- "as reusable.\n", __func__, heap->name);
- data->reusable = 0;
- }
- }
- }
-
for (i = 0; i < msm8930_ion_pdata.nr; ++i) {
const struct ion_platform_heap *heap =
&(msm8930_ion_pdata.heaps[i]);
if (heap->extra_data) {
int fixed_position = NOT_FIXED;
- int mem_is_fmem = 0;
switch ((int) heap->type) {
case ION_HEAP_TYPE_CP:
- mem_is_fmem = ((struct ion_cp_heap_pdata *)
- heap->extra_data)->mem_is_fmem;
fixed_position = ((struct ion_cp_heap_pdata *)
heap->extra_data)->fixed_position;
break;
case ION_HEAP_TYPE_CARVEOUT:
- mem_is_fmem = ((struct ion_co_heap_pdata *)
- heap->extra_data)->mem_is_fmem;
fixed_position = ((struct ion_co_heap_pdata *)
heap->extra_data)->fixed_position;
break;
@@ -570,20 +538,12 @@
else if (fixed_position == FIXED_HIGH)
fixed_high_size += heap->size;
- if (mem_is_fmem)
- msm8930_fmem_pdata.size += heap->size;
}
}
if (!fixed_size)
return;
- if (msm8930_fmem_pdata.size) {
- msm8930_fmem_pdata.reserved_size_low = fixed_low_size +
- HOLE_SIZE;
- msm8930_fmem_pdata.reserved_size_high = fixed_high_size;
- }
-
/* Since the fixed area may be carved out of lowmem,
* make sure the length is a multiple of 1M.
*/
@@ -755,18 +715,6 @@
{
msm8930_set_display_params(prim_panel_name, ext_panel_name);
msm_reserve();
- if (msm8930_fmem_pdata.size) {
-#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
- if (reserve_info->fixed_area_size) {
- msm8930_fmem_pdata.phys =
- reserve_info->fixed_area_start + MSM_MM_FW_SIZE;
- pr_info("mm fw at %lx (fixed) size %x\n",
- reserve_info->fixed_area_start, MSM_MM_FW_SIZE);
- pr_info("fmem start %lx (fixed) size %lx\n",
- msm8930_fmem_pdata.phys, msm8930_fmem_pdata.size);
- }
-#endif
- }
}
static int msm8930_change_memory_power(u64 start, u64 size,
@@ -1048,6 +996,27 @@
},
};
+static struct msm_bus_vectors qseecom_enable_dfab_sfpb_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_SPS,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ib = (492 * 8) * 1000000UL,
+ .ab = (492 * 8) * 100000UL,
+ },
+ {
+ .src = MSM_BUS_MASTER_SPS,
+ .dst = MSM_BUS_SLAVE_SPS,
+ .ib = (492 * 8) * 1000000UL,
+ .ab = (492 * 8) * 100000UL,
+ },
+ {
+ .src = MSM_BUS_MASTER_SPDM,
+ .dst = MSM_BUS_SLAVE_SPDM,
+ .ib = (64 * 8) * 1000000UL,
+ .ab = (64 * 8) * 100000UL,
+ },
+};
+
static struct msm_bus_paths qseecom_hw_bus_scale_usecases[] = {
{
ARRAY_SIZE(qseecom_clks_init_vectors),
@@ -1061,6 +1030,10 @@
ARRAY_SIZE(qseecom_enable_sfpb_vectors),
qseecom_enable_sfpb_vectors,
},
+ {
+ ARRAY_SIZE(qseecom_enable_dfab_sfpb_vectors),
+ qseecom_enable_dfab_sfpb_vectors,
+ },
};
static struct msm_bus_scale_pdata qseecom_bus_pdata = {
@@ -2322,8 +2295,7 @@
static struct platform_device *common_devices[] __initdata = {
&msm_8960_q6_lpass,
- &msm_8960_q6_mss_fw,
- &msm_8960_q6_mss_sw,
+ &msm_8960_q6_mss,
&msm_8960_riva,
&msm_pil_tzapps,
&msm_pil_vidc,
@@ -2373,6 +2345,7 @@
&msm8930_rpm_log_device,
&msm8930_rpm_rbcpr_device,
&msm8930_rpm_stat_device,
+ &msm8930_rpm_master_stat_device,
#ifdef CONFIG_ION_MSM
&msm8930_ion_dev,
#endif
@@ -2388,8 +2361,6 @@
&gpio_keys_8930,
#endif
&msm8930_rtb_device,
- &msm8930_cpu_idle_device,
- &msm8930_msm_gov_device,
&msm_bus_8930_apps_fabric,
&msm_bus_8930_sys_fabric,
&msm_bus_8930_mm_fabric,
diff --git a/arch/arm/mach-msm/board-8960-camera.c b/arch/arm/mach-msm/board-8960-camera.c
index 9bb6e09..88fd527 100644
--- a/arch/arm/mach-msm/board-8960-camera.c
+++ b/arch/arm/mach-msm/board-8960-camera.c
@@ -752,6 +752,33 @@
.eeprom_info = &imx091_eeprom_info,
};
+static struct msm_camera_sensor_flash_data flash_imx135 = {
+ .flash_type = MSM_CAMERA_FLASH_NONE,
+};
+
+static struct msm_camera_csi_lane_params imx135_csi_lane_params = {
+ .csi_lane_assign = 0xE4,
+ .csi_lane_mask = 0xF,
+};
+
+static struct msm_camera_sensor_platform_info sensor_board_info_imx135 = {
+ .mount_angle = 90,
+ .cam_vreg = msm_8960_cam_vreg,
+ .num_vreg = ARRAY_SIZE(msm_8960_cam_vreg),
+ .gpio_conf = &msm_8960_back_cam_gpio_conf,
+ .csi_lane_params = &imx135_csi_lane_params,
+};
+
+static struct msm_camera_sensor_info msm_camera_sensor_imx135_data = {
+ .sensor_name = "imx135",
+ .pdata = &msm_camera_csi_device_data[0],
+ .flash_data = &flash_imx135,
+ .sensor_platform_info = &sensor_board_info_imx135,
+ .csi_if = 1,
+ .camera_type = BACK_CAMERA_2D,
+ .sensor_type = BAYER_SENSOR,
+};
+
static struct pm8xxx_mpp_config_data privacy_light_on_config = {
.type = PM8XXX_MPP_TYPE_SINK,
.level = PM8XXX_MPP_CS_OUT_5MA,
@@ -838,6 +865,10 @@
.platform_data = &msm_camera_sensor_ov2720_data,
},
{
+ I2C_BOARD_INFO("imx135", 0x10),
+ .platform_data = &msm_camera_sensor_imx135_data,
+ },
+ {
I2C_BOARD_INFO("mt9m114", 0x48),
.platform_data = &msm_camera_sensor_mt9m114_data,
},
diff --git a/arch/arm/mach-msm/board-8960-gpiomux.c b/arch/arm/mach-msm/board-8960-gpiomux.c
index 1771bb9..fe37f2a 100644
--- a/arch/arm/mach-msm/board-8960-gpiomux.c
+++ b/arch/arm/mach-msm/board-8960-gpiomux.c
@@ -449,6 +449,13 @@
},
},
{
+ .gpio = 26, /* GSBI6 WLAN_PWD_L for AR6004 */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gsbi6_suspended_cfg,
+ [GPIOMUX_ACTIVE] = &gsbi6_active_cfg,
+ },
+ },
+ {
.gpio = 27, /* GSBI6 BT_INT2AP_N for AR3002 */
.settings = {
[GPIOMUX_SUSPENDED] = &gsbi6_suspended_cfg,
diff --git a/arch/arm/mach-msm/board-8960-pmic.c b/arch/arm/mach-msm/board-8960-pmic.c
index ae74285..d8a260b 100644
--- a/arch/arm/mach-msm/board-8960-pmic.c
+++ b/arch/arm/mach-msm/board-8960-pmic.c
@@ -401,7 +401,10 @@
.max_voltage = MAX_VOLTAGE_MV,
.min_voltage = 3200,
.uvd_thresh_voltage = 4050,
- .resume_voltage_delta = 100,
+ .alarm_low_mv = 3400,
+ .alarm_high_mv = 4000,
+ .resume_voltage_delta = 60,
+ .resume_charge_percent = 99,
.term_current = CHG_TERM_MA,
.cool_temp = 10,
.warm_temp = 40,
diff --git a/arch/arm/mach-msm/board-8960-regulator.c b/arch/arm/mach-msm/board-8960-regulator.c
index 2fa98b6..f9e2c8e 100644
--- a/arch/arm/mach-msm/board-8960-regulator.c
+++ b/arch/arm/mach-msm/board-8960-regulator.c
@@ -78,6 +78,7 @@
REGULATOR_SUPPLY("cam_vana", "4-0048"),
REGULATOR_SUPPLY("cam_vana", "4-0020"),
REGULATOR_SUPPLY("cam_vana", "4-0034"),
+ REGULATOR_SUPPLY("cam_vana", "4-0010"),
};
VREG_CONSUMERS(L12) = {
REGULATOR_SUPPLY("8921_l12", NULL),
@@ -86,6 +87,7 @@
REGULATOR_SUPPLY("cam_vdig", "4-0048"),
REGULATOR_SUPPLY("cam_vdig", "4-0020"),
REGULATOR_SUPPLY("cam_vdig", "4-0034"),
+ REGULATOR_SUPPLY("cam_vdig", "4-0010"),
};
VREG_CONSUMERS(L14) = {
REGULATOR_SUPPLY("8921_l14", NULL),
@@ -101,6 +103,7 @@
REGULATOR_SUPPLY("cam_vaf", "4-0048"),
REGULATOR_SUPPLY("cam_vaf", "4-0020"),
REGULATOR_SUPPLY("cam_vaf", "4-0034"),
+ REGULATOR_SUPPLY("cam_vaf", "4-0010"),
};
VREG_CONSUMERS(L17) = {
REGULATOR_SUPPLY("8921_l17", NULL),
@@ -120,8 +123,7 @@
REGULATOR_SUPPLY("dsi_pll_vddio", "mdp.0"),
REGULATOR_SUPPLY("hdmi_avdd", "hdmi_msm.0"),
REGULATOR_SUPPLY("pll_vdd", "pil_riva"),
- REGULATOR_SUPPLY("pll_vdd", "pil_qdsp6v4.1"),
- REGULATOR_SUPPLY("pll_vdd", "pil_qdsp6v4.2"),
+ REGULATOR_SUPPLY("pll_vdd", "pil-q6v4-modem"),
};
VREG_CONSUMERS(L24) = {
REGULATOR_SUPPLY("8921_l24", NULL),
@@ -136,15 +138,15 @@
};
VREG_CONSUMERS(L26) = {
REGULATOR_SUPPLY("8921_l26", NULL),
- REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.0"),
+ REGULATOR_SUPPLY("core_vdd", "pil-q6v4-lpass"),
};
VREG_CONSUMERS(L27) = {
REGULATOR_SUPPLY("8921_l27", NULL),
- REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.2"),
+ REGULATOR_SUPPLY("sw_core_vdd", "pil-q6v4-modem"),
};
VREG_CONSUMERS(L28) = {
REGULATOR_SUPPLY("8921_l28", NULL),
- REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.1"),
+ REGULATOR_SUPPLY("fw_core_vdd", "pil-q6v4-modem"),
};
VREG_CONSUMERS(L29) = {
REGULATOR_SUPPLY("8921_l29", NULL),
@@ -221,6 +223,7 @@
REGULATOR_SUPPLY("cam_vio", "4-0048"),
REGULATOR_SUPPLY("cam_vio", "4-0020"),
REGULATOR_SUPPLY("cam_vio", "4-0034"),
+ REGULATOR_SUPPLY("cam_vio", "4-0010"),
};
/* This mapping is used for CDP only. */
VREG_CONSUMERS(CDP_LVS6) = {
diff --git a/arch/arm/mach-msm/board-8960-storage.c b/arch/arm/mach-msm/board-8960-storage.c
index 67f44aa..ded5bad 100644
--- a/arch/arm/mach-msm/board-8960-storage.c
+++ b/arch/arm/mach-msm/board-8960-storage.c
@@ -327,9 +327,11 @@
#endif
.vreg_data = &mmc_slot_vreg_data[SDCC3],
.pin_data = &mmc_slot_pin_data[SDCC3],
+#ifndef CONFIG_MMC_MSM_SDC3_POLLING
.status_gpio = PM8921_GPIO_PM_TO_SYS(26),
.status_irq = PM8921_GPIO_IRQ(PM8921_IRQ_BASE, 26),
.irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+#endif
.is_status_gpio_active_low = true,
.xpc_cap = 1,
.uhs_caps = (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index d93a4fa..4a78e31 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -103,6 +103,11 @@
#include "pm-boot.h"
#include "msm_watchdog.h"
+#if defined(CONFIG_BT) && defined(CONFIG_BT_HCIUART_ATH3K)
+#include <linux/wlan_plat.h>
+#include <linux/mutex.h>
+#endif
+
static struct platform_device msm_fm_platform_init = {
.name = "iris_fm",
.id = -1,
@@ -541,42 +546,15 @@
{
#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
unsigned int i;
- unsigned int reusable_count = 0;
unsigned int fixed_size = 0;
unsigned int fixed_low_size, fixed_middle_size, fixed_high_size;
unsigned long fixed_low_start, fixed_middle_start, fixed_high_start;
adjust_mem_for_liquid();
- msm8960_fmem_pdata.size = 0;
- msm8960_fmem_pdata.reserved_size_low = 0;
- msm8960_fmem_pdata.reserved_size_high = 0;
- msm8960_fmem_pdata.align = PAGE_SIZE;
fixed_low_size = 0;
fixed_middle_size = 0;
fixed_high_size = 0;
- /* We only support 1 reusable heap. Check if more than one heap
- * is specified as reusable and set as non-reusable if found.
- */
- for (i = 0; i < msm8960_ion_pdata.nr; ++i) {
- const struct ion_platform_heap *heap =
- &(msm8960_ion_pdata.heaps[i]);
-
- if (heap->type == (enum ion_heap_type) ION_HEAP_TYPE_CP
- && heap->extra_data) {
- struct ion_cp_heap_pdata *data = heap->extra_data;
-
- reusable_count += (data->reusable) ? 1 : 0;
-
- if (data->reusable && reusable_count > 1) {
- pr_err("%s: Too many heaps specified as "
- "reusable. Heap %s was not configured "
- "as reusable.\n", __func__, heap->name);
- data->reusable = 0;
- }
- }
- }
-
for (i = 0; i < msm8960_ion_pdata.nr; ++i) {
struct ion_platform_heap *heap =
&(msm8960_ion_pdata.heaps[i]);
@@ -586,12 +564,9 @@
if (heap->extra_data) {
int fixed_position = NOT_FIXED;
- int mem_is_fmem = 0;
switch ((int) heap->type) {
case ION_HEAP_TYPE_CP:
- mem_is_fmem = ((struct ion_cp_heap_pdata *)
- heap->extra_data)->mem_is_fmem;
fixed_position = ((struct ion_cp_heap_pdata *)
heap->extra_data)->fixed_position;
align = ((struct ion_cp_heap_pdata *)
@@ -601,8 +576,6 @@
heap->extra_data)->iommu_map_all;
break;
case ION_HEAP_TYPE_CARVEOUT:
- mem_is_fmem = ((struct ion_co_heap_pdata *)
- heap->extra_data)->mem_is_fmem;
fixed_position = ((struct ion_co_heap_pdata *)
heap->extra_data)->fixed_position;
adjacent_mem_id = ((struct ion_co_heap_pdata *)
@@ -620,9 +593,6 @@
}
}
- if (mem_is_fmem && adjacent_mem_id != INVALID_HEAP_ID)
- msm8960_fmem_pdata.align = align;
-
if (fixed_position != NOT_FIXED)
fixed_size += heap->size;
else
@@ -634,21 +604,12 @@
fixed_middle_size += heap->size;
else if (fixed_position == FIXED_HIGH)
fixed_high_size += heap->size;
-
- if (mem_is_fmem)
- msm8960_fmem_pdata.size += heap->size;
}
}
if (!fixed_size)
return;
- if (msm8960_fmem_pdata.size) {
- msm8960_fmem_pdata.reserved_size_low = fixed_low_size +
- HOLE_SIZE;
- msm8960_fmem_pdata.reserved_size_high = fixed_high_size;
- }
-
/* Since the fixed area may be carved out of lowmem,
* make sure the length is a multiple of 1M.
*/
@@ -818,19 +779,6 @@
{
msm8960_set_display_params(prim_panel_name, ext_panel_name);
msm_reserve();
- if (msm8960_fmem_pdata.size) {
-#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
- if (reserve_info->fixed_area_size) {
- msm8960_fmem_pdata.phys =
- reserve_info->fixed_area_start + MSM_MM_FW_SIZE;
- pr_info("mm fw at %lx (fixed) size %x\n",
- reserve_info->fixed_area_start, MSM_MM_FW_SIZE);
- pr_info("fmem start %lx (fixed) size %lx\n",
- msm8960_fmem_pdata.phys,
- msm8960_fmem_pdata.size);
- }
-#endif
- }
}
static int msm8960_change_memory_power(u64 start, u64 size,
@@ -1113,6 +1061,27 @@
},
};
+static struct msm_bus_vectors qseecom_enable_dfab_sfpb_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_SPS,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ib = (492 * 8) * 1000000UL,
+ .ab = (492 * 8) * 100000UL,
+ },
+ {
+ .src = MSM_BUS_MASTER_SPS,
+ .dst = MSM_BUS_SLAVE_SPS,
+ .ib = (492 * 8) * 1000000UL,
+ .ab = (492 * 8) * 100000UL,
+ },
+ {
+ .src = MSM_BUS_MASTER_SPDM,
+ .dst = MSM_BUS_SLAVE_SPDM,
+ .ib = (64 * 8) * 1000000UL,
+ .ab = (64 * 8) * 100000UL,
+ },
+};
+
static struct msm_bus_paths qseecom_hw_bus_scale_usecases[] = {
{
ARRAY_SIZE(qseecom_clks_init_vectors),
@@ -1126,6 +1095,10 @@
ARRAY_SIZE(qseecom_enable_sfpb_vectors),
qseecom_enable_sfpb_vectors,
},
+ {
+ ARRAY_SIZE(qseecom_enable_dfab_sfpb_vectors),
+ qseecom_enable_dfab_sfpb_vectors,
+ },
};
static struct msm_bus_scale_pdata qseecom_bus_pdata = {
@@ -2577,6 +2550,76 @@
#endif
#if defined(CONFIG_BT) && defined(CONFIG_BT_HCIUART_ATH3K)
+enum WLANBT_STATUS {
+ WLANOFF_BTOFF = 1,
+ WLANOFF_BTON,
+ WLANON_BTOFF,
+ WLANON_BTON
+};
+
+static DEFINE_MUTEX(ath_wlanbt_mutex);
+static int gpio_wlan_sys_rest_en = 26;
+static int ath_wlanbt_status = WLANOFF_BTOFF;
+
+static int ath6kl_power_control(int on)
+{
+ int rc;
+
+ if (on) {
+ rc = gpio_request(gpio_wlan_sys_rest_en, "wlan sys_rst_n");
+ if (rc) {
+ pr_err("%s: unable to request gpio %d (%d)\n",
+ __func__, gpio_wlan_sys_rest_en, rc);
+ return rc;
+ }
+ rc = gpio_direction_output(gpio_wlan_sys_rest_en, 0);
+ msleep(200);
+ rc = gpio_direction_output(gpio_wlan_sys_rest_en, 1);
+ msleep(100);
+ } else {
+ gpio_set_value(gpio_wlan_sys_rest_en, 0);
+ rc = gpio_direction_input(gpio_wlan_sys_rest_en);
+ msleep(100);
+ gpio_free(gpio_wlan_sys_rest_en);
+ }
+ return 0;
+};
+
+static int ath6kl_wlan_power(int on)
+{
+ int ret = 0;
+
+ mutex_lock(&ath_wlanbt_mutex);
+ if (on) {
+ if (ath_wlanbt_status == WLANOFF_BTOFF) {
+ ret = ath6kl_power_control(1);
+ ath_wlanbt_status = WLANON_BTOFF;
+ } else if (ath_wlanbt_status == WLANOFF_BTON)
+ ath_wlanbt_status = WLANON_BTON;
+ } else {
+ if (ath_wlanbt_status == WLANON_BTOFF) {
+ ret = ath6kl_power_control(0);
+ ath_wlanbt_status = WLANOFF_BTOFF;
+ } else if (ath_wlanbt_status == WLANON_BTON)
+ ath_wlanbt_status = WLANOFF_BTON;
+ }
+ mutex_unlock(&ath_wlanbt_mutex);
+ pr_debug("%s on= %d, wlan_status= %d\n",
+ __func__, on, ath_wlanbt_status);
+ return ret;
+};
+
+static struct wifi_platform_data ath6kl_wifi_control = {
+ .set_power = ath6kl_wlan_power,
+};
+
+static struct platform_device msm_wlan_power_device = {
+ .name = "ath6kl_power",
+ .dev = {
+ .platform_data = &ath6kl_wifi_control,
+ },
+};
+
static struct resource bluesleep_resources[] = {
{
.name = "gpio_host_wake",
@@ -2609,50 +2652,54 @@
.name = "bt_power",
};
-int gpio_bt_sys_rest_en = 28;
+static int gpio_bt_sys_rest_en = 28;
static int bluetooth_power(int on)
{
int rc;
- pr_debug("%s on= %d\n", __func__, on);
-
+ mutex_lock(&ath_wlanbt_mutex);
if (on) {
+ if (ath_wlanbt_status == WLANOFF_BTOFF) {
+ ath6kl_power_control(1);
+ ath_wlanbt_status = WLANOFF_BTON;
+ } else if (ath_wlanbt_status == WLANON_BTOFF)
+ ath_wlanbt_status = WLANON_BTON;
+
rc = gpio_request(gpio_bt_sys_rest_en, "bt sys_rst_n");
if (rc) {
pr_err("%s: unable to request gpio %d (%d)\n",
__func__, gpio_bt_sys_rest_en, rc);
- goto out;
+ mutex_unlock(&ath_wlanbt_mutex);
+ return rc;
}
rc = gpio_direction_output(gpio_bt_sys_rest_en, 0);
- if (rc) {
- pr_err("%s: Unable to set gpio %d direction\n",
- __func__, gpio_bt_sys_rest_en);
- goto free_gpio;
- }
+ msleep(20);
+ rc = gpio_direction_output(gpio_bt_sys_rest_en, 1);
msleep(100);
- gpio_set_value(gpio_bt_sys_rest_en, 1);
- msleep(100);
- goto out;
} else {
gpio_set_value(gpio_bt_sys_rest_en, 0);
rc = gpio_direction_input(gpio_bt_sys_rest_en);
msleep(100);
- }
+ gpio_free(gpio_bt_sys_rest_en);
-free_gpio:
- gpio_free(gpio_bt_sys_rest_en);
-out:
- return rc;
-}
+ if (ath_wlanbt_status == WLANOFF_BTON) {
+ ath6kl_power_control(0);
+ ath_wlanbt_status = WLANOFF_BTOFF;
+ } else if (ath_wlanbt_status == WLANON_BTON)
+ ath_wlanbt_status = WLANON_BTOFF;
+ }
+ mutex_unlock(&ath_wlanbt_mutex);
+ pr_debug("%s on= %d, wlan_status= %d\n",
+ __func__, on, ath_wlanbt_status);
+ return 0;
+};
static void __init bt_power_init(void)
{
- pr_debug("%s enter\n", __func__);
msm_bt_power_device.dev.platform_data = &bluetooth_power;
-
return;
-}
+};
#else
#define bt_power_init(x) do {} while (0)
#endif
@@ -2678,6 +2725,7 @@
#if defined(CONFIG_BT) && defined(CONFIG_BT_HCIUART_ATH3K)
&msm_bluesleep_device,
&msm_bt_power_device,
+ &msm_wlan_power_device,
#endif
#if defined(CONFIG_QSEECOM)
&qseecom_device,
@@ -2728,6 +2776,7 @@
&msm8960_rpm_device,
&msm8960_rpm_log_device,
&msm8960_rpm_stat_device,
+ &msm8960_rpm_master_stat_device,
&msm_device_tz_log,
&coresight_tpiu_device,
&coresight_etb_device,
@@ -2737,8 +2786,6 @@
&msm_device_dspcrashd_8960,
&msm8960_device_watchdog,
&msm8960_rtb_device,
- &msm8960_cpu_idle_device,
- &msm8960_msm_gov_device,
&msm8960_device_cache_erp,
&msm8960_device_ebi1_ch0_erp,
&msm8960_device_ebi1_ch1_erp,
@@ -3257,10 +3304,8 @@
msm8960_pm8921_gpio_mpp_init();
/* Don't add modem devices on APQ targets */
- if (socinfo_get_id() != 124) {
- platform_device_register(&msm_8960_q6_mss_fw);
- platform_device_register(&msm_8960_q6_mss_sw);
- }
+ if (socinfo_get_id() != 124)
+ platform_device_register(&msm_8960_q6_mss);
platform_add_devices(cdp_devices, ARRAY_SIZE(cdp_devices));
msm8960_init_smsc_hub();
msm8960_init_hsic();
diff --git a/arch/arm/mach-msm/board-8974-gpiomux.c b/arch/arm/mach-msm/board-8974-gpiomux.c
index 1577a2b..50b59e1 100644
--- a/arch/arm/mach-msm/board-8974-gpiomux.c
+++ b/arch/arm/mach-msm/board-8974-gpiomux.c
@@ -128,6 +128,12 @@
.dir = GPIOMUX_OUT_LOW,
};
+static struct gpiomux_setting taiko_int = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
static struct msm_gpiomux_config msm_touch_configs[] __initdata = {
{
.gpio = 60, /* TOUCH RESET */
@@ -271,6 +277,30 @@
[GPIOMUX_SUSPENDED] = &gpio_uart_config,
},
},
+ {
+ .gpio = 53, /* BLSP2 QUP4 SPI_DATA_MOSI */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_spi_config,
+ },
+ },
+ {
+ .gpio = 54, /* BLSP2 QUP4 SPI_DATA_MISO */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_spi_config,
+ },
+ },
+ {
+ .gpio = 56, /* BLSP2 QUP4 SPI_CLK */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_spi_config,
+ },
+ },
+ {
+ .gpio = 55, /* BLSP2 QUP4 SPI_CS0_N */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_spi_config,
+ },
+ },
};
static struct msm_gpiomux_config msm8974_slimbus_config[] __initdata = {
@@ -320,6 +350,21 @@
},
};
+static struct gpiomux_setting sd_card_det_config = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+ .dir = GPIOMUX_IN,
+};
+
+static struct msm_gpiomux_config sd_card_det __initdata = {
+ .gpio = 62,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sd_card_det_config,
+ [GPIOMUX_SUSPENDED] = &sd_card_det_config,
+ },
+};
+
static struct msm_gpiomux_config msm_sensor_configs[] __initdata = {
{
.gpio = 15, /* CAM_MCLK0 */
@@ -492,7 +537,13 @@
.settings = {
[GPIOMUX_SUSPENDED] = &taiko_reset,
},
- }
+ },
+ {
+ .gpio = 72, /* CDC_INT */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &taiko_int,
+ },
+ },
};
void __init msm_8974_init_gpiomux(void)
@@ -519,6 +570,8 @@
msm_gpiomux_install(msm_sensor_configs, ARRAY_SIZE(msm_sensor_configs));
+ msm_gpiomux_install(&sd_card_det, 1);
+
msm_gpiomux_install(msm_taiko_config, ARRAY_SIZE(msm_taiko_config));
msm_gpiomux_install(msm_hdmi_configs, ARRAY_SIZE(msm_hdmi_configs));
diff --git a/arch/arm/mach-msm/board-8974.c b/arch/arm/mach-msm/board-8974.c
index dcc0d01..7480437 100644
--- a/arch/arm/mach-msm/board-8974.c
+++ b/arch/arm/mach-msm/board-8974.c
@@ -13,13 +13,11 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/io.h>
-#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
-#include <linux/of_irq.h>
#include <linux/memory.h>
#ifdef CONFIG_ANDROID_PMEM
#include <linux/android_pmem.h>
@@ -29,6 +27,8 @@
#include <linux/msm_thermal.h>
#include <asm/mach/map.h>
#include <asm/hardware/gic.h>
+#include <asm/mach/map.h>
+#include <asm/mach/arch.h>
#include <mach/board.h>
#include <mach/gpiomux.h>
#include <mach/msm_iomap.h>
@@ -37,12 +37,12 @@
#endif
#include <mach/msm_memtypes.h>
#include <mach/msm_smd.h>
+#include <mach/restart.h>
#include <mach/rpm-smd.h>
#include <mach/rpm-regulator-smd.h>
-#include <mach/qpnp-int.h>
#include <mach/socinfo.h>
#include <mach/msm_bus_board.h>
-#include <mach/mpm.h>
+#include "board-dt.h"
#include "clock.h"
#include "devices.h"
#include "spm.h"
@@ -61,7 +61,7 @@
early_param("kernel_ebi1_mem_size", kernel_ebi1_mem_size_setup);
#endif
-static struct memtype_reserve msm_8974_reserve_table[] __initdata = {
+static struct memtype_reserve msm8974_reserve_table[] __initdata = {
[MEMTYPE_SMI] = {
},
[MEMTYPE_EBI0] = {
@@ -72,7 +72,7 @@
},
};
-static int msm_8974_paddr_to_memtype(unsigned int paddr)
+static int msm8974_paddr_to_memtype(unsigned int paddr)
{
return MEMTYPE_EBI1;
}
@@ -80,7 +80,7 @@
static void __init reserve_ebi_memory(void)
{
#ifdef CONFIG_KERNEL_PMEM_EBI_REGION
- msm_8974_reserve_table[MEMTYPE_EBI1].size += kernel_ebi1_mem_size;
+ msm8974_reserve_table[MEMTYPE_EBI1].size += kernel_ebi1_mem_size;
#endif
}
@@ -246,26 +246,21 @@
}
};
-static void __init msm_8974_calculate_reserve_sizes(void)
+static void __init msm8974_calculate_reserve_sizes(void)
{
reserve_ebi_memory();
}
-static struct reserve_info msm_8974_reserve_info __initdata = {
- .memtype_reserve_table = msm_8974_reserve_table,
- .calculate_reserve_sizes = msm_8974_calculate_reserve_sizes,
- .paddr_to_memtype = msm_8974_paddr_to_memtype,
+static struct reserve_info msm8974_reserve_info __initdata = {
+ .memtype_reserve_table = msm8974_reserve_table,
+ .calculate_reserve_sizes = msm8974_calculate_reserve_sizes,
+ .paddr_to_memtype = msm8974_paddr_to_memtype,
};
-static void __init msm_8974_early_memory(void)
+static void __init msm8974_early_memory(void)
{
- reserve_info = &msm_8974_reserve_info;
- of_scan_flat_dt(dt_scan_for_memory_reserve, msm_8974_reserve_table);
-}
-
-void __init msm_8974_reserve(void)
-{
- msm_reserve();
+ reserve_info = &msm8974_reserve_info;
+ of_scan_flat_dt(dt_scan_for_memory_reserve, msm8974_reserve_table);
}
#define BIMC_BASE 0xfc380000
@@ -455,7 +450,7 @@
ARRAY_SIZE(msm_bus_8974_devices));
};
-void __init msm_8974_add_devices(void)
+void __init msm8974_add_devices(void)
{
platform_device_register(&msm_device_smd_8974);
}
@@ -466,7 +461,7 @@
* into this category, and thus the driver should not be added here. The
* EPROBE_DEFER can satisfy most dependency problems.
*/
-void __init msm_8974_add_drivers(void)
+void __init msm8974_add_drivers(void)
{
msm_init_modem_notifier_list();
msm_smd_init();
@@ -484,31 +479,7 @@
mxt_init_vkeys_8974();
}
-static struct of_device_id irq_match[] __initdata = {
- { .compatible = "qcom,msm-qgic2", .data = gic_of_init, },
- { .compatible = "qcom,msm-gpio", .data = msm_gpio_of_init, },
- { .compatible = "qcom,spmi-pmic-arb", .data = qpnpint_of_init, },
- {}
-};
-static struct of_device_id mpm_match[] __initdata = {
- {.compatible = "qcom,mpm-v2", },
- {},
-};
-
-void __init msm_8974_init_irq(void)
-{
- struct device_node *node;
-
- of_irq_init(irq_match);
- node = of_find_matching_node(NULL, mpm_match);
-
- WARN_ON(!node);
-
- if (node)
- of_mpm_init(node);
-}
-
-static struct of_dev_auxdata msm_8974_auxdata_lookup[] __initdata = {
+static struct of_dev_auxdata msm8974_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("qcom,hsusb-otg", 0xF9A55000, \
"msm_otg", NULL),
OF_DEV_AUXDATA("qcom,dwc-usb3-msm", 0xF9200000, \
@@ -575,16 +546,43 @@
{}
};
-void __init msm_8974_init(struct of_dev_auxdata **adata)
+static void __init msm8974_map_io(void)
{
+ msm_map_8974_io();
+ if (socinfo_init() < 0)
+ pr_err("%s: socinfo_init() failed\n", __func__);
+}
+
+void __init msm8974_init(void)
+{
+ struct of_dev_auxdata *adata = msm8974_auxdata_lookup;
+
msm_8974_init_gpiomux();
-
- *adata = msm_8974_auxdata_lookup;
-
regulator_has_full_constraints();
+ of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
+
+ msm8974_add_devices();
+ msm8974_add_drivers();
}
-void __init msm_8974_very_early(void)
+void __init msm8974_init_very_early(void)
{
- msm_8974_early_memory();
+ msm8974_early_memory();
}
+
+static const char *msm8974_dt_match[] __initconst = {
+ "qcom,msm8974",
+ NULL
+};
+
+DT_MACHINE_START(MSM8974_DT, "Qualcomm MSM 8974 (Flattened Device Tree)")
+ .map_io = msm8974_map_io,
+ .init_irq = msm_dt_init_irq,
+ .init_machine = msm8974_init,
+ .handle_irq = gic_handle_irq,
+ .timer = &msm_dt_timer,
+ .dt_compat = msm8974_dt_match,
+ .reserve = msm_reserve,
+ .init_very_early = msm8974_init_very_early,
+ .restart = msm_restart,
+MACHINE_END
diff --git a/arch/arm/mach-msm/board-9615.c b/arch/arm/mach-msm/board-9615.c
index ca95b62..b9da615 100644
--- a/arch/arm/mach-msm/board-9615.c
+++ b/arch/arm/mach-msm/board-9615.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -870,6 +870,8 @@
#ifdef CONFIG_LTC4088_CHARGER
&msm_device_charger,
#endif
+ &msm_9615_q6_lpass,
+ &msm_9615_q6_mss,
&msm_device_otg,
&msm_device_hsic_peripheral,
&msm_device_gadget_peripheral,
@@ -908,6 +910,8 @@
&msm_voip,
&msm_i2s_cpudai0,
&msm_i2s_cpudai1,
+ &msm_i2s_cpudai4,
+ &msm_i2s_cpudai5,
&msm_pcm_hostless,
&msm_cpudai_afe_01_rx,
&msm_cpudai_afe_01_tx,
@@ -933,6 +937,7 @@
&msm_bus_def_fab,
&msm9615_rpm_log_device,
&msm9615_rpm_stat_device,
+ &msm9615_rpm_master_stat_device,
&msm_tsens_device,
};
diff --git a/arch/arm/mach-msm/board-9625-gpiomux.c b/arch/arm/mach-msm/board-9625-gpiomux.c
index fe7670b..c4e174b 100644
--- a/arch/arm/mach-msm/board-9625-gpiomux.c
+++ b/arch/arm/mach-msm/board-9625-gpiomux.c
@@ -76,6 +76,82 @@
};
+static struct gpiomux_setting sdc3_clk_active_cfg = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting sdc3_cmd_active_cfg = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+static struct gpiomux_setting sdc3_data_0_3_active_cfg = {
+ .func = GPIOMUX_FUNC_6,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+static struct gpiomux_setting sdc3_suspended_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+};
+
+static struct gpiomux_setting sdc3_data_1_suspended_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+static struct msm_gpiomux_config sdc3_configs[] __initdata = {
+ {
+ .gpio = 25,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc3_clk_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+ },
+ },
+ {
+ .gpio = 24,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc3_cmd_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+ },
+
+ },
+ {
+ .gpio = 16,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc3_data_0_3_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+ },
+ },
+ {
+ .gpio = 17,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc3_data_0_3_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc3_data_1_suspended_cfg,
+ },
+ },
+ {
+ .gpio = 18,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc3_data_0_3_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+ },
+ },
+ {
+ .gpio = 19,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc3_data_0_3_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+ },
+ },
+};
+
void __init msm9625_init_gpiomux(void)
{
int rc;
@@ -87,4 +163,5 @@
}
msm_gpiomux_install(msm_blsp_configs, ARRAY_SIZE(msm_blsp_configs));
+ msm_gpiomux_install(sdc3_configs, ARRAY_SIZE(sdc3_configs));
}
diff --git a/arch/arm/mach-msm/board-9625.c b/arch/arm/mach-msm/board-9625.c
index 797f5f1..27e91ae 100644
--- a/arch/arm/mach-msm/board-9625.c
+++ b/arch/arm/mach-msm/board-9625.c
@@ -28,10 +28,49 @@
#include <asm/mach/time.h>
#include <mach/socinfo.h>
#include <mach/board.h>
+#include <mach/restart.h>
#include <mach/gpio.h>
#include <mach/clk-provider.h>
#include <mach/qpnp-int.h>
+#include <mach/msm_memtypes.h>
+#include <mach/msm_iomap.h>
+#include <mach/msm_smd.h>
+#include <mach/rpm-smd.h>
+#include <mach/rpm-regulator-smd.h>
+#include <mach/mpm.h>
#include "clock.h"
+#include "modem_notifier.h"
+#include "lpm_resources.h"
+#include "spm.h"
+
+#define MSM_KERNEL_EBI_SIZE 0x51000
+
+static struct memtype_reserve msm9625_reserve_table[] __initdata = {
+ [MEMTYPE_SMI] = {
+ },
+ [MEMTYPE_EBI0] = {
+ .flags = MEMTYPE_FLAGS_1M_ALIGN,
+ },
+ [MEMTYPE_EBI1] = {
+ .flags = MEMTYPE_FLAGS_1M_ALIGN,
+ },
+};
+
+static int msm9625_paddr_to_memtype(unsigned int paddr)
+{
+ return MEMTYPE_EBI1;
+}
+
+static void __init msm9625_calculate_reserve_sizes(void)
+{
+ msm9625_reserve_table[MEMTYPE_EBI1].size += MSM_KERNEL_EBI_SIZE;
+}
+
+static struct reserve_info msm9625_reserve_info __initdata = {
+ .memtype_reserve_table = msm9625_reserve_table,
+ .calculate_reserve_sizes = msm9625_calculate_reserve_sizes,
+ .paddr_to_memtype = msm9625_paddr_to_memtype,
+};
#define L2CC_AUX_CTRL ((0x1 << L2X0_AUX_CTRL_SHARE_OVERRIDE_SHIFT) | \
(0x2 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT) | \
@@ -79,13 +118,29 @@
"spi_qsd.1", NULL),
OF_DEV_AUXDATA("qcom,spmi-pmic-arb", 0xFC4C0000, \
"spmi-pmic-arb.0", NULL),
+ OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF98A4000, \
+ "msm_sdcc.2", NULL),
+ OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF9864000, \
+ "msm_sdcc.3", NULL),
{}
};
+static struct of_device_id mpm_match[] __initdata = {
+ {.compatible = "qcom,mpm-v2", },
+ {},
+};
+
void __init msm9625_init_irq(void)
{
+ struct device_node *node;
l2x0_of_init(L2CC_AUX_CTRL, L2X0_AUX_CTRL_MASK);
of_irq_init(irq_match);
+ node = of_find_matching_node(NULL, mpm_match);
+
+ WARN_ON(!node);
+
+ if (node)
+ of_mpm_init(node);
}
static void __init msm_dt_timer_init(void)
@@ -97,15 +152,178 @@
.init = msm_dt_timer_init
};
+static void __init msm9625_early_memory(void)
+{
+ reserve_info = &msm9625_reserve_info;
+ of_scan_flat_dt(dt_scan_for_memory_reserve, msm9625_reserve_table);
+}
+
+static void __init msm9625_reserve(void)
+{
+ msm_reserve();
+}
+
+static struct resource smd_resource[] = {
+ {
+ .name = "modem_smd_in",
+ .start = 32 + 25, /* mss_sw_to_kpss_ipc_irq0 */
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "modem_smsm_in",
+ .start = 32 + 26, /* mss_sw_to_kpss_ipc_irq1 */
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "adsp_smd_in",
+ .start = 32 + 156, /* lpass_to_kpss_ipc_irq0 */
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "adsp_smsm_in",
+ .start = 32 + 157, /* lpass_to_kpss_ipc_irq1 */
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "rpm_smd_in",
+ .start = 32 + 168, /* rpm_to_kpss_ipc_irq4 */
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct smd_subsystem_config smd_config_list[] = {
+ {
+ .irq_config_id = SMD_MODEM,
+ .subsys_name = "modem",
+ .edge = SMD_APPS_MODEM,
+
+ .smd_int.irq_name = "modem_smd_in",
+ .smd_int.flags = IRQF_TRIGGER_RISING,
+ .smd_int.irq_id = -1,
+ .smd_int.device_name = "smd_dev",
+ .smd_int.dev_id = 0,
+ .smd_int.out_bit_pos = 1 << 12,
+ .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
+ .smd_int.out_offset = 0x8,
+
+ .smsm_int.irq_name = "modem_smsm_in",
+ .smsm_int.flags = IRQF_TRIGGER_RISING,
+ .smsm_int.irq_id = -1,
+ .smsm_int.device_name = "smsm_dev",
+ .smsm_int.dev_id = 0,
+ .smsm_int.out_bit_pos = 1 << 13,
+ .smsm_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
+ .smsm_int.out_offset = 0x8,
+ },
+ {
+ .irq_config_id = SMD_Q6,
+ .subsys_name = "adsp",
+ .edge = SMD_APPS_QDSP,
+
+ .smd_int.irq_name = "adsp_smd_in",
+ .smd_int.flags = IRQF_TRIGGER_RISING,
+ .smd_int.irq_id = -1,
+ .smd_int.device_name = "smd_dev",
+ .smd_int.dev_id = 0,
+ .smd_int.out_bit_pos = 1 << 8,
+ .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
+ .smd_int.out_offset = 0x8,
+
+ .smsm_int.irq_name = "adsp_smsm_in",
+ .smsm_int.flags = IRQF_TRIGGER_RISING,
+ .smsm_int.irq_id = -1,
+ .smsm_int.device_name = "smsm_dev",
+ .smsm_int.dev_id = 0,
+ .smsm_int.out_bit_pos = 1 << 9,
+ .smsm_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
+ .smsm_int.out_offset = 0x8,
+ },
+ {
+ .irq_config_id = SMD_RPM,
+ .subsys_name = NULL, /* do not use PIL to load RPM */
+ .edge = SMD_APPS_RPM,
+
+ .smd_int.irq_name = "rpm_smd_in",
+ .smd_int.flags = IRQF_TRIGGER_RISING,
+ .smd_int.irq_id = -1,
+ .smd_int.device_name = "smd_dev",
+ .smd_int.dev_id = 0,
+ .smd_int.out_bit_pos = 1 << 0,
+ .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
+ .smd_int.out_offset = 0x8,
+
+ .smsm_int.irq_name = NULL, /* RPM does not support SMSM */
+ .smsm_int.flags = 0,
+ .smsm_int.irq_id = 0,
+ .smsm_int.device_name = NULL,
+ .smsm_int.dev_id = 0,
+ .smsm_int.out_bit_pos = 0,
+ .smsm_int.out_base = NULL,
+ .smsm_int.out_offset = 0,
+ },
+};
+
+static struct smd_smem_regions aux_smem_areas[] = {
+ {
+ .phys_addr = (void *)(0xfc428000),
+ .size = 0x4000,
+ },
+};
+
+static struct smd_subsystem_restart_config smd_ssr_cfg = {
+ .disable_smsm_reset_handshake = 1,
+};
+
+static struct smd_platform smd_platform_data = {
+ .num_ss_configs = ARRAY_SIZE(smd_config_list),
+ .smd_ss_configs = smd_config_list,
+ .smd_ssr_config = &smd_ssr_cfg,
+ .num_smem_areas = ARRAY_SIZE(aux_smem_areas),
+ .smd_smem_areas = aux_smem_areas,
+};
+
+struct platform_device msm_device_smd_9625 = {
+ .name = "msm_smd",
+ .id = -1,
+ .resource = smd_resource,
+ .num_resources = ARRAY_SIZE(smd_resource),
+ .dev = {
+ .platform_data = &smd_platform_data,
+ }
+};
+
+void __init msm9625_add_devices(void)
+{
+ platform_device_register(&msm_device_smd_9625);
+}
+
+/*
+ * Used to satisfy dependencies for devices that need to be
+ * run early or in a particular order. Most likely your device doesn't fall
+ * into this category, and thus the driver should not be added here.
+ * EPROBE_DEFER can satisfy most dependency problems.
+ */
+void __init msm9625_add_drivers(void)
+{
+ msm_init_modem_notifier_list();
+ msm_smd_init();
+ msm_rpm_driver_init();
+ msm_lpmrs_module_init();
+ rpm_regulator_smd_driver_init();
+ msm_spm_device_init();
+ msm_clock_init(&msm9625_clock_init_data);
+}
+
void __init msm9625_init(void)
{
if (socinfo_init() < 0)
pr_err("%s: socinfo_init() failed\n", __func__);
msm9625_init_gpiomux();
- msm_clock_init(&msm_dummy_clock_init_data);
of_platform_populate(NULL, of_default_bus_match_table,
msm9625_auxdata_lookup, NULL);
+ msm9625_add_devices();
+ msm9625_add_drivers();
}
DT_MACHINE_START(MSM_DT, "Qualcomm MSM (Flattened Device Tree)")
@@ -115,4 +333,7 @@
.handle_irq = gic_handle_irq,
.timer = &msm_dt_timer,
.dt_compat = msm9625_dt_match,
+ .reserve = msm9625_reserve,
+ .init_very_early = msm9625_early_memory,
+ .restart = msm_restart,
MACHINE_END
diff --git a/arch/arm/mach-msm/board-dt.c b/arch/arm/mach-msm/board-dt.c
index 8f9a0ef..15a544a 100644
--- a/arch/arm/mach-msm/board-dt.c
+++ b/arch/arm/mach-msm/board-dt.c
@@ -10,83 +10,50 @@
* GNU General Public License for more details.
*/
+#include <linux/gpio.h>
#include <linux/kernel.h>
-#include <linux/errno.h>
#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_platform.h>
-#include <linux/of_fdt.h>
#include <linux/of_irq.h>
-#include <asm/hardware/gic.h>
+#include <linux/mfd/wcd9xxx/core.h>
#include <asm/arch_timer.h>
-#include <asm/mach/arch.h>
#include <asm/mach/time.h>
-#include <mach/socinfo.h>
-#include <mach/board.h>
-#include <mach/restart.h>
+#include <asm/hardware/gic.h>
+#include <mach/mpm.h>
+#include <mach/qpnp-int.h>
+
+#include "board-dt.h"
static void __init msm_dt_timer_init(void)
{
arch_timer_of_register();
}
-static struct sys_timer msm_dt_timer = {
+struct sys_timer msm_dt_timer = {
.init = msm_dt_timer_init
};
-static void __init msm_dt_init_irq(void)
-{
- if (machine_is_msm8974())
- msm_8974_init_irq();
-}
-
-static void __init msm_dt_map_io(void)
-{
- if (early_machine_is_msm8974())
- msm_map_8974_io();
- if (socinfo_init() < 0)
- pr_err("%s: socinfo_init() failed\n", __func__);
-}
-
-static void __init msm_dt_init(void)
-{
- struct of_dev_auxdata *adata = NULL;
-
- if (machine_is_msm8974())
- msm_8974_init(&adata);
-
- of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
- if (machine_is_msm8974()) {
- msm_8974_add_devices();
- msm_8974_add_drivers();
- }
-}
-
-static const char *msm_dt_match[] __initconst = {
- "qcom,msm8974",
- NULL
+static struct of_device_id irq_match[] __initdata = {
+ { .compatible = "qcom,msm-qgic2", .data = gic_of_init, },
+ { .compatible = "qcom,msm-gpio", .data = msm_gpio_of_init, },
+ { .compatible = "qcom,spmi-pmic-arb", .data = qpnpint_of_init, },
+ { .compatible = "qcom,wcd9xxx-irq", .data = wcd9xxx_irq_of_init, },
+ {}
};
-static void __init msm_dt_reserve(void)
-{
- if (early_machine_is_msm8974())
- msm_8974_reserve();
-}
+static struct of_device_id mpm_match[] __initdata = {
+ {.compatible = "qcom,mpm-v2", },
+ {}
+};
-static void __init msm_dt_init_very_early(void)
+void __init msm_dt_init_irq(void)
{
- if (early_machine_is_msm8974())
- msm_8974_very_early();
-}
+ struct device_node *node;
-DT_MACHINE_START(MSM_DT, "Qualcomm MSM (Flattened Device Tree)")
- .map_io = msm_dt_map_io,
- .init_irq = msm_dt_init_irq,
- .init_machine = msm_dt_init,
- .handle_irq = gic_handle_irq,
- .timer = &msm_dt_timer,
- .dt_compat = msm_dt_match,
- .reserve = msm_dt_reserve,
- .init_very_early = msm_dt_init_very_early,
- .restart = msm_restart,
-MACHINE_END
+ of_irq_init(irq_match);
+ node = of_find_matching_node(NULL, mpm_match);
+
+ WARN_ON(!node);
+
+ if (node)
+ of_mpm_init(node);
+}
diff --git a/arch/arm/mach-msm/board-dt.h b/arch/arm/mach-msm/board-dt.h
new file mode 100644
index 0000000..31143a5
--- /dev/null
+++ b/arch/arm/mach-msm/board-dt.h
@@ -0,0 +1,14 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+extern struct sys_timer msm_dt_timer;
+void __init msm_dt_init_irq(void);
diff --git a/arch/arm/mach-msm/board-fsm9xxx.c b/arch/arm/mach-msm/board-fsm9xxx.c
index b4f6968..1d6eb01 100644
--- a/arch/arm/mach-msm/board-fsm9xxx.c
+++ b/arch/arm/mach-msm/board-fsm9xxx.c
@@ -941,6 +941,8 @@
static void __init fsm9xxx_init(void)
{
+ msm_clock_init(&fsm9xxx_clock_init_data);
+
regulator_has_full_constraints();
#if defined(CONFIG_I2C_SSBI) || defined(CONFIG_MSM_SSBI)
@@ -977,7 +979,6 @@
{
msm_shared_ram_phys = 0x00100000;
msm_map_fsm9xxx_io();
- msm_clock_init(&fsm9xxx_clock_init_data);
if (socinfo_init() < 0)
pr_err("%s: socinfo_init() failed!\n",
__func__);
diff --git a/arch/arm/mach-msm/board-msm7627a-bt.c b/arch/arm/mach-msm/board-msm7627a-bt.c
index e4edf9b..bcc9645 100644
--- a/arch/arm/mach-msm/board-msm7627a-bt.c
+++ b/arch/arm/mach-msm/board-msm7627a-bt.c
@@ -103,7 +103,8 @@
if (machine_is_msm7627a_qrd1())
gpio_bt_sys_rest_en = 114;
if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
- || machine_is_msm8625_evt())
+ || machine_is_msm8625_evt()
+ || machine_is_qrd_skud_prime())
gpio_bt_sys_rest_en = 16;
if (machine_is_msm8625_qrd7())
gpio_bt_sys_rest_en = 88;
@@ -975,6 +976,8 @@
int i, rc = 0;
struct device *dev;
+ if (machine_is_qrd_skud_prime())
+ return;
gpio_bt_config();
diff --git a/arch/arm/mach-msm/board-msm7627a-camera.c b/arch/arm/mach-msm/board-msm7627a-camera.c
index b5f214b..79ad996 100644
--- a/arch/arm/mach-msm/board-msm7627a-camera.c
+++ b/arch/arm/mach-msm/board-msm7627a-camera.c
@@ -403,7 +403,8 @@
if (machine_is_msm8625_evb() || machine_is_msm7627a_evb()
|| machine_is_msm8625_evt()
|| machine_is_msm7627a_qrd3()
- || machine_is_msm8625_qrd7()) {
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime()) {
sensor_board_info_ov7692.cam_vreg =
ov7692_gpio_vreg;
sensor_board_info_ov7692.num_vreg =
@@ -420,7 +421,8 @@
platform_device_register(&msm_camera_server);
if (machine_is_msm8625_surf() || machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
- || machine_is_msm8625_qrd7()) {
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime()) {
platform_device_register(&msm8625_device_csic0);
platform_device_register(&msm8625_device_csic1);
} else {
@@ -429,7 +431,8 @@
}
if (machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
- || machine_is_msm8625_qrd7())
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime())
*(int *) msm7x27a_device_clkctl.dev.platform_data = 1;
platform_device_register(&msm7x27a_device_clkctl);
platform_device_register(&msm7x27a_device_vfe);
@@ -1175,7 +1178,6 @@
#ifndef CONFIG_MSM_CAMERA_V4L2
int rc;
#endif
-
pr_debug("msm7627a_camera_init Entered\n");
if (machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()) {
@@ -1194,7 +1196,8 @@
if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
|| machine_is_msm7627a_qrd3()
- || machine_is_msm8625_qrd7()) {
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime()) {
#ifndef CONFIG_MSM_CAMERA_V4L2
lcd_camera_power_init();
#endif
@@ -1210,7 +1213,8 @@
|| machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
|| machine_is_msm7627a_qrd3()
- || machine_is_msm8625_qrd7()) {
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime()) {
platform_add_devices(camera_devices_evb,
ARRAY_SIZE(camera_devices_evb));
} else if (machine_is_msm7627a_qrd3())
@@ -1223,7 +1227,8 @@
|| !machine_is_msm8625_evb()
|| !machine_is_msm8625_evt()
|| !machine_is_msm7627a_qrd3()
- || !machine_is_msm8625_qrd7())
+ || !machine_is_msm8625_qrd7()
+ || !machine_is_qrd_skud_prime())
register_i2c_devices();
#ifndef CONFIG_MSM_CAMERA_V4L2
rc = regulator_bulk_get(NULL, ARRAY_SIZE(regs_camera), regs_camera);
@@ -1253,7 +1258,8 @@
|| machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
|| machine_is_msm7627a_qrd3()
- || machine_is_msm8625_qrd7()) {
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime()) {
pr_debug("machine_is_msm7627a_evb i2c_register_board_info\n");
i2c_register_board_info(MSM_GSBI0_QUP_I2C_BUS_ID,
i2c_camera_devices_evb,
diff --git a/arch/arm/mach-msm/board-msm7627a-display.c b/arch/arm/mach-msm/board-msm7627a-display.c
index d62254a..1249c7b 100644
--- a/arch/arm/mach-msm/board-msm7627a-display.c
+++ b/arch/arm/mach-msm/board-msm7627a-display.c
@@ -542,7 +542,8 @@
if (!strncmp(name, "lcdc_truly_hvga_ips3p2335_pt", 28))
ret = 0;
} else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb() ||
- machine_is_msm8625_evt()) {
+ machine_is_msm8625_evt() ||
+ machine_is_qrd_skud_prime()) {
if (!strncmp(name, "mipi_cmd_nt35510_wvga", 21))
ret = 0;
}
@@ -796,7 +797,8 @@
if (machine_is_msm7625a_surf() || machine_is_msm7625a_ffa())
fb_size = MSM7x25A_MSM_FB_SIZE;
else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
- || machine_is_msm8625_evt())
+ || machine_is_msm8625_evt()
+ || machine_is_qrd_skud_prime())
fb_size = MSM8x25_MSM_FB_SIZE;
else
fb_size = MSM_FB_SIZE;
@@ -1017,7 +1019,8 @@
if (machine_is_msm7627a_qrd1())
rc = msm_fb_dsi_client_qrd1_reset();
else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
- || machine_is_msm8625_evt())
+ || machine_is_msm8625_evt()
+ || machine_is_qrd_skud_prime())
rc = msm_fb_dsi_client_qrd3_reset();
else
rc = msm_fb_dsi_client_msm_reset();
@@ -1124,10 +1127,12 @@
wmb();
lcdc_reset_cfg |= 1;
writel_relaxed(lcdc_reset_cfg, lcdc_reset_ptr);
+ msleep(20);
} else {
gpio_set_value_cansleep(GPIO_LCDC_BRDG_RESET_N, 0);
msleep(20);
gpio_set_value_cansleep(GPIO_LCDC_BRDG_RESET_N, 1);
+ msleep(20);
}
} else {
gpio_set_value_cansleep(GPIO_LCDC_BRDG_PD, 1);
@@ -1325,7 +1330,8 @@
if (machine_is_msm7627a_qrd1())
rc = mipi_dsi_panel_qrd1_power(on);
else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
- || machine_is_msm8625_evt())
+ || machine_is_msm8625_evt()
+ || machine_is_qrd_skud_prime())
rc = mipi_dsi_panel_qrd3_power(on);
else
rc = mipi_dsi_panel_msm_power(on);
@@ -1389,7 +1395,8 @@
platform_add_devices(qrd_fb_devices,
ARRAY_SIZE(qrd_fb_devices));
} else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
- || machine_is_msm8625_evt()) {
+ || machine_is_msm8625_evt()
+ || machine_is_qrd_skud_prime()) {
mipi_NT35510_pdata.bl_lock = 1;
mipi_NT35516_pdata.bl_lock = 1;
if (disable_splash)
@@ -1398,7 +1405,10 @@
platform_add_devices(evb_fb_devices,
ARRAY_SIZE(evb_fb_devices));
} else if (machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()) {
- mdp_pdata.cont_splash_enabled = 0x1;
+ if (machine_is_msm7627a_qrd3())
+ mdp_pdata.cont_splash_enabled = 0x0;
+ else
+ mdp_pdata.cont_splash_enabled = 0x1;
platform_add_devices(qrd3_fb_devices,
ARRAY_SIZE(qrd3_fb_devices));
} else {
@@ -1418,7 +1428,8 @@
msm_fb_register_device("mipi_dsi", &mipi_dsi_pdata);
#endif
if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
- || machine_is_msm8625_evt()) {
+ || machine_is_msm8625_evt()
+ || machine_is_qrd_skud_prime()) {
gpio_reg_2p85v = regulator_get(&mipi_dsi_device.dev,
"lcd_vdd");
if (IS_ERR(gpio_reg_2p85v))
diff --git a/arch/arm/mach-msm/board-msm7627a-storage.c b/arch/arm/mach-msm/board-msm7627a-storage.c
index 49ff393..07ff389 100644
--- a/arch/arm/mach-msm/board-msm7627a-storage.c
+++ b/arch/arm/mach-msm/board-msm7627a-storage.c
@@ -378,9 +378,9 @@
if (mmc_regulator_init(1, "mmc", 2850000))
return;
/* 8x25 EVT do not use hw detector */
- if (!(machine_is_msm8625_evt()))
+ if (!((machine_is_msm8625_evt() || machine_is_qrd_skud_prime())))
sdc1_plat_data.status_irq = MSM_GPIO_TO_INT(gpio_sdc1_hw_det);
- if (machine_is_msm8625_evt())
+ if (machine_is_msm8625_evt() || machine_is_qrd_skud_prime())
sdc1_plat_data.status = NULL;
msm_add_sdcc(1, &sdc1_plat_data);
diff --git a/arch/arm/mach-msm/board-msm7627a-wlan.c b/arch/arm/mach-msm/board-msm7627a-wlan.c
index 79f213e..ab29fc5 100644
--- a/arch/arm/mach-msm/board-msm7627a-wlan.c
+++ b/arch/arm/mach-msm/board-msm7627a-wlan.c
@@ -23,6 +23,7 @@
#define GPIO_WLAN_3V3_EN 119
static const char *id = "WLAN";
+static bool wlan_powered_up;
enum {
WLAN_VREG_S3 = 0,
@@ -52,7 +53,8 @@
|| machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
|| machine_is_msm7627a_qrd3()
- || machine_is_msm8625_qrd7())
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime())
gpio_wlan_sys_rest_en = 124;
}
@@ -199,6 +201,11 @@
int rc = 0;
static bool init_done;
+ if (wlan_powered_up) {
+ pr_info("WLAN already powered up\n");
+ return 0;
+ }
+
if (unlikely(!init_done)) {
gpio_wlan_config();
rc = qrf6285_init_regs();
@@ -237,7 +244,8 @@
|| machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
|| machine_is_msm7627a_qrd3()
- || machine_is_msm8625_qrd7()) {
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime()) {
rc = gpio_tlmm_config(GPIO_CFG(gpio_wlan_sys_rest_en, 0,
GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL,
GPIO_CFG_2MA), GPIO_CFG_ENABLE);
@@ -279,13 +287,17 @@
}
pr_info("WLAN power-up success\n");
+ wlan_powered_up = true;
return 0;
set_clock_fail:
setup_wlan_clock(0);
set_gpio_fail:
setup_wlan_gpio(0);
gpio_fail:
- gpio_free(gpio_wlan_sys_rest_en);
+ if (!(machine_is_msm7627a_qrd1() || machine_is_msm7627a_evb() ||
+ machine_is_msm8625_evb() || machine_is_msm8625_evt() ||
+ machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()))
+ gpio_free(gpio_wlan_sys_rest_en);
qrd_gpio_fail:
/* GPIO_WLAN_3V3_EN is only required for the QRD7627a */
if (machine_is_msm7627a_qrd1())
@@ -294,6 +306,7 @@
wlan_switch_regulators(0);
out:
pr_info("WLAN power-up failed\n");
+ wlan_powered_up = false;
return rc;
}
@@ -301,6 +314,11 @@
{
int rc = 0;
+ if (!wlan_powered_up) {
+ pr_info("WLAN is not powered up, returning success\n");
+ return 0;
+ }
+
/* Disable the A0 clock */
rc = setup_wlan_clock(on);
if (rc) {
@@ -316,7 +334,8 @@
|| machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
|| machine_is_msm7627a_qrd3()
- || machine_is_msm8625_qrd7()) {
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime()) {
rc = gpio_tlmm_config(GPIO_CFG(gpio_wlan_sys_rest_en, 0,
GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL,
GPIO_CFG_2MA), GPIO_CFG_ENABLE);
@@ -327,20 +346,12 @@
}
gpio_set_value(gpio_wlan_sys_rest_en, 0);
} else {
- rc = gpio_request(gpio_wlan_sys_rest_en, "WLAN_DEEP_SLEEP_N");
- if (!rc) {
- rc = setup_wlan_gpio(on);
- if (rc) {
- pr_err("%s: setup_wlan_gpio = %d\n",
- __func__, rc);
- goto set_gpio_fail;
- }
- gpio_free(gpio_wlan_sys_rest_en);
- } else {
- pr_err("%s: WLAN sys_rest_en GPIO %d request failed %d\n",
- __func__, gpio_wlan_sys_rest_en, rc);
- goto out;
+ rc = setup_wlan_gpio(on);
+ if (rc) {
+ pr_err("%s: setup_wlan_gpio = %d\n", __func__, rc);
+ goto set_gpio_fail;
}
+ gpio_free(gpio_wlan_sys_rest_en);
}
/* GPIO_WLAN_3V3_EN is only required for the QRD7627a */
@@ -362,7 +373,7 @@
__func__, rc);
goto reg_disable;
}
-
+ wlan_powered_up = false;
pr_info("WLAN power-down success\n");
return 0;
set_clock_fail:
@@ -370,14 +381,16 @@
set_gpio_fail:
setup_wlan_gpio(0);
gpio_fail:
- gpio_free(gpio_wlan_sys_rest_en);
+ if (!(machine_is_msm7627a_qrd1() || machine_is_msm7627a_evb() ||
+ machine_is_msm8625_evb() || machine_is_msm8625_evt() ||
+ machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()))
+ gpio_free(gpio_wlan_sys_rest_en);
qrd_gpio_fail:
/* GPIO_WLAN_3V3_EN is only required for the QRD7627a */
if (machine_is_msm7627a_qrd1())
gpio_free(GPIO_WLAN_3V3_EN);
reg_disable:
wlan_switch_regulators(0);
-out:
pr_info("WLAN power-down failed\n");
return rc;
}
diff --git a/arch/arm/mach-msm/board-msm7x27a.c b/arch/arm/mach-msm/board-msm7x27a.c
index 2a51e66..13c4be2 100644
--- a/arch/arm/mach-msm/board-msm7x27a.c
+++ b/arch/arm/mach-msm/board-msm7x27a.c
@@ -841,7 +841,7 @@
static void __init msm8625_reserve(void)
{
msm7x27a_reserve();
- memblock_remove(MSM8625_SECONDARY_PHYS, SZ_8);
+ memblock_remove(MSM8625_CPU_PHYS, SZ_8);
memblock_remove(MSM8625_WARM_BOOT_PHYS, SZ_32);
}
diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c
index 205db58..ee13e04 100644
--- a/arch/arm/mach-msm/board-msm7x30.c
+++ b/arch/arm/mach-msm/board-msm7x30.c
@@ -5190,7 +5190,7 @@
int bahama_not_marimba = bahama_present();
- if (bahama_not_marimba == -1) {
+ if (bahama_not_marimba < 0) {
printk(KERN_WARNING "%s: bahama_present: %d\n",
__func__, bahama_not_marimba);
return -ENODEV;
diff --git a/arch/arm/mach-msm/board-qrd7627a.c b/arch/arm/mach-msm/board-qrd7627a.c
index ed1fafc..fd322e9 100644
--- a/arch/arm/mach-msm/board-qrd7627a.c
+++ b/arch/arm/mach-msm/board-qrd7627a.c
@@ -32,6 +32,8 @@
#include <linux/msm_adc.h>
#include <linux/regulator/msm-gpio-regulator.h>
#include <linux/msm_ion.h>
+#include <linux/i2c-gpio.h>
+#include <linux/regulator/onsemi-ncp6335d.h>
#include <asm/mach/mmc.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
@@ -128,6 +130,27 @@
.msm_i2c_config_gpio = gsbi_qup_i2c_gpio_config,
};
+static struct msm_gpio i2c_gpio_config[] = {
+ { GPIO_CFG(39, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
+ "qup_scl" },
+ { GPIO_CFG(36, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
+ "qup_sda" },
+};
+
+static struct i2c_gpio_platform_data i2c_gpio_pdata = {
+ .scl_pin = 39,
+ .sda_pin = 36,
+ .udelay = 5, /* 100 Khz */
+};
+
+static struct platform_device msm_i2c_gpio = {
+ .name = "i2c-gpio",
+ .id = 2,
+ .dev = {
+ .platform_data = &i2c_gpio_pdata,
+ }
+};
+
#ifdef CONFIG_ARCH_MSM7X27A
#define MSM_RESERVE_MDP_SIZE 0x1B00000
@@ -597,6 +620,41 @@
},
};
+/* Regulator configuration for the NCP6335D buck */
+struct regulator_consumer_supply ncp6335d_consumer_supplies[] = {
+ REGULATOR_SUPPLY("ncp6335d", NULL),
+};
+
+static struct regulator_init_data ncp6335d_init_data = {
+ .constraints = {
+ .name = "ncp6335d_sw",
+ .min_uV = 600000,
+ .max_uV = 1400000,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS |
+ REGULATOR_CHANGE_MODE,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL |
+ REGULATOR_MODE_FAST,
+ .initial_mode = REGULATOR_MODE_NORMAL,
+ .always_on = 1,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(ncp6335d_consumer_supplies),
+ .consumer_supplies = ncp6335d_consumer_supplies,
+};
+
+static struct ncp6335d_platform_data ncp6335d_pdata = {
+ .init_data = &ncp6335d_init_data,
+ .default_vsel = NCP6335D_VSEL0,
+ .slew_rate_ns = 166,
+};
+
+static struct i2c_board_info i2c2_info[] __initdata = {
+ {
+ I2C_BOARD_INFO("ncp6335d", 0x38 >> 1),
+ .platform_data = &ncp6335d_pdata,
+ },
+};
+
static struct platform_device *common_devices[] __initdata = {
&android_usb_device,
&msm_batt_device,
@@ -651,6 +709,7 @@
&msm8625_device_smd,
&msm8625_gsbi0_qup_i2c_device,
&msm8625_gsbi1_qup_i2c_device,
+ &msm_i2c_gpio, /* TODO: Make this specific to 8625q */
&msm8625_device_uart1,
&msm8625_device_uart_dm1,
&msm8625_device_otg,
@@ -812,7 +871,7 @@
static void __init msm8625_reserve(void)
{
- memblock_remove(MSM8625_SECONDARY_PHYS, SZ_8);
+ memblock_remove(MSM8625_CPU_PHYS, SZ_8);
msm7627a_reserve();
}
@@ -847,10 +906,20 @@
static void __init msm8625_device_i2c_init(void)
{
+ int i, rc;
+
msm8625_gsbi0_qup_i2c_device.dev.platform_data
= &msm_gsbi0_qup_i2c_pdata;
msm8625_gsbi1_qup_i2c_device.dev.platform_data
= &msm_gsbi1_qup_i2c_pdata;
+ if (machine_is_qrd_skud_prime()) {
+ for (i = 0 ; i < ARRAY_SIZE(i2c_gpio_config); i++) {
+ rc = gpio_tlmm_config(i2c_gpio_config[i].gpio_cfg,
+ GPIO_CFG_ENABLE);
+ if (rc)
+ pr_err("I2C-gpio tlmm config failed\n");
+ }
+ }
}
static struct platform_device msm_proccomm_regulator_dev = {
@@ -884,7 +953,8 @@
static void __init add_platform_devices(void)
{
if (machine_is_msm8625_evb() || machine_is_msm8625_qrd7()
- || machine_is_msm8625_evt()) {
+ || machine_is_msm8625_evt()
+ || machine_is_qrd_skud_prime()) {
platform_add_devices(msm8625_evb_devices,
ARRAY_SIZE(msm8625_evb_devices));
platform_add_devices(qrd3_devices,
@@ -899,7 +969,8 @@
ARRAY_SIZE(qrd3_devices));
if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
- || machine_is_msm8625_evt())
+ || machine_is_msm8625_evt()
+ || machine_is_qrd_skud_prime())
platform_add_devices(msm8625_lcd_camera_devices,
ARRAY_SIZE(msm8625_lcd_camera_devices));
else if (machine_is_msm8625_qrd7())
@@ -995,6 +1066,11 @@
msm_pm_register_irqs();
msm_fb_add_devices();
+ if (machine_is_qrd_skud_prime())
+ i2c_register_board_info(2, i2c2_info,
+ ARRAY_SIZE(i2c2_info));
+
+
#if defined(CONFIG_BT) && defined(CONFIG_MARIMBA_CORE)
msm7627a_bt_power_init();
#endif
@@ -1070,3 +1146,13 @@
.init_early = qrd7627a_init_early,
.handle_irq = gic_handle_irq,
MACHINE_END
+MACHINE_START(QRD_SKUD_PRIME, "QRD MSM8625 SKUD PRIME")
+ .atag_offset = 0x100,
+ .map_io = msm8625_map_io,
+ .reserve = msm8625_reserve,
+ .init_irq = msm8625_init_irq,
+ .init_machine = msm_qrd_init,
+ .timer = &msm_timer,
+ .init_early = qrd7627a_init_early,
+ .handle_irq = gic_handle_irq,
+MACHINE_END
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index 2cd2cd4..adf1733 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -4502,6 +4502,7 @@
F_AIF_OSR( 8192000, pll4, 4, 1, 15),
F_AIF_OSR(12288000, pll4, 4, 1, 10),
F_AIF_OSR(24576000, pll4, 4, 1, 5),
+ F_AIF_OSR(27000000, pxo, 1, 0, 0),
F_END
};
@@ -4518,6 +4519,7 @@
F_AIF_OSR( 8192000, pll4, 4, 1, 12),
F_AIF_OSR(12288000, pll4, 4, 1, 8),
F_AIF_OSR(24576000, pll4, 4, 1, 4),
+ F_AIF_OSR(27000000, pxo, 1, 0, 0),
F_END
};
@@ -4543,7 +4545,7 @@
.c = { \
.dbg_name = #i "_clk", \
.ops = &clk_ops_rcg, \
- VDD_DIG_FMAX_MAP1(LOW, 24576000), \
+ VDD_DIG_FMAX_MAP1(LOW, 27000000), \
CLK_INIT(i##_clk.c), \
}, \
}
@@ -4569,7 +4571,7 @@
.c = { \
.dbg_name = #i "_clk", \
.ops = &clk_ops_rcg, \
- VDD_DIG_FMAX_MAP1(LOW, 24576000), \
+ VDD_DIG_FMAX_MAP1(LOW, 27000000), \
CLK_INIT(i##_clk.c), \
}, \
}
@@ -4659,6 +4661,7 @@
F_PCM( 8192000, pll4, 4, 1, 15),
F_PCM(12288000, pll4, 4, 1, 10),
F_PCM(24576000, pll4, 4, 1, 5),
+ F_PCM(27000000, pxo, 1, 0, 0),
F_END
};
@@ -4676,6 +4679,7 @@
F_PCM( 8192000, pll4, 4, 1, 12),
F_PCM(12288000, pll4, 4, 1, 8),
F_PCM(24576000, pll4, 4, 1, 4),
+ F_PCM(27000000, pxo, 1, 0, 0),
F_END
};
@@ -4700,7 +4704,7 @@
.c = {
.dbg_name = "pcm_clk",
.ops = &clk_ops_rcg,
- VDD_DIG_FMAX_MAP1(LOW, 24576000),
+ VDD_DIG_FMAX_MAP1(LOW, 27000000),
CLK_INIT(pcm_clk.c),
.rate = ULONG_MAX,
},
@@ -4727,7 +4731,7 @@
.c = {
.dbg_name = "audio_slimbus_clk",
.ops = &clk_ops_rcg,
- VDD_DIG_FMAX_MAP1(LOW, 24576000),
+ VDD_DIG_FMAX_MAP1(LOW, 27000000),
CLK_INIT(audio_slimbus_clk.c),
},
};
@@ -5216,9 +5220,7 @@
CLK_LOOKUP("pwm_clk", cxo_clk.c, "0-0048"),
CLK_LOOKUP("cxo", cxo_clk.c, "wcnss_wlan.0"),
CLK_LOOKUP("cxo", cxo_clk.c, "pil_riva"),
- CLK_LOOKUP("xo", pxo_clk.c, "pil_qdsp6v4.0"),
- CLK_LOOKUP("xo", cxo_clk.c, "pil_qdsp6v4.1"),
- CLK_LOOKUP("xo", cxo_clk.c, "pil_qdsp6v4.2"),
+ CLK_LOOKUP("xo", pxo_clk.c, "pil-q6v4-lpass"),
CLK_LOOKUP("xo", cxo_clk.c, "pil_gss"),
CLK_LOOKUP("xo", cxo_clk.c, "BAM_RMNT"),
CLK_LOOKUP("xo", cxo_clk.c, "msm_xo"),
@@ -5353,6 +5355,7 @@
CLK_LOOKUP("core_clk", pmic_ssbi2_clk.c, ""),
CLK_LOOKUP("mem_clk", rpm_msg_ram_p_clk.c, ""),
CLK_LOOKUP("cam_clk", cam0_clk.c, "4-001a"),
+ CLK_LOOKUP("cam_clk", cam0_clk.c, "4-0010"),
CLK_LOOKUP("cam_clk", cam0_clk.c, "4-0034"),
CLK_LOOKUP("cam_clk", cam0_clk.c, "4-0020"),
CLK_LOOKUP("cam_clk", cam1_clk.c, "4-0048"),
@@ -5562,9 +5565,8 @@
CLK_LOOKUP("xo", pxo_a_clk.c, ""),
CLK_LOOKUP("cxo", cxo_clk.c, "wcnss_wlan.0"),
CLK_LOOKUP("cxo", cxo_clk.c, "pil_riva"),
- CLK_LOOKUP("xo", pxo_clk.c, "pil_qdsp6v4.0"),
- CLK_LOOKUP("xo", cxo_clk.c, "pil_qdsp6v4.1"),
- CLK_LOOKUP("xo", cxo_clk.c, "pil_qdsp6v4.2"),
+ CLK_LOOKUP("xo", pxo_clk.c, "pil-q6v4-lpass"),
+ CLK_LOOKUP("xo", cxo_clk.c, "pil-q6v4-modem"),
CLK_LOOKUP("xo", cxo_clk.c, "BAM_RMNT"),
CLK_LOOKUP("xo", cxo_clk.c, "msm_xo"),
CLK_LOOKUP("vref_buff", cxo_clk.c, "rpm-regulator"),
@@ -5714,6 +5716,7 @@
CLK_LOOKUP("cam_clk", cam2_clk.c, NULL),
CLK_LOOKUP("cam_clk", cam0_clk.c, "4-0020"),
CLK_LOOKUP("cam_clk", cam0_clk.c, "4-0034"),
+ CLK_LOOKUP("cam_clk", cam0_clk.c, "4-0010"),
CLK_LOOKUP("csi_src_clk", csi0_src_clk.c, "msm_csid.0"),
CLK_LOOKUP("csi_src_clk", csi1_src_clk.c, "msm_csid.1"),
CLK_LOOKUP("csi_src_clk", csi2_src_clk.c, "msm_csid.2"),
@@ -5915,9 +5918,8 @@
CLK_LOOKUP("xo", cxo_clk.c, "msm_xo"),
CLK_LOOKUP("cxo", cxo_clk.c, "wcnss_wlan.0"),
CLK_LOOKUP("cxo", cxo_clk.c, "pil_riva"),
- CLK_LOOKUP("xo", pxo_clk.c, "pil_qdsp6v4.0"),
- CLK_LOOKUP("xo", cxo_clk.c, "pil_qdsp6v4.1"),
- CLK_LOOKUP("xo", cxo_clk.c, "pil_qdsp6v4.2"),
+ CLK_LOOKUP("xo", pxo_clk.c, "pil-q6v4-lpass"),
+ CLK_LOOKUP("xo", cxo_clk.c, "pil-q6v4-modem"),
CLK_LOOKUP("xo", cxo_clk.c, "BAM_RMNT"),
CLK_LOOKUP("vref_buff", cxo_clk.c, "rpm-regulator"),
CLK_LOOKUP("pll2", pll2_clk.c, NULL),
@@ -6708,22 +6710,22 @@
struct clk *mmfpb_a_clk = clk_get_sys("clock-8960", "mmfpb_a_clk");
struct clk *cfpb_a_clk = clk_get_sys("clock-8960", "cfpb_a_clk");
- /* Vote for MMFPB to be at least 76.8MHz when an Apps CPU is active. */
+ /* Vote for MMFPB to be on when Apps is active. */
if (WARN(IS_ERR(mmfpb_a_clk), "mmfpb_a_clk not found (%ld)\n",
PTR_ERR(mmfpb_a_clk)))
return PTR_ERR(mmfpb_a_clk);
- rc = clk_set_rate(mmfpb_a_clk, 76800000);
+ rc = clk_set_rate(mmfpb_a_clk, 38400000);
if (WARN(rc, "mmfpb_a_clk rate was not set (%d)\n", rc))
return rc;
rc = clk_prepare_enable(mmfpb_a_clk);
if (WARN(rc, "mmfpb_a_clk not enabled (%d)\n", rc))
return rc;
- /* Vote for CFPB to be at least 64MHz when an Apps CPU is active. */
+ /* Vote for CFPB to be on when Apps is active. */
if (WARN(IS_ERR(cfpb_a_clk), "cfpb_a_clk not found (%ld)\n",
PTR_ERR(cfpb_a_clk)))
return PTR_ERR(cfpb_a_clk);
- rc = clk_set_rate(cfpb_a_clk, 64000000);
+ rc = clk_set_rate(cfpb_a_clk, 32000000);
if (WARN(rc, "cfpb_a_clk rate was not set (%d)\n", rc))
return rc;
rc = clk_prepare_enable(cfpb_a_clk);
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index 92fdc74..246ceaf 100644
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -632,7 +632,6 @@
#define CXO_ID 0x0
#define QDSS_ID 0x1
-#define RPM_SCALING_ENABLE_ID 0x2
#define PNOC_ID 0x0
#define SNOC_ID 0x1
@@ -789,7 +788,6 @@
static DEFINE_CLK_VOTER(pnoc_sdcc4_clk, &pnoc_clk.c, 0);
static DEFINE_CLK_VOTER(pnoc_sps_clk, &pnoc_clk.c, 0);
-static DEFINE_CLK_VOTER(pnoc_qseecom_clk, &pnoc_clk.c, 0);
static struct clk_freq_tbl ftbl_gcc_usb30_master_clk[] = {
F(125000000, gpll0, 1, 5, 24),
@@ -2933,7 +2931,7 @@
};
static struct clk_freq_tbl ftbl_mdss_edplink_clk[] = {
- F_MDSS(135000000, edppll_270, 2, 0, 0),
+ F_MDSS(162000000, edppll_270, 2, 0, 0),
F_MDSS(270000000, edppll_270, 11, 0, 0),
F_END
};
@@ -2953,7 +2951,7 @@
};
static struct clk_freq_tbl ftbl_mdss_edppixel_clk[] = {
- F_MDSS(175000000, edppll_350, 2, 0, 0),
+ F_MDSS(148500000, edppll_350, 2, 0, 0),
F_MDSS(350000000, edppll_350, 11, 0, 0),
F_END
};
@@ -4679,6 +4677,10 @@
{&gcc_ce1_clk.c, GCC_BASE, 0x0138},
{&gcc_lpass_q6_axi_clk.c, GCC_BASE, 0x0160},
{&gcc_mss_q6_bimc_axi_clk.c, GCC_BASE, 0x0031},
+ {&cnoc_clk.c, GCC_BASE, 0x0008},
+ {&pnoc_clk.c, GCC_BASE, 0x0010},
+ {&snoc_clk.c, GCC_BASE, 0x0000},
+ {&bimc_clk.c, GCC_BASE, 0x0155},
{&mmss_mmssnoc_axi_clk.c, MMSS_BASE, 0x0004},
{&ocmemnoc_clk.c, MMSS_BASE, 0x0007},
{&ocmemcx_ocmemnoc_clk.c, MMSS_BASE, 0x0009},
@@ -5040,6 +5042,7 @@
CLK_LOOKUP("core_clk", gcc_blsp1_uart6_apps_clk.c, ""),
CLK_LOOKUP("iface_clk", gcc_blsp2_ahb_clk.c, "f9967000.i2c"),
+ CLK_LOOKUP("iface_clk", gcc_blsp2_ahb_clk.c, "f9966000.spi"),
CLK_LOOKUP("iface_clk", gcc_blsp2_ahb_clk.c, "f995e000.serial"),
CLK_LOOKUP("core_clk", gcc_blsp2_qup1_i2c_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp2_qup1_spi_apps_clk.c, ""),
@@ -5048,8 +5051,8 @@
CLK_LOOKUP("core_clk", gcc_blsp2_qup3_i2c_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp2_qup3_spi_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp2_qup4_i2c_apps_clk.c, ""),
- CLK_LOOKUP("core_clk", gcc_blsp2_qup4_spi_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp2_qup5_i2c_apps_clk.c, "f9967000.i2c"),
+ CLK_LOOKUP("core_clk", gcc_blsp2_qup4_spi_apps_clk.c, "f9966000.spi"),
CLK_LOOKUP("core_clk", gcc_blsp2_qup5_spi_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp2_qup6_i2c_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp2_qup6_spi_apps_clk.c, ""),
@@ -5078,6 +5081,11 @@
CLK_LOOKUP("bus_clk", gcc_ce2_axi_clk.c, "qcrypto.0"),
CLK_LOOKUP("core_clk_src", ce2_clk_src.c, "qcrypto.0"),
+ CLK_LOOKUP("core_clk", gcc_ce1_clk.c, "qseecom"),
+ CLK_LOOKUP("iface_clk", gcc_ce1_ahb_clk.c, "qseecom"),
+ CLK_LOOKUP("bus_clk", gcc_ce1_axi_clk.c, "qseecom"),
+ CLK_LOOKUP("core_clk_src", ce1_clk_src.c, "qseecom"),
+
CLK_LOOKUP("core_clk", gcc_gp1_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_gp2_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_gp3_clk.c, ""),
@@ -5121,8 +5129,9 @@
/* Multimedia clocks */
CLK_LOOKUP("bus_clk_src", axi_clk_src.c, ""),
CLK_LOOKUP("bus_clk", mmss_mmssnoc_axi_clk.c, ""),
- CLK_LOOKUP("core_clk", mdss_edpaux_clk.c, ""),
- CLK_LOOKUP("core_clk", mdss_edppixel_clk.c, ""),
+ CLK_LOOKUP("core_clk", mdss_edpaux_clk.c, "fd923400.qcom,mdss_edp"),
+ CLK_LOOKUP("pixel_clk", mdss_edppixel_clk.c, "fd923400.qcom,mdss_edp"),
+ CLK_LOOKUP("link_clk", mdss_edplink_clk.c, "fd923400.qcom,mdss_edp"),
CLK_LOOKUP("byte_clk", mdss_byte0_clk.c, "fd922800.qcom,mdss_dsi"),
CLK_LOOKUP("byte_clk", mdss_byte1_clk.c, ""),
CLK_LOOKUP("core_clk", mdss_esc0_clk.c, "fd922800.qcom,mdss_dsi"),
@@ -5282,6 +5291,7 @@
CLK_LOOKUP("core_clk", camss_vfe_cpp_clk.c, "fda44000.qcom,iommu"),
CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "mdp.0"),
CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "mdss_dsi_clk_ctrl"),
+ CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "fd923400.qcom,mdss_edp"),
CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "fd928000.qcom,iommu"),
CLK_LOOKUP("core_clk", mdss_axi_clk.c, "fd928000.qcom,iommu"),
CLK_LOOKUP("bus_clk", mdss_axi_clk.c, "mdp.0"),
@@ -5360,7 +5370,6 @@
CLK_LOOKUP("core_clk", gcc_prng_ahb_clk.c, "msm_rng"),
CLK_LOOKUP("dfab_clk", pnoc_sps_clk.c, "msm_sps"),
- CLK_LOOKUP("bus_clk", pnoc_qseecom_clk.c, "qseecom"),
CLK_LOOKUP("bus_clk", snoc_clk.c, ""),
CLK_LOOKUP("bus_clk", pnoc_clk.c, ""),
@@ -5745,24 +5754,6 @@
#define APCS_GCC_CC_PHYS 0xF9011000
#define APCS_GCC_CC_SIZE SZ_4K
-static void __init enable_rpm_scaling(void)
-{
- int rc, value = 0x1;
- struct msm_rpm_kvp kvp = {
- .key = RPM_SMD_KEY_ENABLE,
- .data = (void *)&value,
- .length = sizeof(value),
- };
-
- rc = msm_rpm_send_message_noirq(MSM_RPM_CTX_SLEEP_SET,
- RPM_MISC_CLK_TYPE, RPM_SCALING_ENABLE_ID, &kvp, 1);
- WARN(rc < 0, "RPM clock scaling (sleep set) did not enable!\n");
-
- rc = msm_rpm_send_message_noirq(MSM_RPM_CTX_ACTIVE_SET,
- RPM_MISC_CLK_TYPE, RPM_SCALING_ENABLE_ID, &kvp, 1);
- WARN(rc < 0, "RPM clock scaling (active set) did not enable!\n");
-}
-
static void __init msm8974_clock_pre_init(void)
{
virt_bases[GCC_BASE] = ioremap(GCC_CC_PHYS, GCC_CC_SIZE);
diff --git a/arch/arm/mach-msm/clock-8x60.c b/arch/arm/mach-msm/clock-8x60.c
index 6ae66fe..bc4bb2e 100644
--- a/arch/arm/mach-msm/clock-8x60.c
+++ b/arch/arm/mach-msm/clock-8x60.c
@@ -2971,6 +2971,7 @@
F_AIF_OSR( 8192000, pll4, 2, 1, 33),
F_AIF_OSR(12288000, pll4, 4, 1, 11),
F_AIF_OSR(24576000, pll4, 2, 1, 11),
+ F_AIF_OSR(27000000, pxo, 1, 0, 0),
F_END
};
@@ -2996,7 +2997,7 @@
.c = { \
.dbg_name = #i "_clk", \
.ops = &clk_ops_rcg, \
- VDD_DIG_FMAX_MAP1(LOW, 24576000), \
+ VDD_DIG_FMAX_MAP1(LOW, 27000000), \
CLK_INIT(i##_clk.c), \
}, \
}
@@ -3065,6 +3066,7 @@
F_PCM( 8192000, pll4, 2, 1, 33),
F_PCM(12288000, pll4, 4, 1, 11),
F_PCM(24580000, pll4, 2, 1, 11),
+ F_PCM(27000000, pxo, 1, 0, 0),
F_END
};
diff --git a/arch/arm/mach-msm/clock-9615.c b/arch/arm/mach-msm/clock-9615.c
index fee8445..035ef5c 100644
--- a/arch/arm/mach-msm/clock-9615.c
+++ b/arch/arm/mach-msm/clock-9615.c
@@ -214,49 +214,6 @@
DEFINE_CLK_RPM_BRANCH(cxo_clk, cxo_a_clk, CXO, 19200000);
-static DEFINE_SPINLOCK(soft_vote_lock);
-
-static int pll_acpu_vote_clk_enable(struct clk *c)
-{
- int ret = 0;
- unsigned long flags;
- struct pll_vote_clk *pllv = to_pll_vote_clk(c);
-
- spin_lock_irqsave(&soft_vote_lock, flags);
-
- if (!*pllv->soft_vote)
- ret = pll_vote_clk_enable(c);
- if (ret == 0)
- *pllv->soft_vote |= (pllv->soft_vote_mask);
-
- spin_unlock_irqrestore(&soft_vote_lock, flags);
- return ret;
-}
-
-static void pll_acpu_vote_clk_disable(struct clk *c)
-{
- unsigned long flags;
- struct pll_vote_clk *pllv = to_pll_vote_clk(c);
-
- spin_lock_irqsave(&soft_vote_lock, flags);
-
- *pllv->soft_vote &= ~(pllv->soft_vote_mask);
- if (!*pllv->soft_vote)
- pll_vote_clk_disable(c);
-
- spin_unlock_irqrestore(&soft_vote_lock, flags);
-}
-
-static struct clk_ops clk_ops_pll_acpu_vote = {
- .enable = pll_acpu_vote_clk_enable,
- .disable = pll_acpu_vote_clk_disable,
- .is_enabled = pll_vote_clk_is_enabled,
- .get_parent = pll_vote_clk_get_parent,
-};
-
-#define PLL_SOFT_VOTE_PRIMARY BIT(0)
-#define PLL_SOFT_VOTE_ACPU BIT(1)
-
static unsigned int soft_vote_pll0;
static struct pll_vote_clk pll0_clk = {
diff --git a/arch/arm/mach-msm/clock-9625.c b/arch/arm/mach-msm/clock-9625.c
new file mode 100644
index 0000000..fb4f32a
--- /dev/null
+++ b/arch/arm/mach-msm/clock-9625.c
@@ -0,0 +1,2417 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/ctype.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/iopoll.h>
+
+#include <mach/clk.h>
+#include <mach/rpm-regulator-smd.h>
+#include <mach/socinfo.h>
+
+#include "clock-local2.h"
+#include "clock-pll.h"
+#include "clock-rpm.h"
+#include "clock-voter.h"
+#include "clock.h"
+
+enum {
+ GCC_BASE,
+ LPASS_BASE,
+ APCS_BASE,
+ APCS_PLL_BASE,
+ N_BASES,
+};
+
+static void __iomem *virt_bases[N_BASES];
+
+#define GCC_REG_BASE(x) (void __iomem *)(virt_bases[GCC_BASE] + (x))
+#define LPASS_REG_BASE(x) (void __iomem *)(virt_bases[LPASS_BASE] + (x))
+#define APCS_REG_BASE(x) (void __iomem *)(virt_bases[APCS_BASE] + (x))
+#define APCS_PLL_REG_BASE(x) (void __iomem *)(virt_bases[APCS_PLL_BASE] + (x))
+
+/* GCC registers */
+#define GPLL0_MODE_REG 0x0000
+#define GPLL0_L_REG 0x0004
+#define GPLL0_M_REG 0x0008
+#define GPLL0_N_REG 0x000C
+#define GPLL0_USER_CTL_REG 0x0010
+#define GPLL0_CONFIG_CTL_REG 0x0014
+#define GPLL0_TEST_CTL_REG 0x0018
+#define GPLL0_STATUS_REG 0x001C
+
+#define GPLL1_MODE_REG 0x0040
+#define GPLL1_L_REG 0x0044
+#define GPLL1_M_REG 0x0048
+#define GPLL1_N_REG 0x004C
+#define GPLL1_USER_CTL_REG 0x0050
+#define GPLL1_CONFIG_CTL_REG 0x0054
+#define GPLL1_TEST_CTL_REG 0x0058
+#define GPLL1_STATUS_REG 0x005C
+
+#define GCC_DEBUG_CLK_CTL_REG 0x1880
+#define CLOCK_FRQ_MEASURE_CTL_REG 0x1884
+#define CLOCK_FRQ_MEASURE_STATUS_REG 0x1888
+#define GCC_PLLTEST_PAD_CFG_REG 0x188C
+#define GCC_XO_DIV4_CBCR_REG 0x10C8
+#define APCS_GPLL_ENA_VOTE_REG 0x1480
+#define APCS_CLOCK_BRANCH_ENA_VOTE 0x1484
+#define APCS_CLOCK_SLEEP_ENA_VOTE 0x1488
+
+#define APCS_CLK_DIAG_REG 0x001C
+
+#define APCS_CPU_PLL_MODE_REG 0x0000
+#define APCS_CPU_PLL_L_REG 0x0004
+#define APCS_CPU_PLL_M_REG 0x0008
+#define APCS_CPU_PLL_N_REG 0x000C
+#define APCS_CPU_PLL_USER_CTL_REG 0x0010
+#define APCS_CPU_PLL_CONFIG_CTL_REG 0x0014
+#define APCS_CPU_PLL_TEST_CTL_REG 0x0018
+#define APCS_CPU_PLL_STATUS_REG 0x001C
+
+#define USB_HSIC_SYSTEM_CMD_RCGR 0x041C
+#define USB_HSIC_XCVR_FS_CMD_RCGR 0x0424
+#define USB_HSIC_CMD_RCGR 0x0440
+#define USB_HSIC_IO_CAL_CMD_RCGR 0x0458
+#define USB_HS_SYSTEM_CMD_RCGR 0x0490
+#define SDCC2_APPS_CMD_RCGR 0x0510
+#define SDCC3_APPS_CMD_RCGR 0x0550
+#define BLSP1_QUP1_SPI_APPS_CMD_RCGR 0x064C
+#define BLSP1_UART1_APPS_CMD_RCGR 0x068C
+#define BLSP1_QUP2_SPI_APPS_CMD_RCGR 0x06CC
+#define BLSP1_UART2_APPS_CMD_RCGR 0x070C
+#define BLSP1_QUP3_SPI_APPS_CMD_RCGR 0x074C
+#define BLSP1_UART3_APPS_CMD_RCGR 0x078C
+#define BLSP1_QUP4_SPI_APPS_CMD_RCGR 0x07CC
+#define BLSP1_UART4_APPS_CMD_RCGR 0x080C
+#define BLSP1_QUP5_SPI_APPS_CMD_RCGR 0x084C
+#define BLSP1_UART5_APPS_CMD_RCGR 0x088C
+#define BLSP1_QUP6_SPI_APPS_CMD_RCGR 0x08CC
+#define BLSP1_UART6_APPS_CMD_RCGR 0x090C
+#define PDM2_CMD_RCGR 0x0CD0
+#define CE1_CMD_RCGR 0x1050
+#define GP1_CMD_RCGR 0x1904
+#define GP2_CMD_RCGR 0x1944
+#define GP3_CMD_RCGR 0x1984
+#define QPIC_CMD_RCGR 0x1A50
+#define IPA_CMD_RCGR 0x1A90
+
+#define USB_HS_HSIC_BCR 0x0400
+#define USB_HS_BCR 0x0480
+#define SDCC2_BCR 0x0500
+#define SDCC3_BCR 0x0540
+#define BLSP1_BCR 0x05C0
+#define BLSP1_QUP1_BCR 0x0640
+#define BLSP1_UART1_BCR 0x0680
+#define BLSP1_QUP2_BCR 0x06C0
+#define BLSP1_UART2_BCR 0x0700
+#define BLSP1_QUP3_BCR 0x0740
+#define BLSP1_UART3_BCR 0x0780
+#define BLSP1_QUP4_BCR 0x07C0
+#define BLSP1_UART4_BCR 0x0800
+#define BLSP1_QUP5_BCR 0x0840
+#define BLSP1_UART5_BCR 0x0880
+#define BLSP1_QUP6_BCR 0x08C0
+#define BLSP1_UART6_BCR 0x0900
+#define PDM_BCR 0x0CC0
+#define PRNG_BCR 0x0D00
+#define BAM_DMA_BCR 0x0D40
+#define BOOT_ROM_BCR 0x0E00
+#define CE1_BCR 0x1040
+#define QPIC_BCR 0x1040
+#define IPA_BCR 0x1A80
+
+
+#define SYS_NOC_IPA_AXI_CBCR 0x0128
+#define USB_HSIC_AHB_CBCR 0x0408
+#define USB_HSIC_SYSTEM_CBCR 0x040C
+#define USB_HSIC_CBCR 0x0410
+#define USB_HSIC_IO_CAL_CBCR 0x0414
+#define USB_HSIC_XCVR_FS_CBCR 0x042C
+#define USB_HS_SYSTEM_CBCR 0x0484
+#define USB_HS_AHB_CBCR 0x0488
+#define SDCC2_APPS_CBCR 0x0504
+#define SDCC2_AHB_CBCR 0x0508
+#define SDCC3_APPS_CBCR 0x0544
+#define SDCC3_AHB_CBCR 0x0548
+#define BLSP1_AHB_CBCR 0x05C4
+#define BLSP1_QUP1_SPI_APPS_CBCR 0x0644
+#define BLSP1_QUP1_I2C_APPS_CBCR 0x0648
+#define BLSP1_UART1_APPS_CBCR 0x0684
+#define BLSP1_UART1_SIM_CBCR 0x0688
+#define BLSP1_QUP2_SPI_APPS_CBCR 0x06C4
+#define BLSP1_QUP2_I2C_APPS_CBCR 0x06C8
+#define BLSP1_UART2_APPS_CBCR 0x0704
+#define BLSP1_UART2_SIM_CBCR 0x0708
+#define BLSP1_QUP3_SPI_APPS_CBCR 0x0744
+#define BLSP1_QUP3_I2C_APPS_CBCR 0x0748
+#define BLSP1_UART3_APPS_CBCR 0x0784
+#define BLSP1_UART3_SIM_CBCR 0x0788
+#define BLSP1_QUP4_SPI_APPS_CBCR 0x07C4
+#define BLSP1_QUP4_I2C_APPS_CBCR 0x07C8
+#define BLSP1_UART4_APPS_CBCR 0x0804
+#define BLSP1_UART4_SIM_CBCR 0x0808
+#define BLSP1_QUP5_SPI_APPS_CBCR 0x0844
+#define BLSP1_QUP5_I2C_APPS_CBCR 0x0848
+#define BLSP1_UART5_APPS_CBCR 0x0884
+#define BLSP1_UART5_SIM_CBCR 0x0888
+#define BLSP1_QUP6_SPI_APPS_CBCR 0x08C4
+#define BLSP1_QUP6_I2C_APPS_CBCR 0x08C8
+#define BLSP1_UART6_APPS_CBCR 0x0904
+#define BLSP1_UART6_SIM_CBCR 0x0908
+#define BOOT_ROM_AHB_CBCR 0x0E04
+#define PDM_AHB_CBCR 0x0CC4
+#define PDM_XO4_CBCR 0x0CC8
+#define PDM_AHB_CBCR 0x0CC4
+#define PDM_XO4_CBCR 0x0CC8
+#define PDM2_CBCR 0x0CCC
+#define PRNG_AHB_CBCR 0x0D04
+#define BAM_DMA_AHB_CBCR 0x0D44
+#define MSG_RAM_AHB_CBCR 0x0E44
+#define CE1_CBCR 0x1044
+#define CE1_AXI_CBCR 0x1048
+#define CE1_AHB_CBCR 0x104C
+#define GCC_AHB_CBCR 0x10C0
+#define GP1_CBCR 0x1900
+#define GP2_CBCR 0x1940
+#define GP3_CBCR 0x1980
+#define QPIC_CBCR 0x1A44
+#define QPIC_AHB_CBCR 0x1A48
+#define IPA_CBCR 0x1A84
+#define IPA_CNOC_CBCR 0x1A88
+#define IPA_SLEEP_CBCR 0x1A8C
+
+/* LPASS registers */
+/* TODO: Needs to double check lpass regiserts after get the SWI for hw */
+#define LPAPLL_MODE_REG 0x0000
+#define LPAPLL_L_REG 0x0004
+#define LPAPLL_M_REG 0x0008
+#define LPAPLL_N_REG 0x000C
+#define LPAPLL_USER_CTL_REG 0x0010
+#define LPAPLL_CONFIG_CTL_REG 0x0014
+#define LPAPLL_TEST_CTL_REG 0x0018
+#define LPAPLL_STATUS_REG 0x001C
+
+#define LPASS_DEBUG_CLK_CTL_REG 0x29000
+#define LPASS_LPA_PLL_VOTE_APPS_REG 0x2000
+
+#define LPAIF_PRI_CMD_RCGR 0xB000
+#define LPAIF_SEC_CMD_RCGR 0xC000
+#define LPAIF_PCM0_CMD_RCGR 0xF000
+#define LPAIF_PCM1_CMD_RCGR 0x10000
+#define SLIMBUS_CMD_RCGR 0x12000
+#define LPAIF_PCMOE_CMD_RCGR 0x13000
+
+#define AUDIO_CORE_BCR 0x4000
+
+#define AUDIO_CORE_GDSCR 0x7000
+#define AUDIO_CORE_LPAIF_PRI_OSR_CBCR 0xB014
+#define AUDIO_CORE_LPAIF_PRI_IBIT_CBCR 0xB018
+#define AUDIO_CORE_LPAIF_PRI_EBIT_CBCR 0xB01C
+#define AUDIO_CORE_LPAIF_SEC_OSR_CBCR 0xC014
+#define AUDIO_CORE_LPAIF_SEC_IBIT_CBCR 0xC018
+#define AUDIO_CORE_LPAIF_SEC_EBIT_CBCR 0xC01C
+#define AUDIO_CORE_LPAIF_PCM0_IBIT_CBCR 0xF014
+#define AUDIO_CORE_LPAIF_PCM0_EBIT_CBCR 0xF018
+#define AUDIO_CORE_LPAIF_PCM1_IBIT_CBCR 0x10014
+#define AUDIO_CORE_LPAIF_PCM1_EBIT_CBCR 0x10018
+#define AUDIO_CORE_RESAMPLER_CORE_CBCR 0x11014
+#define AUDIO_CORE_RESAMPLER_LFABIF_CBCR 0x11018
+#define AUDIO_CORE_SLIMBUS_CORE_CBCR 0x12014
+#define AUDIO_CORE_SLIMBUS_LFABIF_CBCR 0x12018
+#define AUDIO_CORE_LPAIF_PCM_DATA_OE_CBCR 0x13014
+
+/* Mux source select values */
+#define cxo_source_val 0
+#define gpll0_source_val 1
+#define gpll1_hsic_source_val 4
+#define gnd_source_val 5
+#define cxo_lpass_source_val 0
+#define lpapll0_lpass_source_val 1
+#define gpll0_lpass_source_val 5
+
+#define F(f, s, div, m, n) \
+ { \
+ .freq_hz = (f), \
+ .src_clk = &s##_clk_src.c, \
+ .m_val = (m), \
+ .n_val = ~((n)-(m)) * !!(n), \
+ .d_val = ~(n),\
+ .div_src_val = BVAL(4, 0, (int)(2*(div) - 1)) \
+ | BVAL(10, 8, s##_source_val), \
+ }
+
+#define F_HSIC(f, s, div, m, n) \
+ { \
+ .freq_hz = (f), \
+ .src_clk = &s##_clk_src.c, \
+ .m_val = (m), \
+ .n_val = ~((n)-(m)) * !!(n), \
+ .d_val = ~(n),\
+ .div_src_val = BVAL(4, 0, (int)(2*(div) - 1)) \
+ | BVAL(10, 8, s##_hsic_source_val), \
+ }
+
+#define F_LPASS(f, s, div, m, n) \
+ { \
+ .freq_hz = (f), \
+ .src_clk = &s##_clk_src.c, \
+ .m_val = (m), \
+ .n_val = ~((n)-(m)) * !!(n), \
+ .d_val = ~(n),\
+ .div_src_val = BVAL(4, 0, (int)(2*(div) - 1)) \
+ | BVAL(10, 8, s##_lpass_source_val), \
+ }
+
+#define F_APCS_PLL(f, l, m, n, pre_div, post_div, vco) \
+ { \
+ .freq_hz = (f), \
+ .l_val = (l), \
+ .m_val = (m), \
+ .n_val = (n), \
+ .pre_div_val = BVAL(14, 12, (pre_div)), \
+ .post_div_val = BVAL(9, 8, (post_div)), \
+ .vco_val = BVAL(21, 20, (vco)), \
+ }
+
+#define VDD_DIG_FMAX_MAP1(l1, f1) \
+ .vdd_class = &vdd_dig, \
+ .fmax[VDD_DIG_##l1] = (f1)
+#define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \
+ .vdd_class = &vdd_dig, \
+ .fmax[VDD_DIG_##l1] = (f1), \
+ .fmax[VDD_DIG_##l2] = (f2)
+#define VDD_DIG_FMAX_MAP3(l1, f1, l2, f2, l3, f3) \
+ .vdd_class = &vdd_dig, \
+ .fmax[VDD_DIG_##l1] = (f1), \
+ .fmax[VDD_DIG_##l2] = (f2), \
+ .fmax[VDD_DIG_##l3] = (f3)
+
+enum vdd_dig_levels {
+ VDD_DIG_NONE,
+ VDD_DIG_LOW,
+ VDD_DIG_NOMINAL,
+ VDD_DIG_HIGH
+};
+
+static const int vdd_corner[] = {
+ [VDD_DIG_NONE] = RPM_REGULATOR_CORNER_NONE,
+ [VDD_DIG_LOW] = RPM_REGULATOR_CORNER_SVS_SOC,
+ [VDD_DIG_NOMINAL] = RPM_REGULATOR_CORNER_NORMAL,
+ [VDD_DIG_HIGH] = RPM_REGULATOR_CORNER_SUPER_TURBO,
+};
+
+static struct regulator *vdd_dig_reg;
+
+int set_vdd_dig(struct clk_vdd_class *vdd_class, int level)
+{
+ return regulator_set_voltage(vdd_dig_reg, vdd_corner[level],
+ RPM_REGULATOR_CORNER_SUPER_TURBO);
+}
+
+static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig);
+
+/* TODO: Needs to confirm the below values */
+#define RPM_MISC_CLK_TYPE 0x306b6c63
+#define RPM_BUS_CLK_TYPE 0x316b6c63
+#define RPM_MEM_CLK_TYPE 0x326b6c63
+
+#define RPM_SMD_KEY_ENABLE 0x62616E45
+
+#define CXO_ID 0x0
+#define QDSS_ID 0x1
+
+#define PNOC_ID 0x0
+#define SNOC_ID 0x1
+#define CNOC_ID 0x2
+
+#define BIMC_ID 0x0
+
+#define D0_ID 1
+#define D1_ID 2
+#define A0_ID 3
+#define A1_ID 4
+#define A2_ID 5
+
+DEFINE_CLK_RPM_SMD_BRANCH(cxo_clk_src, cxo_a_clk_src,
+ RPM_MISC_CLK_TYPE, CXO_ID, 19200000);
+
+DEFINE_CLK_RPM_SMD(cnoc_clk, cnoc_a_clk, RPM_BUS_CLK_TYPE, CNOC_ID, NULL);
+DEFINE_CLK_RPM_SMD(pnoc_clk, pnoc_a_clk, RPM_BUS_CLK_TYPE, PNOC_ID, NULL);
+DEFINE_CLK_RPM_SMD(snoc_clk, snoc_a_clk, RPM_BUS_CLK_TYPE, SNOC_ID, NULL);
+
+DEFINE_CLK_RPM_SMD(bimc_clk, bimc_a_clk, RPM_MEM_CLK_TYPE, BIMC_ID, NULL);
+
+DEFINE_CLK_RPM_SMD_QDSS(qdss_clk, qdss_a_clk, RPM_MISC_CLK_TYPE, QDSS_ID);
+
+DEFINE_CLK_RPM_SMD_XO_BUFFER(cxo_d0, cxo_d0_a, D0_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER(cxo_d1, cxo_d1_a, D1_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER(cxo_a0, cxo_a0_a, A0_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER(cxo_a1, cxo_a1_a, A1_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER(cxo_a2, cxo_a2_a, A2_ID);
+
+DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(cxo_d0_pin, cxo_d0_a_pin, D0_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(cxo_d1_pin, cxo_d1_a_pin, D1_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(cxo_a0_pin, cxo_a0_a_pin, A0_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(cxo_a1_pin, cxo_a1_a_pin, A1_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(cxo_a2_pin, cxo_a2_a_pin, A2_ID);
+
+static unsigned int soft_vote_gpll0;
+
+static struct pll_vote_clk gpll0_clk_src = {
+ .en_reg = (void __iomem *)APCS_GPLL_ENA_VOTE_REG,
+ .status_reg = (void __iomem *)GPLL0_STATUS_REG,
+ .status_mask = BIT(17),
+ .parent = &cxo_clk_src.c,
+ .soft_vote = &soft_vote_gpll0,
+ .soft_vote_mask = PLL_SOFT_VOTE_PRIMARY,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .rate = 600000000,
+ .dbg_name = "gpll0_clk_src",
+ .ops = &clk_ops_pll_acpu_vote,
+ CLK_INIT(gpll0_clk_src.c),
+ },
+};
+
+static struct pll_vote_clk gpll0_activeonly_clk_src = {
+ .en_reg = (void __iomem *)APCS_GPLL_ENA_VOTE_REG,
+ .status_reg = (void __iomem *)GPLL0_STATUS_REG,
+ .status_mask = BIT(17),
+ .soft_vote = &soft_vote_gpll0,
+ .soft_vote_mask = PLL_SOFT_VOTE_ACPU,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .rate = 600000000,
+ .dbg_name = "gpll0_activeonly_clk_src",
+ .ops = &clk_ops_pll_acpu_vote,
+ CLK_INIT(gpll0_activeonly_clk_src.c),
+ },
+};
+
+static struct pll_vote_clk lpapll0_clk_src = {
+ .en_reg = (void __iomem *)LPASS_LPA_PLL_VOTE_APPS_REG,
+ .en_mask = BIT(0),
+ .status_reg = (void __iomem *)LPAPLL_STATUS_REG,
+ .status_mask = BIT(17),
+ .parent = &cxo_clk_src.c,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .rate = 393216000,
+ .dbg_name = "lpapll0_clk_src",
+ .ops = &clk_ops_pll_vote,
+ CLK_INIT(lpapll0_clk_src.c),
+ },
+};
+
+static struct pll_vote_clk gpll1_clk_src = {
+ .en_reg = (void __iomem *)APCS_GPLL_ENA_VOTE_REG,
+ .en_mask = BIT(1),
+ .status_reg = (void __iomem *)GPLL1_STATUS_REG,
+ .status_mask = BIT(17),
+ .parent = &cxo_clk_src.c,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .rate = 480000000,
+ .dbg_name = "gpll1_clk_src",
+ .ops = &clk_ops_pll_vote,
+ CLK_INIT(gpll1_clk_src.c),
+ },
+};
+
+static struct pll_freq_tbl apcs_pll_freq[] = {
+ F_APCS_PLL(748800000, 0x27, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(998400000, 0x34, 0x0, 0x1, 0x0, 0x0, 0x0),
+ PLL_F_END
+};
+
+/*
+ * Need to skip handoff of the acpu pll to avoid handoff code
+ * to turn off the pll when the acpu is running off this pll.
+ */
+static struct pll_clk apcspll_clk_src = {
+ .mode_reg = (void __iomem *)APCS_CPU_PLL_MODE_REG,
+ .l_reg = (void __iomem *)APCS_CPU_PLL_L_REG,
+ .m_reg = (void __iomem *)APCS_CPU_PLL_M_REG,
+ .n_reg = (void __iomem *)APCS_CPU_PLL_N_REG,
+ .config_reg = (void __iomem *)APCS_CPU_PLL_USER_CTL_REG,
+ .status_reg = (void __iomem *)APCS_CPU_PLL_STATUS_REG,
+ .freq_tbl = apcs_pll_freq,
+ .masks = {
+ .vco_mask = BM(21, 20),
+ .pre_div_mask = BM(14, 12),
+ .post_div_mask = BM(9, 8),
+ .mn_en_mask = BIT(24),
+ .main_output_mask = BIT(0),
+ },
+ .base = &virt_bases[APCS_PLL_BASE],
+ .c = {
+ .dbg_name = "apcspll_clk_src",
+ .ops = &clk_ops_local_pll,
+ CLK_INIT(apcspll_clk_src.c),
+ .flags = CLKFLAG_SKIP_HANDOFF,
+ },
+};
+
+static DEFINE_CLK_VOTER(pnoc_msmbus_clk, &pnoc_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(snoc_msmbus_clk, &snoc_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(cnoc_msmbus_clk, &cnoc_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(pnoc_msmbus_a_clk, &pnoc_a_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(snoc_msmbus_a_clk, &snoc_a_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(cnoc_msmbus_a_clk, &cnoc_a_clk.c, LONG_MAX);
+
+static DEFINE_CLK_VOTER(bimc_msmbus_clk, &bimc_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(bimc_msmbus_a_clk, &bimc_a_clk.c, LONG_MAX);
+
+static DEFINE_CLK_VOTER(pnoc_sdcc2_clk, &pnoc_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(pnoc_sdcc3_clk, &pnoc_clk.c, LONG_MAX);
+
+static DEFINE_CLK_VOTER(pnoc_sps_clk, &pnoc_clk.c, LONG_MAX);
+
+static struct clk_freq_tbl ftbl_gcc_ipa_clk[] = {
+ F( 50000000, gpll0, 12, 0, 0),
+ F( 92310000, gpll0, 6.5, 0, 0),
+ F(100000000, gpll0, 6, 0, 0),
+ F_END
+};
+
+static struct rcg_clk ipa_clk_src = {
+ .cmd_rcgr_reg = IPA_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_ipa_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "ipa_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 50000000, NOMINAL, 100000000),
+ CLK_INIT(ipa_clk_src.c)
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_blsp1_qup1_6_spi_apps_clk[] = {
+ F( 960000, cxo, 10, 1, 2),
+ F( 4800000, cxo, 4, 0, 0),
+ F( 9600000, cxo, 2, 0, 0),
+ F(15000000, gpll0, 10, 1, 4),
+ F(19200000, cxo, 1, 0, 0),
+ F(25000000, gpll0, 12, 1, 2),
+ F(50000000, gpll0, 12, 0, 0),
+ F_END
+};
+
+static struct rcg_clk blsp1_qup1_spi_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_QUP1_SPI_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_qup1_6_spi_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_qup1_spi_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000),
+ CLK_INIT(blsp1_qup1_spi_apps_clk_src.c)
+ },
+};
+
+static struct rcg_clk blsp1_qup2_spi_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_QUP2_SPI_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_qup1_6_spi_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_qup2_spi_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000),
+ CLK_INIT(blsp1_qup2_spi_apps_clk_src.c)
+ },
+};
+
+static struct rcg_clk blsp1_qup3_spi_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_QUP3_SPI_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_qup1_6_spi_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_qup3_spi_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000),
+ CLK_INIT(blsp1_qup3_spi_apps_clk_src.c)
+ },
+};
+
+static struct rcg_clk blsp1_qup4_spi_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_QUP4_SPI_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_qup1_6_spi_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_qup4_spi_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000),
+ CLK_INIT(blsp1_qup4_spi_apps_clk_src.c)
+ },
+};
+
+static struct rcg_clk blsp1_qup5_spi_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_QUP5_SPI_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_qup1_6_spi_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_qup5_spi_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000),
+ CLK_INIT(blsp1_qup5_spi_apps_clk_src.c)
+ },
+};
+
+static struct rcg_clk blsp1_qup6_spi_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_QUP6_SPI_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_qup1_6_spi_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_qup6_spi_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000),
+ CLK_INIT(blsp1_qup6_spi_apps_clk_src.c)
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_blsp1_uart1_6_apps_clk[] = {
+ F( 3686400, gpll0, 1, 96, 15625),
+ F( 7372800, gpll0, 1, 192, 15625),
+ F(14745600, gpll0, 1, 384, 15625),
+ F(16000000, gpll0, 5, 2, 15),
+ F(19200000, cxo, 1, 0, 0),
+ F(24000000, gpll0, 5, 1, 5),
+ F(32000000, gpll0, 1, 4, 75),
+ F(40000000, gpll0, 15, 0, 0),
+ F(46400000, gpll0, 1, 29, 375),
+ F(48000000, gpll0, 12.5, 0, 0),
+ F(51200000, gpll0, 1, 32, 375),
+ F(56000000, gpll0, 1, 7, 75),
+ F(58982400, gpll0, 1, 1536, 15625),
+ F(60000000, gpll0, 10, 0, 0),
+ F_END
+};
+
+static struct rcg_clk blsp1_uart1_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_UART1_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_uart1_6_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_uart1_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000),
+ CLK_INIT(blsp1_uart1_apps_clk_src.c)
+ },
+};
+
+static struct rcg_clk blsp1_uart2_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_UART2_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_uart1_6_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_uart2_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000),
+ CLK_INIT(blsp1_uart2_apps_clk_src.c)
+ },
+};
+
+static struct rcg_clk blsp1_uart3_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_UART3_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_uart1_6_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_uart3_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000),
+ CLK_INIT(blsp1_uart3_apps_clk_src.c)
+ },
+};
+
+static struct rcg_clk blsp1_uart4_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_UART4_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_uart1_6_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_uart4_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000),
+ CLK_INIT(blsp1_uart4_apps_clk_src.c)
+ },
+};
+
+static struct rcg_clk blsp1_uart5_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_UART5_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_uart1_6_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_uart5_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000),
+ CLK_INIT(blsp1_uart5_apps_clk_src.c)
+ },
+};
+
+static struct rcg_clk blsp1_uart6_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_UART6_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_uart1_6_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_uart6_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000),
+ CLK_INIT(blsp1_uart6_apps_clk_src.c)
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_ce1_clk[] = {
+ F( 50000000, gpll0, 12, 0, 0),
+ F(100000000, gpll0, 6, 0, 0),
+ F_END
+};
+
+static struct rcg_clk ce1_clk_src = {
+ .cmd_rcgr_reg = CE1_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_ce1_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "ce1_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP2(LOW, 50000000, NOMINAL, 100000000),
+ CLK_INIT(ce1_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_gp_clk[] = {
+ F(19200000, cxo, 1, 0, 0),
+ F_END
+};
+
+static struct rcg_clk gp1_clk_src = {
+ .cmd_rcgr_reg = GP1_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_gp_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gp1_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000),
+ CLK_INIT(gp1_clk_src.c)
+ },
+};
+
+static struct rcg_clk gp2_clk_src = {
+ .cmd_rcgr_reg = GP2_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_gp_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gp2_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000),
+ CLK_INIT(gp2_clk_src.c)
+ },
+};
+
+static struct rcg_clk gp3_clk_src = {
+ .cmd_rcgr_reg = GP3_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_gp_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gp3_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000),
+ CLK_INIT(gp3_clk_src.c)
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_pdm2_clk[] = {
+ F(60000000, gpll0, 10, 0, 0),
+ F_END
+};
+
+static struct rcg_clk pdm2_clk_src = {
+ .cmd_rcgr_reg = PDM2_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_pdm2_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "pdm2_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP1(LOW, 60000000),
+ CLK_INIT(pdm2_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_qpic_clk[] = {
+ F( 50000000, gpll0, 12, 0, 0),
+ F(100000000, gpll0, 6, 0, 0),
+ F_END
+};
+
+static struct rcg_clk qpic_clk_src = {
+ .cmd_rcgr_reg = QPIC_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_qpic_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "qpic_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 50000000, NOMINAL, 100000000),
+ CLK_INIT(qpic_clk_src.c)
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_sdcc2_apps_clk[] = {
+ F( 144000, cxo, 16, 3, 25),
+ F( 400000, cxo, 12, 1, 4),
+ F( 20000000, gpll0, 15, 1, 2),
+ F( 25000000, gpll0, 12, 1, 2),
+ F( 50000000, gpll0, 12, 0, 0),
+ F(100000000, gpll0, 6, 0, 0),
+ F(200000000, gpll0, 3, 0, 0),
+ F_END
+};
+
+static struct clk_freq_tbl ftbl_gcc_sdcc3_apps_clk[] = {
+ F( 144000, cxo, 16, 3, 25),
+ F( 400000, cxo, 12, 1, 4),
+ F( 20000000, gpll0, 15, 1, 2),
+ F( 25000000, gpll0, 12, 1, 2),
+ F( 50000000, gpll0, 12, 0, 0),
+ F(100000000, gpll0, 6, 0, 0),
+ F_END
+};
+
+static struct rcg_clk sdcc2_apps_clk_src = {
+ .cmd_rcgr_reg = SDCC2_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_sdcc2_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "sdcc2_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000),
+ CLK_INIT(sdcc2_apps_clk_src.c)
+ },
+};
+
+static struct rcg_clk sdcc3_apps_clk_src = {
+ .cmd_rcgr_reg = SDCC3_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_sdcc3_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "sdcc3_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 50000000, NOMINAL, 100000000),
+ CLK_INIT(sdcc3_apps_clk_src.c)
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_usb_hs_system_clk[] = {
+ F(75000000, gpll0, 8, 0, 0),
+ F_END
+};
+
+static struct rcg_clk usb_hs_system_clk_src = {
+ .cmd_rcgr_reg = USB_HS_SYSTEM_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_usb_hs_system_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "usb_hs_system_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP2(LOW, 37500000, NOMINAL, 75000000),
+ CLK_INIT(usb_hs_system_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_usb_hsic_clk[] = {
+ F_HSIC(480000000, gpll1, 1, 0, 0),
+ F_END
+};
+
+static struct rcg_clk usb_hsic_clk_src = {
+ .cmd_rcgr_reg = USB_HSIC_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_usb_hsic_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "usb_hsic_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP1(LOW, 480000000),
+ CLK_INIT(usb_hsic_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_usb_hsic_io_cal_clk[] = {
+ F(9600000, cxo, 2, 0, 0),
+ F_END
+};
+
+static struct rcg_clk usb_hsic_io_cal_clk_src = {
+ .cmd_rcgr_reg = USB_HSIC_IO_CAL_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_usb_hsic_io_cal_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "usb_hsic_io_cal_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP1(LOW, 9600000),
+ CLK_INIT(usb_hsic_io_cal_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_usb_hsic_system_clk[] = {
+ F(75000000, gpll0, 8, 0, 0),
+ F_END
+};
+
+static struct rcg_clk usb_hsic_system_clk_src = {
+ .cmd_rcgr_reg = USB_HSIC_SYSTEM_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_usb_hsic_system_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "usb_hsic_system_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP2(LOW, 60000000, NOMINAL, 75000000),
+ CLK_INIT(usb_hsic_system_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_usb_hsic_xcvr_fs_clk[] = {
+ F(60000000, gpll0, 10, 0, 0),
+ F_END
+};
+
+static struct rcg_clk usb_hsic_xcvr_fs_clk_src = {
+ .cmd_rcgr_reg = USB_HSIC_XCVR_FS_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_usb_hsic_xcvr_fs_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "usb_hsic_xcvr_fs_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP1(LOW, 60000000),
+ CLK_INIT(usb_hsic_xcvr_fs_clk_src.c),
+ },
+};
+
+static struct local_vote_clk gcc_bam_dma_ahb_clk = {
+ .cbcr_reg = BAM_DMA_AHB_CBCR,
+ .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(12),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_bam_dma_ahb_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_bam_dma_ahb_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_blsp1_ahb_clk = {
+ .cbcr_reg = BLSP1_AHB_CBCR,
+ .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(17),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_ahb_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_blsp1_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup1_i2c_apps_clk = {
+ .cbcr_reg = BLSP1_QUP1_I2C_APPS_CBCR,
+ .parent = &cxo_clk_src.c,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup1_i2c_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup1_i2c_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup1_spi_apps_clk = {
+ .cbcr_reg = BLSP1_QUP1_SPI_APPS_CBCR,
+ .parent = &blsp1_qup1_spi_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup1_spi_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup1_spi_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup2_i2c_apps_clk = {
+ .cbcr_reg = BLSP1_QUP2_I2C_APPS_CBCR,
+ .parent = &cxo_clk_src.c,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup2_i2c_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup2_i2c_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup2_spi_apps_clk = {
+ .cbcr_reg = BLSP1_QUP2_SPI_APPS_CBCR,
+ .parent = &blsp1_qup2_spi_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup2_spi_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup2_spi_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup3_i2c_apps_clk = {
+ .cbcr_reg = BLSP1_QUP3_I2C_APPS_CBCR,
+ .parent = &cxo_clk_src.c,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup3_i2c_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup3_i2c_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup3_spi_apps_clk = {
+ .cbcr_reg = BLSP1_QUP3_SPI_APPS_CBCR,
+ .parent = &blsp1_qup3_spi_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup3_spi_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup3_spi_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup4_i2c_apps_clk = {
+ .cbcr_reg = BLSP1_QUP4_I2C_APPS_CBCR,
+ .parent = &cxo_clk_src.c,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup4_i2c_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup4_i2c_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup4_spi_apps_clk = {
+ .cbcr_reg = BLSP1_QUP4_SPI_APPS_CBCR,
+ .parent = &blsp1_qup4_spi_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup4_spi_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup4_spi_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup5_i2c_apps_clk = {
+ .cbcr_reg = BLSP1_QUP5_I2C_APPS_CBCR,
+ .parent = &cxo_clk_src.c,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup5_i2c_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup5_i2c_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup5_spi_apps_clk = {
+ .cbcr_reg = BLSP1_QUP5_SPI_APPS_CBCR,
+ .parent = &blsp1_qup5_spi_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup5_spi_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup5_spi_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup6_i2c_apps_clk = {
+ .cbcr_reg = BLSP1_QUP6_I2C_APPS_CBCR,
+ .parent = &cxo_clk_src.c,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup6_i2c_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup6_i2c_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup6_spi_apps_clk = {
+ .cbcr_reg = BLSP1_QUP6_SPI_APPS_CBCR,
+ .parent = &blsp1_qup6_spi_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup6_spi_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup6_spi_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_uart1_apps_clk = {
+ .cbcr_reg = BLSP1_UART1_APPS_CBCR,
+ .parent = &blsp1_uart1_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_uart1_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_uart1_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_uart2_apps_clk = {
+ .cbcr_reg = BLSP1_UART2_APPS_CBCR,
+ .parent = &blsp1_uart2_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_uart2_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_uart2_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_uart3_apps_clk = {
+ .cbcr_reg = BLSP1_UART3_APPS_CBCR,
+ .parent = &blsp1_uart3_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_uart3_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_uart3_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_uart4_apps_clk = {
+ .cbcr_reg = BLSP1_UART4_APPS_CBCR,
+ .parent = &blsp1_uart4_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_uart4_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_uart4_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_uart5_apps_clk = {
+ .cbcr_reg = BLSP1_UART5_APPS_CBCR,
+ .parent = &blsp1_uart5_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_uart5_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_uart5_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_uart6_apps_clk = {
+ .cbcr_reg = BLSP1_UART6_APPS_CBCR,
+ .parent = &blsp1_uart6_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_uart6_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_uart6_apps_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_boot_rom_ahb_clk = {
+ .cbcr_reg = BOOT_ROM_AHB_CBCR,
+ .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(10),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_boot_rom_ahb_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_boot_rom_ahb_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_ce1_ahb_clk = {
+ .cbcr_reg = CE1_AHB_CBCR,
+ .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(3),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_ce1_ahb_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_ce1_ahb_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_ce1_axi_clk = {
+ .cbcr_reg = CE1_AXI_CBCR,
+ .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(4),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_ce1_axi_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_ce1_axi_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_ce1_clk = {
+ .cbcr_reg = CE1_CBCR,
+ .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(5),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_ce1_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_ce1_clk.c),
+ },
+};
+
+static struct branch_clk gcc_gp1_clk = {
+ .cbcr_reg = GP1_CBCR,
+ .parent = &gp1_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_gp1_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_gp1_clk.c),
+ },
+};
+
+static struct branch_clk gcc_gp2_clk = {
+ .cbcr_reg = GP2_CBCR,
+ .parent = &gp2_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_gp2_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_gp2_clk.c),
+ },
+};
+
+static struct branch_clk gcc_gp3_clk = {
+ .cbcr_reg = GP3_CBCR,
+ .parent = &gp3_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_gp3_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_gp3_clk.c),
+ },
+};
+
+static struct branch_clk gcc_ipa_clk = {
+ .cbcr_reg = IPA_CBCR,
+ .parent = &ipa_clk_src.c,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_ipa_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_ipa_clk.c),
+ },
+};
+
+static struct branch_clk gcc_ipa_cnoc_clk = {
+ .cbcr_reg = IPA_CNOC_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_ipa_cnoc_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_ipa_cnoc_clk.c),
+ },
+};
+
+static struct branch_clk gcc_pdm2_clk = {
+ .cbcr_reg = PDM2_CBCR,
+ .parent = &pdm2_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_pdm2_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_pdm2_clk.c),
+ },
+};
+
+static struct branch_clk gcc_pdm_ahb_clk = {
+ .cbcr_reg = PDM_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_pdm_ahb_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_pdm_ahb_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_prng_ahb_clk = {
+ .cbcr_reg = PRNG_AHB_CBCR,
+ .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(13),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_prng_ahb_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_prng_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_qpic_ahb_clk = {
+ .cbcr_reg = QPIC_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_qpic_ahb_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_qpic_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_qpic_clk = {
+ .cbcr_reg = QPIC_CBCR,
+ .parent = &qpic_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_qpic_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_qpic_clk.c),
+ },
+};
+
+static struct branch_clk gcc_sdcc2_ahb_clk = {
+ .cbcr_reg = SDCC2_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_sdcc2_ahb_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_sdcc2_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_sdcc2_apps_clk = {
+ .cbcr_reg = SDCC2_APPS_CBCR,
+ .parent = &sdcc2_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_sdcc2_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_sdcc2_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_sdcc3_ahb_clk = {
+ .cbcr_reg = SDCC3_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_sdcc3_ahb_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_sdcc3_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_sdcc3_apps_clk = {
+ .cbcr_reg = SDCC3_APPS_CBCR,
+ .parent = &sdcc3_apps_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_sdcc3_apps_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_sdcc3_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_sys_noc_ipa_axi_clk = {
+ .cbcr_reg = SYS_NOC_IPA_AXI_CBCR,
+ .parent = &ipa_clk_src.c,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_sys_noc_ipa_axi_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_sys_noc_ipa_axi_clk.c),
+ },
+};
+
+static struct branch_clk gcc_usb_hs_ahb_clk = {
+ .cbcr_reg = USB_HS_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_usb_hs_ahb_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_usb_hs_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_usb_hs_system_clk = {
+ .cbcr_reg = USB_HS_SYSTEM_CBCR,
+ .bcr_reg = USB_HS_BCR,
+ .parent = &usb_hs_system_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_usb_hs_system_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_usb_hs_system_clk.c),
+ },
+};
+
+static struct branch_clk gcc_usb_hsic_ahb_clk = {
+ .cbcr_reg = USB_HSIC_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_usb_hsic_ahb_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_usb_hsic_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_usb_hsic_clk = {
+ .cbcr_reg = USB_HSIC_CBCR,
+ .parent = &usb_hsic_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_usb_hsic_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_usb_hsic_clk.c),
+ },
+};
+
+static struct branch_clk gcc_usb_hsic_io_cal_clk = {
+ .cbcr_reg = USB_HSIC_IO_CAL_CBCR,
+ .parent = &usb_hsic_io_cal_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_usb_hsic_io_cal_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_usb_hsic_io_cal_clk.c),
+ },
+};
+
+static struct branch_clk gcc_usb_hsic_system_clk = {
+ .cbcr_reg = USB_HSIC_SYSTEM_CBCR,
+ .bcr_reg = USB_HS_HSIC_BCR,
+ .parent = &usb_hsic_system_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_usb_hsic_system_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_usb_hsic_system_clk.c),
+ },
+};
+
+static struct branch_clk gcc_usb_hsic_xcvr_fs_clk = {
+ .cbcr_reg = USB_HSIC_XCVR_FS_CBCR,
+ .parent = &usb_hsic_xcvr_fs_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_usb_hsic_xcvr_fs_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_usb_hsic_xcvr_fs_clk.c),
+ },
+};
+
+/* LPASS clock data */
+static struct clk_freq_tbl ftbl_audio_core_lpaif_clock[] = {
+ F_LPASS( 512000, lpapll0, 16, 1, 48),
+ F_LPASS( 768000, lpapll0, 16, 1, 32),
+ F_LPASS( 1024000, lpapll0, 16, 1, 24),
+ F_LPASS( 1536000, lpapll0, 16, 1, 16),
+ F_LPASS( 2048000, lpapll0, 16, 1, 12),
+ F_LPASS( 3072000, lpapll0, 16, 1, 8),
+ F_LPASS( 4096000, lpapll0, 16, 1, 6),
+ F_LPASS( 6144000, lpapll0, 16, 1, 4),
+ F_LPASS( 8192000, lpapll0, 16, 1, 3),
+ F_LPASS(12288000, lpapll0, 16, 1, 2),
+ F_END
+};
+
+static struct clk_freq_tbl ftbl_audio_core_lpaif_pcm_clock[] = {
+ F_LPASS( 512000, lpapll0, 16, 1, 48),
+ F_LPASS( 768000, lpapll0, 16, 1, 32),
+ F_LPASS( 1024000, lpapll0, 16, 1, 24),
+ F_LPASS( 1536000, lpapll0, 16, 1, 16),
+ F_LPASS( 2048000, lpapll0, 16, 1, 12),
+ F_LPASS( 3072000, lpapll0, 16, 1, 8),
+ F_LPASS( 4096000, lpapll0, 16, 1, 6),
+ F_LPASS( 6144000, lpapll0, 16, 1, 4),
+ F_LPASS( 8192000, lpapll0, 16, 1, 3),
+ F_END
+};
+
+static struct rcg_clk audio_core_lpaif_pcmoe_clk_src = {
+ .cmd_rcgr_reg = LPAIF_PCMOE_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_audio_core_lpaif_clock,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_pcmoe_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP1(LOW, 12288000),
+ CLK_INIT(audio_core_lpaif_pcmoe_clk_src.c)
+ },
+};
+
+static struct rcg_clk audio_core_lpaif_pri_clk_src = {
+ .cmd_rcgr_reg = LPAIF_PRI_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_audio_core_lpaif_clock,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_pri_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 12288000, NOMINAL, 24576000),
+ CLK_INIT(audio_core_lpaif_pri_clk_src.c)
+ },
+};
+
+static struct rcg_clk audio_core_lpaif_sec_clk_src = {
+ .cmd_rcgr_reg = LPAIF_SEC_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_audio_core_lpaif_clock,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_sec_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 12288000, NOMINAL, 24576000),
+ CLK_INIT(audio_core_lpaif_sec_clk_src.c)
+ },
+};
+
+static struct clk_freq_tbl ftbl_audio_core_slimbus_core_clock[] = {
+ F_LPASS(26041000, lpapll0, 1, 10, 151),
+ F_END
+};
+
+static struct rcg_clk audio_core_slimbus_core_clk_src = {
+ .cmd_rcgr_reg = SLIMBUS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_audio_core_slimbus_core_clock,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_slimbus_core_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 13107000, NOMINAL, 26214000),
+ CLK_INIT(audio_core_slimbus_core_clk_src.c)
+ },
+};
+
+static struct rcg_clk audio_core_lpaif_pcm0_clk_src = {
+ .cmd_rcgr_reg = LPAIF_PCM0_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_audio_core_lpaif_pcm_clock,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_pcm0_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 4096000, NOMINAL, 8192000),
+ CLK_INIT(audio_core_lpaif_pcm0_clk_src.c)
+ },
+};
+
+static struct rcg_clk audio_core_lpaif_pcm1_clk_src = {
+ .cmd_rcgr_reg = LPAIF_PCM1_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_audio_core_lpaif_pcm_clock,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_pcm1_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOW, 4096000, NOMINAL, 8192000),
+ CLK_INIT(audio_core_lpaif_pcm1_clk_src.c)
+ },
+};
+
+static struct branch_clk audio_core_slimbus_lfabif_clk = {
+ .cbcr_reg = AUDIO_CORE_SLIMBUS_LFABIF_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_slimbus_lfabif_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_slimbus_lfabif_clk.c),
+ },
+};
+
+static struct branch_clk audio_core_lpaif_pcm_data_oe_clk = {
+ .cbcr_reg = AUDIO_CORE_LPAIF_PCM_DATA_OE_CBCR,
+ .parent = &audio_core_lpaif_pcmoe_clk_src.c,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_pcm_data_oe_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_lpaif_pcm_data_oe_clk.c),
+ },
+};
+
+static struct branch_clk audio_core_slimbus_core_clk = {
+ .cbcr_reg = AUDIO_CORE_SLIMBUS_CORE_CBCR,
+ .parent = &audio_core_slimbus_core_clk_src.c,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_slimbus_core_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_slimbus_core_clk.c),
+ },
+};
+
+static struct branch_clk audio_core_lpaif_pri_ebit_clk = {
+ .cbcr_reg = AUDIO_CORE_LPAIF_PRI_EBIT_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_pri_ebit_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_lpaif_pri_ebit_clk.c),
+ },
+};
+
+static struct branch_clk audio_core_lpaif_pri_ibit_clk = {
+ .cbcr_reg = AUDIO_CORE_LPAIF_PRI_IBIT_CBCR,
+ .parent = &audio_core_lpaif_pri_clk_src.c,
+ .has_sibling = 1,
+ .max_div = 15,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_pri_ibit_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_lpaif_pri_ibit_clk.c),
+ },
+};
+
+static struct branch_clk audio_core_lpaif_pri_osr_clk = {
+ .cbcr_reg = AUDIO_CORE_LPAIF_PRI_OSR_CBCR,
+ .parent = &audio_core_lpaif_pri_clk_src.c,
+ .has_sibling = 1,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_pri_osr_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_lpaif_pri_osr_clk.c),
+ },
+};
+
+static struct branch_clk audio_core_lpaif_pcm0_ebit_clk = {
+ .cbcr_reg = AUDIO_CORE_LPAIF_PCM0_EBIT_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_pcm0_ebit_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_lpaif_pcm0_ebit_clk.c),
+ },
+};
+
+static struct branch_clk audio_core_lpaif_pcm0_ibit_clk = {
+ .cbcr_reg = AUDIO_CORE_LPAIF_PCM0_IBIT_CBCR,
+ .parent = &audio_core_lpaif_pcm0_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_pcm0_ibit_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_lpaif_pcm0_ibit_clk.c),
+ },
+};
+
+static struct branch_clk audio_core_lpaif_sec_ebit_clk = {
+ .cbcr_reg = AUDIO_CORE_LPAIF_SEC_EBIT_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_sec_ebit_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_lpaif_sec_ebit_clk.c),
+ },
+};
+
+static struct branch_clk audio_core_lpaif_sec_ibit_clk = {
+ .cbcr_reg = AUDIO_CORE_LPAIF_SEC_IBIT_CBCR,
+ .parent = &audio_core_lpaif_sec_clk_src.c,
+ .has_sibling = 1,
+ .max_div = 15,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_sec_ibit_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_lpaif_sec_ibit_clk.c),
+ },
+};
+
+static struct branch_clk audio_core_lpaif_sec_osr_clk = {
+ .cbcr_reg = AUDIO_CORE_LPAIF_SEC_OSR_CBCR,
+ .parent = &audio_core_lpaif_sec_clk_src.c,
+ .has_sibling = 1,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_sec_osr_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_lpaif_sec_osr_clk.c),
+ },
+};
+
+static struct branch_clk audio_core_lpaif_pcm1_ebit_clk = {
+ .cbcr_reg = AUDIO_CORE_LPAIF_PCM1_EBIT_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_pcm1_ebit_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_lpaif_pcm1_ebit_clk.c),
+ },
+};
+
+static struct branch_clk audio_core_lpaif_pcm1_ibit_clk = {
+ .cbcr_reg = AUDIO_CORE_LPAIF_PCM1_IBIT_CBCR,
+ .parent = &audio_core_lpaif_pcm1_clk_src.c,
+ .has_sibling = 0,
+ .base = &virt_bases[LPASS_BASE],
+ .c = {
+ .dbg_name = "audio_core_lpaif_pcm1_ibit_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(audio_core_lpaif_pcm1_ibit_clk.c),
+ },
+};
+
+static DEFINE_CLK_MEASURE(a5_m_clk);
+
+#ifdef CONFIG_DEBUG_FS
+
+struct measure_mux_entry {
+ struct clk *c;
+ int base;
+ u32 debug_mux;
+};
+
+struct measure_mux_entry measure_mux[] = {
+ {&gcc_pdm_ahb_clk.c, GCC_BASE, 0x00d0},
+ {&gcc_usb_hsic_xcvr_fs_clk.c, GCC_BASE, 0x005d},
+ {&gcc_usb_hsic_system_clk.c, GCC_BASE, 0x0059},
+ {&gcc_usb_hsic_io_cal_clk.c, GCC_BASE, 0x005b},
+ {&gcc_sdcc3_ahb_clk.c, GCC_BASE, 0x0079},
+ {&gcc_blsp1_qup5_i2c_apps_clk.c, GCC_BASE, 0x009d},
+ {&gcc_blsp1_qup1_spi_apps_clk.c, GCC_BASE, 0x008a},
+ {&gcc_blsp1_uart2_apps_clk.c, GCC_BASE, 0x0091},
+ {&gcc_blsp1_qup4_spi_apps_clk.c, GCC_BASE, 0x0098},
+ {&gcc_blsp1_qup3_spi_apps_clk.c, GCC_BASE, 0x0093},
+ {&gcc_blsp1_qup6_i2c_apps_clk.c, GCC_BASE, 0x00a2},
+ {&gcc_bam_dma_ahb_clk.c, GCC_BASE, 0x00e0},
+ {&gcc_sdcc3_apps_clk.c, GCC_BASE, 0x0078},
+ {&gcc_usb_hs_system_clk.c, GCC_BASE, 0x0060},
+ {&gcc_blsp1_ahb_clk.c, GCC_BASE, 0x0088},
+ {&gcc_blsp1_uart4_apps_clk.c, GCC_BASE, 0x009a},
+ {&gcc_blsp1_qup2_spi_apps_clk.c, GCC_BASE, 0x008e},
+ {&gcc_usb_hsic_ahb_clk.c, GCC_BASE, 0x0058},
+ {&gcc_blsp1_uart3_apps_clk.c, GCC_BASE, 0x0095},
+ {&gcc_ce1_axi_clk.c, GCC_BASE, 0x0139},
+ {&gcc_blsp1_qup5_spi_apps_clk.c, GCC_BASE, 0x009c},
+ {&gcc_usb_hs_ahb_clk.c, GCC_BASE, 0x0061},
+ {&gcc_blsp1_qup6_spi_apps_clk.c, GCC_BASE, 0x00a1},
+ {&gcc_prng_ahb_clk.c, GCC_BASE, 0x00d8},
+ {&gcc_blsp1_qup3_i2c_apps_clk.c, GCC_BASE, 0x0094},
+ {&gcc_usb_hsic_clk.c, GCC_BASE, 0x005a},
+ {&gcc_blsp1_uart6_apps_clk.c, GCC_BASE, 0x00a3},
+ {&gcc_sdcc2_apps_clk.c, GCC_BASE, 0x0070},
+ {&gcc_blsp1_uart1_apps_clk.c, GCC_BASE, 0x008c},
+ {&gcc_blsp1_qup4_i2c_apps_clk.c, GCC_BASE, 0x0099},
+ {&gcc_boot_rom_ahb_clk.c, GCC_BASE, 0x00f8},
+ {&gcc_ce1_ahb_clk.c, GCC_BASE, 0x013a},
+ {&gcc_pdm2_clk.c, GCC_BASE, 0x00d2},
+ {&gcc_blsp1_uart5_apps_clk.c, GCC_BASE, 0x009e},
+ {&gcc_blsp1_qup2_i2c_apps_clk.c, GCC_BASE, 0x0090},
+ {&gcc_blsp1_qup1_i2c_apps_clk.c, GCC_BASE, 0x008b},
+ {&gcc_sdcc2_ahb_clk.c, GCC_BASE, 0x0071},
+ {&gcc_ce1_clk.c, GCC_BASE, 0x0138},
+ {&gcc_sys_noc_ipa_axi_clk.c, GCC_BASE, 0x0007},
+
+ {&audio_core_lpaif_pcm_data_oe_clk.c, LPASS_BASE, 0x0030},
+ {&audio_core_slimbus_core_clk.c, LPASS_BASE, 0x003d},
+ {&audio_core_lpaif_pri_clk_src.c, LPASS_BASE, 0x0017},
+ {&audio_core_lpaif_sec_clk_src.c, LPASS_BASE, 0x0016},
+ {&audio_core_slimbus_core_clk_src.c, LPASS_BASE, 0x0011},
+ {&audio_core_lpaif_pcm1_clk_src.c, LPASS_BASE, 0x0012},
+ {&audio_core_lpaif_pcm0_clk_src.c, LPASS_BASE, 0x0013},
+ {&audio_core_lpaif_pcmoe_clk_src.c, LPASS_BASE, 0x000f},
+ {&audio_core_slimbus_lfabif_clk.c, LPASS_BASE, 0x003e},
+
+ {&a5_m_clk, APCS_BASE, 0x3},
+
+ {&dummy_clk, N_BASES, 0x0000},
+};
+
+static int measure_clk_set_parent(struct clk *c, struct clk *parent)
+{
+ struct measure_clk *clk = to_measure_clk(c);
+ unsigned long flags;
+ u32 regval, clk_sel, i;
+
+ if (!parent)
+ return -EINVAL;
+
+ for (i = 0; i < (ARRAY_SIZE(measure_mux) - 1); i++)
+ if (measure_mux[i].c == parent)
+ break;
+
+ if (measure_mux[i].c == &dummy_clk)
+ return -EINVAL;
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ /*
+ * Program the test vector, measurement period (sample_ticks)
+ * and scaling multiplier.
+ */
+ clk->sample_ticks = 0x10000;
+ clk->multiplier = 1;
+
+ writel_relaxed(0, LPASS_REG_BASE(LPASS_DEBUG_CLK_CTL_REG));
+ writel_relaxed(0, GCC_REG_BASE(GCC_DEBUG_CLK_CTL_REG));
+
+ switch (measure_mux[i].base) {
+
+ case GCC_BASE:
+ clk_sel = measure_mux[i].debug_mux;
+ break;
+
+ case LPASS_BASE:
+ clk_sel = 0x161;
+ regval = BVAL(15, 0, measure_mux[i].debug_mux);
+ writel_relaxed(regval, LPASS_REG_BASE(LPASS_DEBUG_CLK_CTL_REG));
+
+ /* Activate debug clock output */
+ regval |= BIT(20);
+ writel_relaxed(regval, LPASS_REG_BASE(LPASS_DEBUG_CLK_CTL_REG));
+ break;
+
+ case APCS_BASE:
+ clk_sel = 0x16A;
+ regval = BVAL(5, 3, measure_mux[i].debug_mux);
+ writel_relaxed(regval, APCS_REG_BASE(APCS_CLK_DIAG_REG));
+
+ /* Activate debug clock output */
+ regval |= BIT(7);
+ writel_relaxed(regval, APCS_REG_BASE(APCS_CLK_DIAG_REG));
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* Set debug mux clock index */
+ regval = BVAL(8, 0, clk_sel);
+ writel_relaxed(regval, GCC_REG_BASE(GCC_DEBUG_CLK_CTL_REG));
+
+ /* Activate debug clock output */
+ regval |= BIT(16);
+ writel_relaxed(regval, GCC_REG_BASE(GCC_DEBUG_CLK_CTL_REG));
+
+ /* Make sure test vector is set before starting measurements. */
+ mb();
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+
+ return 0;
+}
+
+/* Sample clock for 'ticks' reference clock ticks. */
+static u32 run_measurement(unsigned ticks)
+{
+ /* Stop counters and set the XO4 counter start value. */
+ writel_relaxed(ticks, GCC_REG_BASE(CLOCK_FRQ_MEASURE_CTL_REG));
+
+ /* Wait for timer to become ready. */
+ while ((readl_relaxed(GCC_REG_BASE(CLOCK_FRQ_MEASURE_STATUS_REG)) &
+ BIT(25)) != 0)
+ cpu_relax();
+
+ /* Run measurement and wait for completion. */
+ writel_relaxed(BIT(20)|ticks, GCC_REG_BASE(CLOCK_FRQ_MEASURE_CTL_REG));
+ while ((readl_relaxed(GCC_REG_BASE(CLOCK_FRQ_MEASURE_STATUS_REG)) &
+ BIT(25)) == 0)
+ cpu_relax();
+
+ /* Return measured ticks. */
+ return readl_relaxed(GCC_REG_BASE(CLOCK_FRQ_MEASURE_STATUS_REG)) &
+ BM(24, 0);
+}
+
+/*
+ * Perform a hardware rate measurement for a given clock.
+ * FOR DEBUG USE ONLY: Measurements take ~15 ms!
+ */
+static unsigned long measure_clk_get_rate(struct clk *c)
+{
+ unsigned long flags;
+ u32 gcc_xo4_reg_backup;
+ u64 raw_count_short, raw_count_full;
+ struct measure_clk *clk = to_measure_clk(c);
+ unsigned ret;
+
+ ret = clk_prepare_enable(&cxo_clk_src.c);
+ if (ret) {
+ pr_warning("CXO clock failed to enable. Can't measure\n");
+ return 0;
+ }
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+
+ /* Enable CXO/4 and RINGOSC branch. */
+ gcc_xo4_reg_backup = readl_relaxed(GCC_REG_BASE(GCC_XO_DIV4_CBCR_REG));
+ writel_relaxed(0x1, GCC_REG_BASE(GCC_XO_DIV4_CBCR_REG));
+
+ /*
+ * The ring oscillator counter will not reset if the measured clock
+ * is not running. To detect this, run a short measurement before
+ * the full measurement. If the raw results of the two are the same
+ * then the clock must be off.
+ */
+
+ /* Run a short measurement. (~1 ms) */
+ raw_count_short = run_measurement(0x1000);
+ /* Run a full measurement. (~14 ms) */
+ raw_count_full = run_measurement(clk->sample_ticks);
+
+ writel_relaxed(gcc_xo4_reg_backup, GCC_REG_BASE(GCC_XO_DIV4_CBCR_REG));
+
+ /* Return 0 if the clock is off. */
+ if (raw_count_full == raw_count_short) {
+ ret = 0;
+ } else {
+ /* Compute rate in Hz. */
+ raw_count_full = ((raw_count_full * 10) + 15) * 4800000;
+ do_div(raw_count_full, ((clk->sample_ticks * 10) + 35));
+ ret = (raw_count_full * clk->multiplier);
+ }
+
+ writel_relaxed(0x51A00, GCC_REG_BASE(GCC_PLLTEST_PAD_CFG_REG));
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+
+ clk_disable_unprepare(&cxo_clk_src.c);
+
+ return ret;
+}
+#else /* !CONFIG_DEBUG_FS */
+static int measure_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ return -EINVAL;
+}
+
+static unsigned long measure_clk_get_rate(struct clk *clk)
+{
+ return 0;
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static struct clk_ops clk_ops_measure = {
+ .set_parent = measure_clk_set_parent,
+ .get_rate = measure_clk_get_rate,
+};
+
+static struct measure_clk measure_clk = {
+ .c = {
+ .dbg_name = "measure_clk",
+ .ops = &clk_ops_measure,
+ CLK_INIT(measure_clk.c),
+ },
+ .multiplier = 1,
+};
+
+static struct clk_lookup msm_clocks_9625[] = {
+ CLK_LOOKUP("xo", cxo_clk_src.c, ""),
+ CLK_LOOKUP("measure", measure_clk.c, "debug"),
+
+ CLK_LOOKUP("pll0", gpll0_activeonly_clk_src.c, "f9010008.qcom,acpuclk"),
+ CLK_LOOKUP("pll14", apcspll_clk_src.c, "f9010008.qcom,acpuclk"),
+
+ CLK_LOOKUP("dma_bam_pclk", gcc_bam_dma_ahb_clk.c, "msm_sps"),
+ CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "msm_serial_hsl.0"),
+ CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "spi_qsd.1"),
+ CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "f9925000.i2c"),
+ CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup1_i2c_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup1_spi_apps_clk.c, "spi_qsd.1"),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup2_i2c_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup2_spi_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup3_i2c_apps_clk.c, "f9925000.i2c"),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup3_spi_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup4_i2c_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup4_spi_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup5_i2c_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup5_spi_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup6_i2c_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup6_spi_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_uart1_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_uart2_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_uart3_apps_clk.c, "msm_serial_hsl.0"),
+ CLK_LOOKUP("core_clk", gcc_blsp1_uart4_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_uart5_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_uart6_apps_clk.c, ""),
+
+ CLK_LOOKUP("core_clk_src", ce1_clk_src.c, ""),
+ CLK_LOOKUP("core_clk", gcc_ce1_clk.c, ""),
+ CLK_LOOKUP("iface_clk", gcc_ce1_ahb_clk.c, ""),
+ CLK_LOOKUP("bus_clk", gcc_ce1_axi_clk.c, ""),
+
+ CLK_LOOKUP("core_clk", gcc_gp1_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_gp2_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_gp3_clk.c, ""),
+
+ CLK_LOOKUP("core_src_clk", ipa_clk_src.c, "fd4c0000.qcom,ipa"),
+ CLK_LOOKUP("core_clk", gcc_ipa_clk.c, "fd4c0000.qcom,ipa"),
+ CLK_LOOKUP("bus_clk", gcc_sys_noc_ipa_axi_clk.c, "fd4c0000.qcom,ipa"),
+ CLK_LOOKUP("iface_clk", gcc_ipa_cnoc_clk.c, "fd4c0000.qcom,ipa"),
+
+ CLK_LOOKUP("core_clk", gcc_pdm2_clk.c, ""),
+ CLK_LOOKUP("iface_clk", gcc_pdm_ahb_clk.c, ""),
+
+ CLK_LOOKUP("iface_clk", gcc_sdcc2_ahb_clk.c, "msm_sdcc.2"),
+ CLK_LOOKUP("core_clk", gcc_sdcc2_apps_clk.c, "msm_sdcc.2"),
+ CLK_LOOKUP("bus_clk", pnoc_sdcc2_clk.c, "msm_sdcc.2"),
+ CLK_LOOKUP("iface_clk", gcc_sdcc3_ahb_clk.c, "msm_sdcc.3"),
+ CLK_LOOKUP("core_clk", gcc_sdcc3_apps_clk.c, "msm_sdcc.3"),
+ CLK_LOOKUP("bus_clk", pnoc_sdcc3_clk.c, "msm_sdcc.3"),
+
+ CLK_LOOKUP("iface_clk", gcc_usb_hs_ahb_clk.c, "f9a55000.usb"),
+ CLK_LOOKUP("core_clk", gcc_usb_hs_system_clk.c, "f9a55000.usb"),
+ CLK_LOOKUP("iface_clk", gcc_usb_hsic_ahb_clk.c, "f9a15000.hsic"),
+ CLK_LOOKUP("phy_clk", gcc_usb_hsic_clk.c, "f9a15000.hsic"),
+ CLK_LOOKUP("cal_clk", gcc_usb_hsic_io_cal_clk.c, "f9a15000.hsic"),
+ CLK_LOOKUP("core_clk", gcc_usb_hsic_system_clk.c, "f9a15000.hsic"),
+ CLK_LOOKUP("alt_core_clk", gcc_usb_hsic_xcvr_fs_clk.c,
+ "f9a15000.hsic"),
+
+ /* LPASS clocks */
+ CLK_LOOKUP("core_clk", audio_core_slimbus_core_clk.c, "fe12f000.slim"),
+ CLK_LOOKUP("iface_clk", audio_core_slimbus_lfabif_clk.c, ""),
+ CLK_LOOKUP("core_clk", audio_core_lpaif_pri_clk_src.c, ""),
+ CLK_LOOKUP("osr_clk", audio_core_lpaif_pri_osr_clk.c, ""),
+ CLK_LOOKUP("ebit_clk", audio_core_lpaif_pri_ebit_clk.c, ""),
+ CLK_LOOKUP("ibit_clk", audio_core_lpaif_pri_ibit_clk.c, ""),
+ CLK_LOOKUP("core_clk", audio_core_lpaif_sec_clk_src.c, ""),
+ CLK_LOOKUP("osr_clk", audio_core_lpaif_sec_osr_clk.c, ""),
+ CLK_LOOKUP("ebit_clk", audio_core_lpaif_sec_ebit_clk.c, ""),
+ CLK_LOOKUP("ibit_clk", audio_core_lpaif_sec_ibit_clk.c, ""),
+ CLK_LOOKUP("core_clk", audio_core_lpaif_pcm0_clk_src.c, ""),
+ CLK_LOOKUP("ebit_clk", audio_core_lpaif_pcm0_ebit_clk.c, ""),
+ CLK_LOOKUP("ibit_clk", audio_core_lpaif_pcm0_ibit_clk.c, ""),
+ CLK_LOOKUP("core_clk", audio_core_lpaif_pcm1_clk_src.c, ""),
+ CLK_LOOKUP("ebit_clk", audio_core_lpaif_pcm1_ebit_clk.c, ""),
+ CLK_LOOKUP("ibit_clk", audio_core_lpaif_pcm1_ibit_clk.c, ""),
+ CLK_LOOKUP("core_oe_src_clk", audio_core_lpaif_pcmoe_clk_src.c, ""),
+ CLK_LOOKUP("core_oe_clk", audio_core_lpaif_pcm_data_oe_clk.c, ""),
+
+ /* RPM and voter clocks */
+ CLK_LOOKUP("bus_clk", snoc_clk.c, ""),
+ CLK_LOOKUP("bus_clk", pnoc_clk.c, ""),
+ CLK_LOOKUP("bus_clk", cnoc_clk.c, ""),
+ CLK_LOOKUP("mem_clk", bimc_clk.c, ""),
+ CLK_LOOKUP("bus_clk", snoc_a_clk.c, ""),
+ CLK_LOOKUP("bus_clk", pnoc_a_clk.c, ""),
+ CLK_LOOKUP("bus_clk", cnoc_a_clk.c, ""),
+ CLK_LOOKUP("mem_clk", bimc_a_clk.c, ""),
+
+ CLK_LOOKUP("bus_clk", cnoc_msmbus_clk.c, "msm_config_noc"),
+ CLK_LOOKUP("bus_a_clk", cnoc_msmbus_a_clk.c, "msm_config_noc"),
+ CLK_LOOKUP("bus_clk", snoc_msmbus_clk.c, "msm_sys_noc"),
+ CLK_LOOKUP("bus_a_clk", snoc_msmbus_a_clk.c, "msm_sys_noc"),
+ CLK_LOOKUP("bus_clk", pnoc_msmbus_clk.c, "msm_periph_noc"),
+ CLK_LOOKUP("bus_a_clk", pnoc_msmbus_a_clk.c, "msm_periph_noc"),
+ CLK_LOOKUP("mem_clk", bimc_msmbus_clk.c, "msm_bimc"),
+ CLK_LOOKUP("mem_a_clk", bimc_msmbus_a_clk.c, "msm_bimc"),
+
+ CLK_LOOKUP("dfab_clk", pnoc_sps_clk.c, "msm_sps"),
+
+ CLK_LOOKUP("a5_m_clk", a5_m_clk, ""),
+};
+
+static struct pll_config_regs gpll0_regs __initdata = {
+ .l_reg = (void __iomem *)GPLL0_L_REG,
+ .m_reg = (void __iomem *)GPLL0_M_REG,
+ .n_reg = (void __iomem *)GPLL0_N_REG,
+ .config_reg = (void __iomem *)GPLL0_USER_CTL_REG,
+ .mode_reg = (void __iomem *)GPLL0_MODE_REG,
+ .base = &virt_bases[GCC_BASE],
+};
+
+/* GPLL0 at 600 MHz, main output enabled. */
+static struct pll_config gpll0_config __initdata = {
+ .l = 0x1f,
+ .m = 0x1,
+ .n = 0x4,
+ .vco_val = 0x0,
+ .vco_mask = BM(21, 20),
+ .pre_div_val = 0x0,
+ .pre_div_mask = BM(14, 12),
+ .post_div_val = 0x0,
+ .post_div_mask = BM(9, 8),
+ .mn_ena_val = BIT(24),
+ .mn_ena_mask = BIT(24),
+ .main_output_val = BIT(0),
+ .main_output_mask = BIT(0),
+};
+
+static struct pll_config_regs gpll1_regs __initdata = {
+ .l_reg = (void __iomem *)GPLL1_L_REG,
+ .m_reg = (void __iomem *)GPLL1_M_REG,
+ .n_reg = (void __iomem *)GPLL1_N_REG,
+ .config_reg = (void __iomem *)GPLL1_USER_CTL_REG,
+ .mode_reg = (void __iomem *)GPLL1_MODE_REG,
+ .base = &virt_bases[GCC_BASE],
+};
+
+/* GPLL1 at 480 MHz, main output enabled. */
+static struct pll_config gpll1_config __initdata = {
+ .l = 0x19,
+ .m = 0x0,
+ .n = 0x1,
+ .vco_val = 0x0,
+ .vco_mask = BM(21, 20),
+ .pre_div_val = 0x0,
+ .pre_div_mask = BM(14, 12),
+ .post_div_val = 0x0,
+ .post_div_mask = BM(9, 8),
+ .main_output_val = BIT(0),
+ .main_output_mask = BIT(0),
+};
+
+static struct pll_config_regs lpapll0_regs __initdata = {
+ .l_reg = (void __iomem *)LPAPLL_L_REG,
+ .m_reg = (void __iomem *)LPAPLL_M_REG,
+ .n_reg = (void __iomem *)LPAPLL_N_REG,
+ .config_reg = (void __iomem *)LPAPLL_USER_CTL_REG,
+ .mode_reg = (void __iomem *)LPAPLL_MODE_REG,
+ .base = &virt_bases[LPASS_BASE],
+};
+
+/* LPAPLL0 at 393.216 MHz, main output enabled. */
+static struct pll_config lpapll0_config __initdata = {
+ .l = 0x28,
+ .m = 0x18,
+ .n = 0x19,
+ .vco_val = 0x0,
+ .vco_mask = BM(21, 20),
+ .pre_div_val = 0x0,
+ .pre_div_mask = BM(14, 12),
+ .post_div_val = BVAL(9, 8, 0x1),
+ .post_div_mask = BM(9, 8),
+ .mn_ena_val = BIT(24),
+ .mn_ena_mask = BIT(24),
+ .main_output_val = BIT(0),
+ .main_output_mask = BIT(0),
+};
+
+#define PLL_AUX_OUTPUT_BIT 1
+#define PLL_AUX2_OUTPUT_BIT 2
+
+/*
+ * TODO: Need to remove this function when the v2 hardware
+ * fix the broken lock status bit.
+ */
+#define PLL_OUTCTRL BIT(0)
+#define PLL_BYPASSNL BIT(1)
+#define PLL_RESET_N BIT(2)
+
+static DEFINE_SPINLOCK(sr_pll_reg_lock);
+
+static int sr_pll_clk_enable_9625(struct clk *c)
+{
+ unsigned long flags;
+ struct pll_clk *pll = to_pll_clk(c);
+ u32 mode;
+ void __iomem *mode_reg = *pll->base + (u32)pll->mode_reg;
+
+ spin_lock_irqsave(&sr_pll_reg_lock, flags);
+
+ /* Disable PLL bypass mode and de-assert reset. */
+ mode = readl_relaxed(mode_reg);
+ mode |= PLL_BYPASSNL | PLL_RESET_N;
+ writel_relaxed(mode, mode_reg);
+
+ /* Wait for pll to lock. */
+ udelay(100);
+
+ /* Enable PLL output. */
+ mode |= PLL_OUTCTRL;
+ writel_relaxed(mode, mode_reg);
+
+ /* Ensure the write above goes through before returning. */
+ mb();
+
+ spin_unlock_irqrestore(&sr_pll_reg_lock, flags);
+ return 0;
+}
+
+static void __init configure_apcs_pll(void)
+{
+ u32 regval;
+
+ clk_set_rate(&apcspll_clk_src.c, 998400000);
+
+ writel_relaxed(0x00141200,
+ APCS_PLL_REG_BASE(APCS_CPU_PLL_CONFIG_CTL_REG));
+
+ /* Enable AUX and AUX2 output */
+ regval = readl_relaxed(APCS_PLL_REG_BASE(APCS_CPU_PLL_USER_CTL_REG));
+ regval |= BIT(PLL_AUX_OUTPUT_BIT) | BIT(PLL_AUX2_OUTPUT_BIT);
+ writel_relaxed(regval, APCS_PLL_REG_BASE(APCS_CPU_PLL_USER_CTL_REG));
+}
+
+#define PWR_ON_MASK BIT(31)
+#define EN_REST_WAIT_MASK (0xF << 20)
+#define EN_FEW_WAIT_MASK (0xF << 16)
+#define CLK_DIS_WAIT_MASK (0xF << 12)
+#define SW_OVERRIDE_MASK BIT(2)
+#define HW_CONTROL_MASK BIT(1)
+#define SW_COLLAPSE_MASK BIT(0)
+
+/* Wait 2^n CXO cycles between all states. Here, n=2 (4 cycles). */
+#define EN_REST_WAIT_VAL (0x2 << 20)
+#define EN_FEW_WAIT_VAL (0x2 << 16)
+#define CLK_DIS_WAIT_VAL (0x2 << 12)
+#define GDSC_TIMEOUT_US 50000
+
+static void __init reg_init(void)
+{
+ u32 regval, status;
+ int ret;
+
+ if (!(readl_relaxed(GCC_REG_BASE(GPLL0_STATUS_REG))
+ & gpll0_clk_src.status_mask))
+ configure_sr_hpm_lp_pll(&gpll0_config, &gpll0_regs, 1);
+
+ if (!(readl_relaxed(GCC_REG_BASE(GPLL1_STATUS_REG))
+ & gpll1_clk_src.status_mask))
+ configure_sr_hpm_lp_pll(&gpll1_config, &gpll1_regs, 1);
+
+ configure_sr_hpm_lp_pll(&lpapll0_config, &lpapll0_regs, 1);
+
+ /* TODO: Remove A5 pll configuration once the bootloader is avaiable */
+ regval = readl_relaxed(APCS_PLL_REG_BASE(APCS_CPU_PLL_MODE_REG));
+ if ((regval & BM(2, 0)) != 0x7)
+ configure_apcs_pll();
+
+ /* TODO:
+ * 1) do we need to turn on AUX2 output too?
+ * 2) if need to vote off all sleep clocks
+ */
+
+ /* Enable GPLL0's aux outputs. */
+ regval = readl_relaxed(GCC_REG_BASE(GPLL0_USER_CTL_REG));
+ regval |= BIT(PLL_AUX_OUTPUT_BIT) | BIT(PLL_AUX2_OUTPUT_BIT);
+ writel_relaxed(regval, GCC_REG_BASE(GPLL0_USER_CTL_REG));
+
+ /* Vote for GPLL0 to turn on. Needed by acpuclock. */
+ regval = readl_relaxed(GCC_REG_BASE(APCS_GPLL_ENA_VOTE_REG));
+ regval |= BIT(0);
+ writel_relaxed(regval, GCC_REG_BASE(APCS_GPLL_ENA_VOTE_REG));
+
+ /*
+ * TODO: Confirm that no clocks need to be voted on in this sleep vote
+ * register.
+ */
+ writel_relaxed(0x0, GCC_REG_BASE(APCS_CLOCK_SLEEP_ENA_VOTE));
+
+ /*
+ * TODO: The following sequence enables the LPASS audio core GDSC.
+ * Remove when this becomes unnecessary.
+ */
+
+ /*
+ * Disable HW trigger: collapse/restore occur based on registers writes.
+ * Disable SW override: Use hardware state-machine for sequencing.
+ */
+ regval = readl_relaxed(LPASS_REG_BASE(AUDIO_CORE_GDSCR));
+ regval &= ~(HW_CONTROL_MASK | SW_OVERRIDE_MASK);
+
+ /* Configure wait time between states. */
+ regval &= ~(EN_REST_WAIT_MASK | EN_FEW_WAIT_MASK | CLK_DIS_WAIT_MASK);
+ regval |= EN_REST_WAIT_VAL | EN_FEW_WAIT_VAL | CLK_DIS_WAIT_VAL;
+ writel_relaxed(regval, LPASS_REG_BASE(AUDIO_CORE_GDSCR));
+
+ regval = readl_relaxed(LPASS_REG_BASE(AUDIO_CORE_GDSCR));
+ regval &= ~BIT(0);
+ writel_relaxed(regval, LPASS_REG_BASE(AUDIO_CORE_GDSCR));
+
+ ret = readl_poll_timeout(LPASS_REG_BASE(AUDIO_CORE_GDSCR), status,
+ status & PWR_ON_MASK, 50, GDSC_TIMEOUT_US);
+ WARN(ret, "LPASS Audio Core GDSC did not power on.\n");
+}
+
+static void __init msm9625_clock_post_init(void)
+{
+ /*
+ * Hold an active set vote for CXO; this is because CXO is expected
+ * to remain on whenever CPUs aren't power collapsed.
+ */
+ clk_prepare_enable(&cxo_a_clk_src.c);
+
+ /*
+ * TODO: This call is to prevent sending 0Hz to rpm to turn off pnoc.
+ * Needs to remove this after vote of pnoc from sdcc driver is ready.
+ */
+ clk_prepare_enable(&pnoc_msmbus_a_clk.c);
+
+ /* Set rates for single-rate clocks. */
+ clk_set_rate(&usb_hs_system_clk_src.c,
+ usb_hs_system_clk_src.freq_tbl[0].freq_hz);
+ clk_set_rate(&usb_hsic_clk_src.c,
+ usb_hsic_clk_src.freq_tbl[0].freq_hz);
+ clk_set_rate(&usb_hsic_io_cal_clk_src.c,
+ usb_hsic_io_cal_clk_src.freq_tbl[0].freq_hz);
+ clk_set_rate(&usb_hsic_system_clk_src.c,
+ usb_hsic_system_clk_src.freq_tbl[0].freq_hz);
+ clk_set_rate(&usb_hsic_xcvr_fs_clk_src.c,
+ usb_hsic_xcvr_fs_clk_src.freq_tbl[0].freq_hz);
+ clk_set_rate(&pdm2_clk_src.c, pdm2_clk_src.freq_tbl[0].freq_hz);
+ clk_set_rate(&audio_core_slimbus_core_clk_src.c,
+ audio_core_slimbus_core_clk_src.freq_tbl[0].freq_hz);
+}
+
+#define GCC_CC_PHYS 0xFC400000
+#define GCC_CC_SIZE SZ_16K
+
+#define LPASS_CC_PHYS 0xFE000000
+#define LPASS_CC_SIZE SZ_256K
+
+#define APCS_GCC_CC_PHYS 0xF9011000
+#define APCS_GCC_CC_SIZE SZ_4K
+
+#define APCS_PLL_PHYS 0xF9008018
+#define APCS_PLL_SIZE 0x18
+
+static void __init msm9625_clock_pre_init(void)
+{
+ virt_bases[GCC_BASE] = ioremap(GCC_CC_PHYS, GCC_CC_SIZE);
+ if (!virt_bases[GCC_BASE])
+ panic("clock-9625: Unable to ioremap GCC memory!");
+
+ virt_bases[LPASS_BASE] = ioremap(LPASS_CC_PHYS, LPASS_CC_SIZE);
+ if (!virt_bases[LPASS_BASE])
+ panic("clock-9625: Unable to ioremap LPASS_CC memory!");
+
+ virt_bases[APCS_BASE] = ioremap(APCS_GCC_CC_PHYS, APCS_GCC_CC_SIZE);
+ if (!virt_bases[APCS_BASE])
+ panic("clock-9625: Unable to ioremap APCS_GCC_CC memory!");
+
+ virt_bases[APCS_PLL_BASE] = ioremap(APCS_PLL_PHYS, APCS_PLL_SIZE);
+ if (!virt_bases[APCS_PLL_BASE])
+ panic("clock-9625: Unable to ioremap APCS_PLL memory!");
+
+ clk_ops_local_pll.enable = sr_pll_clk_enable_9625;
+
+ vdd_dig_reg = regulator_get(NULL, "vdd_dig");
+ if (IS_ERR(vdd_dig_reg))
+ panic("clock-9625: Unable to get the vdd_dig regulator!");
+
+ vote_vdd_level(&vdd_dig, VDD_DIG_HIGH);
+ regulator_enable(vdd_dig_reg);
+
+ enable_rpm_scaling();
+
+ reg_init();
+}
+
+static int __init msm9625_clock_late_init(void)
+{
+ return unvote_vdd_level(&vdd_dig, VDD_DIG_HIGH);
+}
+
+struct clock_init_data msm9625_clock_init_data __initdata = {
+ .table = msm_clocks_9625,
+ .size = ARRAY_SIZE(msm_clocks_9625),
+ .pre_init = msm9625_clock_pre_init,
+ .post_init = msm9625_clock_post_init,
+ .late_init = msm9625_clock_late_init,
+};
diff --git a/arch/arm/mach-msm/clock-debug.c b/arch/arm/mach-msm/clock-debug.c
index e942173..8bd4433 100644
--- a/arch/arm/mach-msm/clock-debug.c
+++ b/arch/arm/mach-msm/clock-debug.c
@@ -218,6 +218,28 @@
.release = seq_release,
};
+static int clock_parent_show(struct seq_file *m, void *unused)
+{
+ struct clk *clock = m->private;
+ struct clk *parent = clk_get_parent(clock);
+
+ seq_printf(m, "%s\n", (parent ? parent->dbg_name : "None"));
+
+ return 0;
+}
+
+static int clock_parent_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, clock_parent_show, inode->i_private);
+}
+
+static const struct file_operations clock_parent_fops = {
+ .open = clock_parent_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
static struct dentry *debugfs_base;
static u32 debug_suspend;
@@ -274,6 +296,10 @@
S_IRUGO, clk_dir, clock, &fmax_rates_fops))
goto error;
+ if (!debugfs_create_file("parent", S_IRUGO, clk_dir, clock,
+ &clock_parent_fops))
+ goto error;
+
return 0;
error:
debugfs_remove_recursive(clk_dir);
diff --git a/arch/arm/mach-msm/clock-local.c b/arch/arm/mach-msm/clock-local.c
index ca031ad..d2260cb 100644
--- a/arch/arm/mach-msm/clock-local.c
+++ b/arch/arm/mach-msm/clock-local.c
@@ -352,7 +352,7 @@
u32 reg_val;
reg_val = b->ctl_reg ? readl_relaxed(b->ctl_reg) : 0;
- if (b->en_mask) {
+ if (b->ctl_reg && b->en_mask) {
reg_val &= ~(b->en_mask);
writel_relaxed(reg_val, b->ctl_reg);
}
diff --git a/arch/arm/mach-msm/clock-pll.c b/arch/arm/mach-msm/clock-pll.c
index 23941d7..240f4e4 100644
--- a/arch/arm/mach-msm/clock-pll.c
+++ b/arch/arm/mach-msm/clock-pll.c
@@ -55,7 +55,7 @@
#define ENABLE_WAIT_MAX_LOOPS 200
-int pll_vote_clk_enable(struct clk *c)
+static int pll_vote_clk_enable(struct clk *c)
{
u32 ena, count;
unsigned long flags;
@@ -85,7 +85,7 @@
return -ETIMEDOUT;
}
-void pll_vote_clk_disable(struct clk *c)
+static void pll_vote_clk_disable(struct clk *c)
{
u32 ena;
unsigned long flags;
@@ -98,12 +98,12 @@
spin_unlock_irqrestore(&pll_reg_lock, flags);
}
-struct clk *pll_vote_clk_get_parent(struct clk *c)
+static struct clk *pll_vote_clk_get_parent(struct clk *c)
{
return to_pll_vote_clk(c)->parent;
}
-int pll_vote_clk_is_enabled(struct clk *c)
+static int pll_vote_clk_is_enabled(struct clk *c)
{
struct pll_vote_clk *pllv = to_pll_vote_clk(c);
return !!(readl_relaxed(PLL_STATUS_REG(pllv)) & pllv->status_mask);
@@ -126,6 +126,34 @@
.handoff = pll_vote_clk_handoff,
};
+static void __pll_config_reg(void __iomem *pll_config, struct pll_freq_tbl *f,
+ struct pll_config_masks *masks)
+{
+ u32 regval;
+
+ regval = readl_relaxed(pll_config);
+
+ /* Enable the MN counter if used */
+ if (f->m_val)
+ regval |= masks->mn_en_mask;
+
+ /* Set pre-divider and post-divider values */
+ regval &= ~masks->pre_div_mask;
+ regval |= f->pre_div_val;
+ regval &= ~masks->post_div_mask;
+ regval |= f->post_div_val;
+
+ /* Select VCO setting */
+ regval &= ~masks->vco_mask;
+ regval |= f->vco_val;
+
+ /* Enable main output if it has not been enabled */
+ if (masks->main_output_mask && !(regval & masks->main_output_mask))
+ regval |= masks->main_output_mask;
+
+ writel_relaxed(regval, pll_config);
+}
+
static void __pll_clk_enable_reg(void __iomem *mode_reg)
{
u32 mode = readl_relaxed(mode_reg);
@@ -206,6 +234,34 @@
return to_pll_clk(c)->parent;
}
+static int local_pll_clk_set_rate(struct clk *c, unsigned long rate)
+{
+ struct pll_freq_tbl *nf;
+ struct pll_clk *pll = to_pll_clk(c);
+ u32 mode;
+
+ mode = readl_relaxed(PLL_MODE_REG(pll));
+
+ /* Don't change PLL's rate if it is enabled */
+ if ((mode & PLL_MODE_MASK) == PLL_MODE_MASK)
+ return -EBUSY;
+
+ for (nf = pll->freq_tbl; nf->freq_hz != PLL_FREQ_END
+ && nf->freq_hz != rate; nf++)
+ ;
+
+ if (nf->freq_hz == PLL_FREQ_END)
+ return -EINVAL;
+
+ writel_relaxed(nf->l_val, PLL_L_REG(pll));
+ writel_relaxed(nf->m_val, PLL_M_REG(pll));
+ writel_relaxed(nf->n_val, PLL_N_REG(pll));
+
+ __pll_config_reg(PLL_CONFIG_REG(pll), nf, &pll->masks);
+
+ return 0;
+}
+
int sr_pll_clk_enable(struct clk *c)
{
u32 mode;
@@ -288,6 +344,7 @@
struct clk_ops clk_ops_local_pll = {
.enable = local_pll_clk_enable,
.disable = local_pll_clk_disable,
+ .set_rate = local_pll_clk_set_rate,
.handoff = local_pll_clk_handoff,
.get_parent = local_pll_clk_get_parent,
};
@@ -305,6 +362,7 @@
{41, 800000000},
{50, 960000000},
{52, 1008000000},
+ {57, 1104000000},
{60, 1152000000},
{62, 1200000000},
{63, 1209600000},
@@ -442,6 +500,46 @@
.is_enabled = pll_clk_is_enabled,
};
+static DEFINE_SPINLOCK(soft_vote_lock);
+
+static int pll_acpu_vote_clk_enable(struct clk *c)
+{
+ int ret = 0;
+ unsigned long flags;
+ struct pll_vote_clk *pllv = to_pll_vote_clk(c);
+
+ spin_lock_irqsave(&soft_vote_lock, flags);
+
+ if (!*pllv->soft_vote)
+ ret = pll_vote_clk_enable(c);
+ if (ret == 0)
+ *pllv->soft_vote |= (pllv->soft_vote_mask);
+
+ spin_unlock_irqrestore(&soft_vote_lock, flags);
+ return ret;
+}
+
+static void pll_acpu_vote_clk_disable(struct clk *c)
+{
+ unsigned long flags;
+ struct pll_vote_clk *pllv = to_pll_vote_clk(c);
+
+ spin_lock_irqsave(&soft_vote_lock, flags);
+
+ *pllv->soft_vote &= ~(pllv->soft_vote_mask);
+ if (!*pllv->soft_vote)
+ pll_vote_clk_disable(c);
+
+ spin_unlock_irqrestore(&soft_vote_lock, flags);
+}
+
+struct clk_ops clk_ops_pll_acpu_vote = {
+ .enable = pll_acpu_vote_clk_enable,
+ .disable = pll_acpu_vote_clk_disable,
+ .is_enabled = pll_vote_clk_is_enabled,
+ .get_parent = pll_vote_clk_get_parent,
+};
+
static void __init __set_fsm_mode(void __iomem *mode_reg,
u32 bias_count, u32 lock_count)
{
diff --git a/arch/arm/mach-msm/clock-pll.h b/arch/arm/mach-msm/clock-pll.h
index 5c7c304..33b35a8 100644
--- a/arch/arm/mach-msm/clock-pll.h
+++ b/arch/arm/mach-msm/clock-pll.h
@@ -58,6 +58,45 @@
void msm_shared_pll_control_init(void);
/**
+ * struct pll_freq_tbl - generic PLL frequency definition
+ * @freq_hz: pll frequency in hz
+ * @l_val: pll l value
+ * @m_val: pll m value
+ * @n_val: pll n value
+ * @post_div_val: pll post divider value
+ * @pre_div_val: pll pre-divider value
+ * @vco_val: pll vco value
+ */
+struct pll_freq_tbl {
+ const u32 freq_hz;
+ const u32 l_val;
+ const u32 m_val;
+ const u32 n_val;
+ const u32 post_div_val;
+ const u32 pre_div_val;
+ const u32 vco_val;
+};
+
+/**
+ * struct pll_config_masks - PLL config masks struct
+ * @post_div_mask: mask for post divider bits location
+ * @pre_div_mask: mask for pre-divider bits location
+ * @vco_mask: mask for vco bits location
+ * @mn_en_mask: ORed with pll config register to enable the mn counter
+ * @main_output_mask: ORed with pll config register to enable the main output
+ */
+struct pll_config_masks {
+ u32 post_div_mask;
+ u32 pre_div_mask;
+ u32 vco_mask;
+ u32 mn_en_mask;
+ u32 main_output_mask;
+};
+
+#define PLL_FREQ_END (UINT_MAX-1)
+#define PLL_F_END { .freq_hz = PLL_FREQ_END }
+
+/**
* struct pll_vote_clk - phase locked loop (HW voteable)
* @soft_vote: soft voting variable for multiple PLL software instances
* @soft_vote_mask: soft voting mask for multiple PLL software instances
@@ -82,6 +121,11 @@
};
extern struct clk_ops clk_ops_pll_vote;
+extern struct clk_ops clk_ops_pll_acpu_vote;
+
+/* Soft voting values */
+#define PLL_SOFT_VOTE_PRIMARY BIT(0)
+#define PLL_SOFT_VOTE_ACPU BIT(1)
static inline struct pll_vote_clk *to_pll_vote_clk(struct clk *c)
{
@@ -91,15 +135,30 @@
/**
* struct pll_clk - phase locked loop
* @mode_reg: enable register
+ * @l_reg: l value register
+ * @m_reg: m value register
+ * @n_reg: n value register
+ * @config_reg: configuration register, contains mn divider enable, pre divider,
+ * post divider and vco configuration. register name can be configure register
+ * or user_ctl register depending on targets
* @status_reg: status register, contains the lock detection bit
+ * @masks: masks used for settings in config_reg
+ * @freq_tbl: pll freq table
* @parent: clock source
* @c: clk
* @base: pointer to base address of ioremapped registers.
*/
struct pll_clk {
void __iomem *const mode_reg;
+ void __iomem *const l_reg;
+ void __iomem *const m_reg;
+ void __iomem *const n_reg;
+ void __iomem *const config_reg;
void __iomem *const status_reg;
+ struct pll_config_masks masks;
+ struct pll_freq_tbl *freq_tbl;
+
struct clk *parent;
struct clk c;
void *const __iomem *base;
@@ -115,14 +174,6 @@
int sr_pll_clk_enable(struct clk *c);
int sr_hpm_lp_pll_clk_enable(struct clk *c);
-/*
- * PLL vote clock APIs
- */
-int pll_vote_clk_enable(struct clk *c);
-void pll_vote_clk_disable(struct clk *c);
-struct clk *pll_vote_clk_get_parent(struct clk *c);
-int pll_vote_clk_is_enabled(struct clk *c);
-
struct pll_config {
u32 l;
u32 m;
diff --git a/arch/arm/mach-msm/clock-rpm.c b/arch/arm/mach-msm/clock-rpm.c
index e06eb4b..daf83e2 100644
--- a/arch/arm/mach-msm/clock-rpm.c
+++ b/arch/arm/mach-msm/clock-rpm.c
@@ -297,6 +297,27 @@
return HANDOFF_ENABLED_CLK;
}
+#define RPM_MISC_CLK_TYPE 0x306b6c63
+#define RPM_SCALING_ENABLE_ID 0x2
+
+void enable_rpm_scaling(void)
+{
+ int rc, value = 0x1;
+ struct msm_rpm_kvp kvp = {
+ .key = RPM_SMD_KEY_ENABLE,
+ .data = (void *)&value,
+ .length = sizeof(value),
+ };
+
+ rc = msm_rpm_send_message_noirq(MSM_RPM_CTX_SLEEP_SET,
+ RPM_MISC_CLK_TYPE, RPM_SCALING_ENABLE_ID, &kvp, 1);
+ WARN(rc < 0, "RPM clock scaling (sleep set) did not enable!\n");
+
+ rc = msm_rpm_send_message_noirq(MSM_RPM_CTX_ACTIVE_SET,
+ RPM_MISC_CLK_TYPE, RPM_SCALING_ENABLE_ID, &kvp, 1);
+ WARN(rc < 0, "RPM clock scaling (active set) did not enable!\n");
+}
+
struct clk_ops clk_ops_rpm = {
.enable = rpm_clk_enable,
.disable = rpm_clk_disable,
diff --git a/arch/arm/mach-msm/clock-rpm.h b/arch/arm/mach-msm/clock-rpm.h
index 2f0b729..252e8cb 100644
--- a/arch/arm/mach-msm/clock-rpm.h
+++ b/arch/arm/mach-msm/clock-rpm.h
@@ -54,6 +54,12 @@
return container_of(clk, struct rpm_clk, c);
}
+/*
+ * RPM scaling enable function used for target that has an RPM resource for
+ * rpm clock scaling enable.
+ */
+void enable_rpm_scaling(void);
+
extern struct clk_rpmrs_data clk_rpmrs_data;
extern struct clk_rpmrs_data clk_rpmrs_data_smd;
diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c
index c30bd79..5100980 100644
--- a/arch/arm/mach-msm/clock.c
+++ b/arch/arm/mach-msm/clock.c
@@ -425,6 +425,19 @@
static struct clock_init_data *clk_init_data;
+static void init_sibling_lists(struct clk_lookup *clock_tbl, size_t num_clocks)
+{
+ struct clk *clk, *parent;
+ unsigned n;
+
+ for (n = 0; n < num_clocks; n++) {
+ clk = clock_tbl[n].clk;
+ parent = clk_get_parent(clk);
+ if (parent && list_empty(&clk->siblings))
+ list_add(&clk->siblings, &parent->children);
+ }
+}
+
/**
* msm_clock_register() - Register additional clock tables
* @table: Table of clocks
@@ -443,6 +456,7 @@
if (!table)
return -EINVAL;
+ init_sibling_lists(table, size);
clkdev_add_table(table, size);
clock_debug_register(table, size);
@@ -514,7 +528,6 @@
unsigned n;
struct clk_lookup *clock_tbl;
size_t num_clocks;
- struct clk *clk;
if (!data)
return -EINVAL;
@@ -526,13 +539,7 @@
clock_tbl = data->table;
num_clocks = data->size;
- for (n = 0; n < num_clocks; n++) {
- struct clk *parent;
- clk = clock_tbl[n].clk;
- parent = clk_get_parent(clk);
- if (parent && list_empty(&clk->siblings))
- list_add(&clk->siblings, &parent->children);
- }
+ init_sibling_lists(clock_tbl, num_clocks);
/*
* Detect and preserve initial clock state until clock_late_init() or
diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h
index 48f897b..8a75d390 100644
--- a/arch/arm/mach-msm/clock.h
+++ b/arch/arm/mach-msm/clock.h
@@ -34,6 +34,7 @@
};
extern struct clock_init_data msm9615_clock_init_data;
+extern struct clock_init_data msm9625_clock_init_data;
extern struct clock_init_data apq8064_clock_init_data;
extern struct clock_init_data fsm9xxx_clock_init_data;
extern struct clock_init_data msm7x01a_clock_init_data;
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index c0461e1..d727c54 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -101,6 +101,7 @@
#define PCIE20_SIZE SZ_4K
#define MSM8064_PC_CNTR_PHYS (APQ8064_IMEM_PHYS + 0x664)
#define MSM8064_PC_CNTR_SIZE 0x40
+#define MSM8064_RPM_MASTER_STATS_BASE 0x10BB00
static struct resource msm8064_resources_pccntr[] = {
{
@@ -1962,6 +1963,11 @@
.end = 0x10008000 + SZ_256 - 1,
.flags = IORESOURCE_MEM,
},
+ {
+ .start = GSS_A5_WDOG_EXPIRED,
+ .end = GSS_A5_WDOG_EXPIRED,
+ .flags = IORESOURCE_IRQ,
+ },
};
struct platform_device msm_gss = {
@@ -2346,6 +2352,37 @@
},
};
+static struct resource resources_rpm_master_stats[] = {
+ {
+ .start = MSM8064_RPM_MASTER_STATS_BASE,
+ .end = MSM8064_RPM_MASTER_STATS_BASE + SZ_256,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static char *master_names[] = {
+ "KPSS",
+ "MPSS",
+ "LPASS",
+ "RIVA",
+ "DSPS",
+};
+
+static struct msm_rpm_master_stats_platform_data msm_rpm_master_stat_pdata = {
+ .masters = master_names,
+ .nomasters = ARRAY_SIZE(master_names),
+};
+
+struct platform_device apq8064_rpm_master_stat_device = {
+ .name = "msm_rpm_master_stat",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(resources_rpm_master_stats),
+ .resource = resources_rpm_master_stats,
+ .dev = {
+ .platform_data = &msm_rpm_master_stat_pdata,
+ },
+};
+
static struct msm_rpm_log_platform_data msm_rpm_log_pdata = {
.phys_addr_base = 0x0010C000,
.reg_offsets = {
@@ -2367,19 +2404,6 @@
/* Sensors DSPS platform data */
-#define PPSS_DSPS_TCM_CODE_BASE 0x12000000
-#define PPSS_DSPS_TCM_CODE_SIZE 0x28000
-#define PPSS_DSPS_TCM_BUF_BASE 0x12040000
-#define PPSS_DSPS_TCM_BUF_SIZE 0x4000
-#define PPSS_DSPS_PIPE_BASE 0x12800000
-#define PPSS_DSPS_PIPE_SIZE 0x4000
-#define PPSS_DSPS_DDR_BASE 0x8fe00000
-#define PPSS_DSPS_DDR_SIZE 0x100000
-#define PPSS_SMEM_BASE 0x80000000
-#define PPSS_SMEM_SIZE 0x200000
-#define PPSS_REG_PHYS_BASE 0x12080000
-#define PPSS_WDOG_UNMASKED_INT_EN 0x1808
-
static struct dsps_clk_info dsps_clks[] = {};
static struct dsps_regulator_info dsps_regs[] = {};
@@ -2388,6 +2412,8 @@
* apq8064_init_dsps().
*/
+#define PPSS_REG_PHYS_BASE 0x12080000
+
struct msm_dsps_platform_data msm_dsps_pdata_8064 = {
.clks = dsps_clks,
.clks_num = ARRAY_SIZE(dsps_clks),
@@ -2396,17 +2422,6 @@
.regs = dsps_regs,
.regs_num = ARRAY_SIZE(dsps_regs),
.dsps_pwr_ctl_en = 1,
- .tcm_code_start = PPSS_DSPS_TCM_CODE_BASE,
- .tcm_code_size = PPSS_DSPS_TCM_CODE_SIZE,
- .tcm_buf_start = PPSS_DSPS_TCM_BUF_BASE,
- .tcm_buf_size = PPSS_DSPS_TCM_BUF_SIZE,
- .pipe_start = PPSS_DSPS_PIPE_BASE,
- .pipe_size = PPSS_DSPS_PIPE_SIZE,
- .ddr_start = PPSS_DSPS_DDR_BASE,
- .ddr_size = PPSS_DSPS_DDR_SIZE,
- .smem_start = PPSS_SMEM_BASE,
- .smem_size = PPSS_SMEM_SIZE,
- .ppss_wdog_unmasked_int_en_reg = PPSS_WDOG_UNMASKED_INT_EN,
.signature = DSPS_SIGNATURE,
};
@@ -2417,13 +2432,6 @@
.name = "ppss_reg",
.flags = IORESOURCE_MEM,
},
-
- {
- .start = PPSS_WDOG_TIMER_IRQ,
- .end = PPSS_WDOG_TIMER_IRQ,
- .name = "ppss_wdog",
- .flags = IORESOURCE_IRQ,
- },
};
struct platform_device msm_dsps_device_8064 = {
@@ -2644,50 +2652,86 @@
.num_resources = ARRAY_SIZE(i2s_mdm_resources),
.resource = i2s_mdm_resources,
};
-static int apq8064_LPM_latency = 1000; /* >100 usec for WFI */
-
-struct platform_device apq8064_cpu_idle_device = {
- .name = "msm_cpu_idle",
- .id = -1,
- .dev = {
- .platform_data = &apq8064_LPM_latency,
- },
-};
static struct msm_dcvs_freq_entry apq8064_freq[] = {
- { 384000, 166981, 345600},
- { 702000, 213049, 632502},
- {1026000, 285712, 925613},
- {1242000, 383945, 1176550},
- {1458000, 419729, 1465478},
- {1512000, 434116, 1546674},
-
+ { 384000, 900, 0, 0, 0},
+ { 594000, 950, 0, 0, 0},
+ { 702000, 975, 0, 0, 0},
+ {1026000, 1075, 0, 0, 0},
+ {1242000, 1150, 0, 100, 100},
+ {1458000, 1188, 0, 100, 100},
+ {1512000, 1200, 1, 100, 100},
};
static struct msm_dcvs_core_info apq8064_core_info = {
- .freq_tbl = &apq8064_freq[0],
- .core_param = {
- .max_time_us = 100000,
- .num_freq = ARRAY_SIZE(apq8064_freq),
+ .freq_tbl = &apq8064_freq[0],
+ .num_cores = 4,
+ .sensors = (int[]){7, 8, 9, 10},
+ .thermal_poll_ms = 60000,
+ .core_param = {
+ .core_type = MSM_DCVS_CORE_TYPE_CPU,
},
- .algo_param = {
- .slack_time_us = 58000,
- .scale_slack_time = 0,
- .scale_slack_time_pct = 0,
- .disable_pc_threshold = 1458000,
- .em_window_size = 100000,
- .em_max_util_pct = 97,
- .ss_window_size = 1000000,
- .ss_util_pct = 95,
- .ss_iobusy_conv = 100,
+ .algo_param = {
+ .disable_pc_threshold = 1458000,
+ .em_win_size_min_us = 100000,
+ .em_win_size_max_us = 300000,
+ .em_max_util_pct = 97,
+ .group_id = 1,
+ .max_freq_chg_time_us = 100000,
+ .slack_mode_dynamic = 0,
+ .slack_weight_thresh_pct = 3,
+ .slack_time_min_us = 45000,
+ .slack_time_max_us = 45000,
+ .ss_iobusy_conv = 100,
+ .ss_win_size_min_us = 1000000,
+ .ss_win_size_max_us = 1000000,
+ .ss_util_pct = 95,
},
+ .energy_coeffs = {
+ .active_coeff_a = 336,
+ .active_coeff_b = 0,
+ .active_coeff_c = 0,
+
+ .leakage_coeff_a = -17720,
+ .leakage_coeff_b = 37,
+ .leakage_coeff_c = 3329,
+ .leakage_coeff_d = -277,
+ },
+ .power_param = {
+ .current_temp = 25,
+ .num_freq = ARRAY_SIZE(apq8064_freq),
+ }
+};
+
+#define APQ8064_LPM_LATENCY 1000 /* >100 usec for WFI */
+
+static struct msm_gov_platform_data gov_platform_data = {
+ .info = &apq8064_core_info,
+ .latency = APQ8064_LPM_LATENCY,
};
struct platform_device apq8064_msm_gov_device = {
.name = "msm_dcvs_gov",
.id = -1,
.dev = {
- .platform_data = &apq8064_core_info,
+ .platform_data = &gov_platform_data,
+ },
+};
+
+static struct msm_mpd_algo_param apq8064_mpd_algo_param = {
+ .em_win_size_min_us = 10000,
+ .em_win_size_max_us = 100000,
+ .em_max_util_pct = 90,
+ .online_util_pct_min = 60,
+ .slack_time_min_us = 50000,
+ .slack_time_max_us = 100000,
+};
+
+struct platform_device apq8064_msm_mpd_device = {
+ .name = "msm_mpdecision",
+ .id = -1,
+ .dev = {
+ .platform_data = &apq8064_mpd_algo_param,
},
};
diff --git a/arch/arm/mach-msm/devices-8930.c b/arch/arm/mach-msm/devices-8930.c
index f52ee18..30a99cd 100644
--- a/arch/arm/mach-msm/devices-8930.c
+++ b/arch/arm/mach-msm/devices-8930.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -18,7 +18,6 @@
#include <mach/msm_iomap.h>
#include <mach/irqs-8930.h>
#include <mach/rpm.h>
-#include <mach/msm_dcvs.h>
#include <mach/msm_bus.h>
#include <mach/msm_bus_board.h>
#include <mach/board.h>
@@ -39,6 +38,7 @@
#endif
#define MSM8930_PC_CNTR_PHYS (MSM8930_IMEM_PHYS + 0x664)
#define MSM8930_PC_CNTR_SIZE 0x40
+#define MSM8930_RPM_MASTER_STATS_BASE 0x10B100
static struct resource msm8930_resources_pccntr[] = {
{
@@ -559,6 +559,36 @@
},
};
+static struct resource resources_rpm_master_stats[] = {
+ {
+ .start = MSM8930_RPM_MASTER_STATS_BASE,
+ .end = MSM8930_RPM_MASTER_STATS_BASE + SZ_256,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static char *master_names[] = {
+ "KPSS",
+ "MPSS",
+ "LPASS",
+ "RIVA",
+};
+
+static struct msm_rpm_master_stats_platform_data msm_rpm_master_stat_pdata = {
+ .masters = master_names,
+ .nomasters = ARRAY_SIZE(master_names),
+};
+
+struct platform_device msm8930_rpm_master_stat_device = {
+ .name = "msm_rpm_master_stat",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(resources_rpm_master_stats),
+ .resource = resources_rpm_master_stats,
+ .dev = {
+ .platform_data = &msm_rpm_master_stat_pdata,
+ },
+};
+
static struct resource msm_rpm_rbcpr_resource = {
.start = 0x0010DB00,
.end = 0x0010DB00 + SZ_8K - 1,
@@ -584,53 +614,6 @@
.resource = &msm_rpm_rbcpr_resource,
};
-static int msm8930_LPM_latency = 1000; /* >100 usec for WFI */
-
-struct platform_device msm8930_cpu_idle_device = {
- .name = "msm_cpu_idle",
- .id = -1,
- .dev = {
- .platform_data = &msm8930_LPM_latency,
- },
-};
-
-static struct msm_dcvs_freq_entry msm8930_freq[] = {
- { 384000, 166981, 345600},
- { 702000, 213049, 632502},
- {1026000, 285712, 925613},
- {1242000, 383945, 1176550},
- {1458000, 419729, 1465478},
- {1512000, 434116, 1546674},
-
-};
-
-static struct msm_dcvs_core_info msm8930_core_info = {
- .freq_tbl = &msm8930_freq[0],
- .core_param = {
- .max_time_us = 100000,
- .num_freq = ARRAY_SIZE(msm8930_freq),
- },
- .algo_param = {
- .slack_time_us = 58000,
- .scale_slack_time = 0,
- .scale_slack_time_pct = 0,
- .disable_pc_threshold = 1458000,
- .em_window_size = 100000,
- .em_max_util_pct = 97,
- .ss_window_size = 1000000,
- .ss_util_pct = 95,
- .ss_iobusy_conv = 100,
- },
-};
-
-struct platform_device msm8930_msm_gov_device = {
- .name = "msm_dcvs_gov",
- .id = -1,
- .dev = {
- .platform_data = &msm8930_core_info,
- },
-};
-
struct platform_device msm_bus_8930_sys_fabric = {
.name = "msm_bus_fabric",
.id = MSM_BUS_FAB_SYSTEM,
diff --git a/arch/arm/mach-msm/devices-8960.c b/arch/arm/mach-msm/devices-8960.c
index 5cbfb5b..0f71bc4 100644
--- a/arch/arm/mach-msm/devices-8960.c
+++ b/arch/arm/mach-msm/devices-8960.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -105,6 +105,7 @@
#define MSM8960_PC_CNTR_PHYS (MSM8960_IMEM_PHYS + 0x664)
#define MSM8960_PC_CNTR_SIZE 0x40
+#define MSM8960_RPM_MASTER_STATS_BASE 0x10BB00
static struct resource msm8960_resources_pccntr[] = {
{
@@ -1334,108 +1335,106 @@
},
};
-#define MSM_LPASS_QDSP6SS_PHYS 0x28800000
-#define SFAB_LPASS_Q6_ACLK_CTL (MSM_CLK_CTL_BASE + 0x23A0)
-
static struct resource msm_8960_q6_lpass_resources[] = {
{
- .start = MSM_LPASS_QDSP6SS_PHYS,
- .end = MSM_LPASS_QDSP6SS_PHYS + SZ_256 - 1,
+ .start = 0x28800000,
+ .end = 0x28800000 + SZ_256 - 1,
.flags = IORESOURCE_MEM,
},
+ {
+ .start = LPASS_Q6SS_WDOG_EXPIRED,
+ .end = LPASS_Q6SS_WDOG_EXPIRED,
+ .flags = IORESOURCE_IRQ,
+ },
};
static struct pil_q6v4_pdata msm_8960_q6_lpass_data = {
.strap_tcm_base = 0x01460000,
.strap_ahb_upper = 0x00290000,
.strap_ahb_lower = 0x00000280,
- .aclk_reg = SFAB_LPASS_Q6_ACLK_CTL,
+ .aclk_reg = MSM_CLK_CTL_BASE + 0x23A0,
.name = "q6",
.pas_id = PAS_Q6,
.bus_port = MSM_BUS_MASTER_LPASS_PROC,
};
struct platform_device msm_8960_q6_lpass = {
- .name = "pil_qdsp6v4",
- .id = 0,
+ .name = "pil-q6v4-lpass",
+ .id = -1,
.num_resources = ARRAY_SIZE(msm_8960_q6_lpass_resources),
.resource = msm_8960_q6_lpass_resources,
.dev.platform_data = &msm_8960_q6_lpass_data,
};
-#define MSM_MSS_ENABLE_PHYS 0x08B00000
-#define MSM_FW_QDSP6SS_PHYS 0x08800000
-#define MSS_Q6FW_JTAG_CLK_CTL (MSM_CLK_CTL_BASE + 0x2C6C)
-#define SFAB_MSS_Q6_FW_ACLK_CTL (MSM_CLK_CTL_BASE + 0x2044)
-
-static struct resource msm_8960_q6_mss_fw_resources[] = {
+static struct resource msm_8960_q6_mss_resources[] = {
{
- .start = MSM_FW_QDSP6SS_PHYS,
- .end = MSM_FW_QDSP6SS_PHYS + SZ_256 - 1,
+ .start = 0x08800000,
+ .end = 0x08800000 + SZ_256 - 1,
.flags = IORESOURCE_MEM,
},
{
- .start = MSM_MSS_ENABLE_PHYS,
- .end = MSM_MSS_ENABLE_PHYS + 4 - 1,
- .flags = IORESOURCE_MEM,
- },
-};
-
-static struct pil_q6v4_pdata msm_8960_q6_mss_fw_data = {
- .strap_tcm_base = 0x00400000,
- .strap_ahb_upper = 0x00090000,
- .strap_ahb_lower = 0x00000080,
- .aclk_reg = SFAB_MSS_Q6_FW_ACLK_CTL,
- .jtag_clk_reg = MSS_Q6FW_JTAG_CLK_CTL,
- .name = "modem_fw",
- .depends = "q6",
- .pas_id = PAS_MODEM_FW,
- .bus_port = MSM_BUS_MASTER_MSS_FW_PROC,
-};
-
-struct platform_device msm_8960_q6_mss_fw = {
- .name = "pil_qdsp6v4",
- .id = 1,
- .num_resources = ARRAY_SIZE(msm_8960_q6_mss_fw_resources),
- .resource = msm_8960_q6_mss_fw_resources,
- .dev.platform_data = &msm_8960_q6_mss_fw_data,
-};
-
-#define MSM_SW_QDSP6SS_PHYS 0x08900000
-#define SFAB_MSS_Q6_SW_ACLK_CTL (MSM_CLK_CTL_BASE + 0x2040)
-#define MSS_Q6SW_JTAG_CLK_CTL (MSM_CLK_CTL_BASE + 0x2C68)
-
-static struct resource msm_8960_q6_mss_sw_resources[] = {
- {
- .start = MSM_SW_QDSP6SS_PHYS,
- .end = MSM_SW_QDSP6SS_PHYS + SZ_256 - 1,
+ .start = 0x08B00000,
+ .end = 0x08B00000 + SZ_256 - 1,
.flags = IORESOURCE_MEM,
},
{
- .start = MSM_MSS_ENABLE_PHYS,
- .end = MSM_MSS_ENABLE_PHYS + 4 - 1,
+ .start = 0x08882000,
+ .end = 0x08882000 + SZ_256 - 1,
.flags = IORESOURCE_MEM,
},
+ {
+ .start = 0x08900000,
+ .end = 0x08900000 + SZ_256 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = 0x08982000,
+ .end = 0x08982000 + SZ_256 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = Q6FW_WDOG_EXPIRED_IRQ,
+ .end = Q6FW_WDOG_EXPIRED_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = Q6SW_WDOG_EXPIRED_IRQ,
+ .end = Q6SW_WDOG_EXPIRED_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
};
-static struct pil_q6v4_pdata msm_8960_q6_mss_sw_data = {
- .strap_tcm_base = 0x00420000,
- .strap_ahb_upper = 0x00090000,
- .strap_ahb_lower = 0x00000080,
- .aclk_reg = SFAB_MSS_Q6_SW_ACLK_CTL,
- .jtag_clk_reg = MSS_Q6SW_JTAG_CLK_CTL,
- .name = "modem",
- .depends = "modem_fw",
- .pas_id = PAS_MODEM_SW,
- .bus_port = MSM_BUS_MASTER_MSS_SW_PROC,
+static struct pil_q6v4_pdata msm_8960_q6_mss_data[2] = {
+ {
+ .strap_tcm_base = 0x00400000,
+ .strap_ahb_upper = 0x00090000,
+ .strap_ahb_lower = 0x00000080,
+ .aclk_reg = MSM_CLK_CTL_BASE + 0x2C6C,
+ .jtag_clk_reg = MSM_CLK_CTL_BASE + 0x2044,
+ .name = "modem_fw",
+ .depends = "q6",
+ .pas_id = PAS_MODEM_FW,
+ .bus_port = MSM_BUS_MASTER_MSS_FW_PROC,
+ },
+ {
+ .strap_tcm_base = 0x00420000,
+ .strap_ahb_upper = 0x00090000,
+ .strap_ahb_lower = 0x00000080,
+ .aclk_reg = MSM_CLK_CTL_BASE + 0x2040,
+ .jtag_clk_reg = MSM_CLK_CTL_BASE + 0x2C68,
+ .name = "modem",
+ .depends = "modem_fw",
+ .pas_id = PAS_MODEM_SW,
+ .bus_port = MSM_BUS_MASTER_MSS_SW_PROC,
+ }
};
-struct platform_device msm_8960_q6_mss_sw = {
- .name = "pil_qdsp6v4",
- .id = 2,
- .num_resources = ARRAY_SIZE(msm_8960_q6_mss_sw_resources),
- .resource = msm_8960_q6_mss_sw_resources,
- .dev.platform_data = &msm_8960_q6_mss_sw_data,
+struct platform_device msm_8960_q6_mss = {
+ .name = "pil-q6v4-modem",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(msm_8960_q6_mss_resources),
+ .resource = msm_8960_q6_mss_resources,
+ .dev.platform_data = msm_8960_q6_mss_data,
};
static struct resource msm_8960_riva_resources[] = {
@@ -1444,6 +1443,11 @@
.end = 0x03204000 + SZ_256 - 1,
.flags = IORESOURCE_MEM,
},
+ {
+ .start = RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ,
+ .end = RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
};
struct platform_device msm_8960_riva = {
@@ -1458,9 +1462,24 @@
.id = -1,
};
+static struct resource msm_pil_dsps_resources[] = {
+ {
+ .start = PPSS_WDOG_TIMER_IRQ,
+ .end = PPSS_WDOG_TIMER_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = 0x12080000,
+ .end = 0x12080000 + SZ_8K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
struct platform_device msm_pil_dsps = {
- .name = "pil_dsps",
- .id = -1,
+ .name = "pil_dsps",
+ .id = -1,
+ .resource = msm_pil_dsps_resources,
+ .num_resources = ARRAY_SIZE(msm_pil_dsps_resources),
.dev.platform_data = "dsps",
};
@@ -2935,46 +2954,89 @@
};
static struct msm_dcvs_freq_entry grp3d_freq[] = {
- {0, 0, 333932},
- {0, 0, 497532},
- {0, 0, 707610},
- {0, 0, 844545},
+ {0, 900, 0, 0, 0},
+ {0, 950, 0, 0, 0},
+ {0, 950, 0, 0, 0},
+ {0, 1200, 1, 100, 100},
};
static struct msm_dcvs_freq_entry grp2d_freq[] = {
- {0, 0, 86000},
- {0, 0, 200000},
+ {0, 900, 0, 0, 0},
+ {0, 950, 1, 100, 100},
};
static struct msm_dcvs_core_info grp3d_core_info = {
- .freq_tbl = &grp3d_freq[0],
- .core_param = {
- .max_time_us = 100000,
- .num_freq = ARRAY_SIZE(grp3d_freq),
+ .freq_tbl = &grp3d_freq[0],
+ .core_param = {
+ .core_type = MSM_DCVS_CORE_TYPE_GPU,
},
- .algo_param = {
- .slack_time_us = 39000,
- .disable_pc_threshold = 86000,
- .ss_window_size = 1000000,
- .ss_util_pct = 95,
- .em_max_util_pct = 97,
- .ss_iobusy_conv = 100,
+ .algo_param = {
+ .disable_pc_threshold = 0,
+ .em_win_size_min_us = 100000,
+ .em_win_size_max_us = 300000,
+ .em_max_util_pct = 97,
+ .group_id = 0,
+ .max_freq_chg_time_us = 100000,
+ .slack_mode_dynamic = 0,
+ .slack_weight_thresh_pct = 0,
+ .slack_time_min_us = 39000,
+ .slack_time_max_us = 39000,
+ .ss_win_size_min_us = 1000000,
+ .ss_win_size_max_us = 1000000,
+ .ss_util_pct = 95,
+ .ss_iobusy_conv = 100,
},
+ .energy_coeffs = {
+ .active_coeff_a = 2492,
+ .active_coeff_b = 0,
+ .active_coeff_c = 0,
+
+ .leakage_coeff_a = -17720,
+ .leakage_coeff_b = 37,
+ .leakage_coeff_c = 2729,
+ .leakage_coeff_d = -277,
+ },
+ .power_param = {
+ .current_temp = 25,
+ .num_freq = ARRAY_SIZE(grp3d_freq),
+ }
};
static struct msm_dcvs_core_info grp2d_core_info = {
- .freq_tbl = &grp2d_freq[0],
- .core_param = {
- .max_time_us = 100000,
- .num_freq = ARRAY_SIZE(grp2d_freq),
+ .freq_tbl = &grp2d_freq[0],
+ .core_param = {
+ .core_type = MSM_DCVS_CORE_TYPE_GPU,
},
- .algo_param = {
- .slack_time_us = 39000,
- .disable_pc_threshold = 90000,
- .ss_window_size = 1000000,
- .ss_util_pct = 90,
- .em_max_util_pct = 95,
+ .algo_param = {
+ .disable_pc_threshold = 0,
+ .em_win_size_min_us = 100000,
+ .em_win_size_max_us = 300000,
+ .em_max_util_pct = 97,
+ .group_id = 0,
+ .max_freq_chg_time_us = 100000,
+ .slack_mode_dynamic = 0,
+ .slack_weight_thresh_pct = 0,
+ .slack_time_min_us = 39000,
+ .slack_time_max_us = 39000,
+ .ss_win_size_min_us = 1000000,
+ .ss_win_size_max_us = 1000000,
+ .ss_util_pct = 95,
+ .ss_iobusy_conv = 100,
},
+ .energy_coeffs = {
+ .active_coeff_a = 2492,
+ .active_coeff_b = 0,
+ .active_coeff_c = 0,
+
+ .leakage_coeff_a = -17720,
+ .leakage_coeff_b = 37,
+ .leakage_coeff_c = 2729,
+ .leakage_coeff_d = -277,
+ },
+ .power_param = {
+ .current_temp = 25,
+ .num_freq = ARRAY_SIZE(grp2d_freq),
+ }
};
#ifdef CONFIG_MSM_BUS_SCALING
@@ -3699,6 +3761,37 @@
},
};
+static struct resource resources_rpm_master_stats[] = {
+ {
+ .start = MSM8960_RPM_MASTER_STATS_BASE,
+ .end = MSM8960_RPM_MASTER_STATS_BASE + SZ_256,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static char *master_names[] = {
+ "KPSS",
+ "GPSS",
+ "LPASS",
+ "RIVA",
+ "DSPS",
+};
+
+static struct msm_rpm_master_stats_platform_data msm_rpm_master_stat_pdata = {
+ .masters = master_names,
+ .nomasters = ARRAY_SIZE(master_names),
+};
+
+struct platform_device msm8960_rpm_master_stat_device = {
+ .name = "msm_rpm_master_stat",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(resources_rpm_master_stats),
+ .resource = resources_rpm_master_stats,
+ .dev = {
+ .platform_data = &msm_rpm_master_stat_pdata,
+ },
+};
+
struct platform_device msm_bus_sys_fabric = {
.name = "msm_bus_fabric",
.id = MSM_BUS_FAB_SYSTEM,
@@ -3723,18 +3816,7 @@
/* Sensors DSPS platform data */
#ifdef CONFIG_MSM_DSPS
-#define PPSS_DSPS_TCM_CODE_BASE 0x12000000
-#define PPSS_DSPS_TCM_CODE_SIZE 0x28000
-#define PPSS_DSPS_TCM_BUF_BASE 0x12040000
-#define PPSS_DSPS_TCM_BUF_SIZE 0x4000
-#define PPSS_DSPS_PIPE_BASE 0x12800000
-#define PPSS_DSPS_PIPE_SIZE 0x4000
-#define PPSS_DSPS_DDR_BASE 0x8fe00000
-#define PPSS_DSPS_DDR_SIZE 0x100000
-#define PPSS_SMEM_BASE 0x80000000
-#define PPSS_SMEM_SIZE 0x200000
-#define PPSS_REG_PHYS_BASE 0x12080000
-#define PPSS_WDOG_UNMASKED_INT_EN 0x1808
+#define PPSS_REG_PHYS_BASE 0x12080000
static struct dsps_clk_info dsps_clks[] = {};
static struct dsps_regulator_info dsps_regs[] = {};
@@ -3752,17 +3834,6 @@
.regs = dsps_regs,
.regs_num = ARRAY_SIZE(dsps_regs),
.dsps_pwr_ctl_en = 1,
- .tcm_code_start = PPSS_DSPS_TCM_CODE_BASE,
- .tcm_code_size = PPSS_DSPS_TCM_CODE_SIZE,
- .tcm_buf_start = PPSS_DSPS_TCM_BUF_BASE,
- .tcm_buf_size = PPSS_DSPS_TCM_BUF_SIZE,
- .pipe_start = PPSS_DSPS_PIPE_BASE,
- .pipe_size = PPSS_DSPS_PIPE_SIZE,
- .ddr_start = PPSS_DSPS_DDR_BASE,
- .ddr_size = PPSS_DSPS_DDR_SIZE,
- .smem_start = PPSS_SMEM_BASE,
- .smem_size = PPSS_SMEM_SIZE,
- .ppss_wdog_unmasked_int_en_reg = PPSS_WDOG_UNMASKED_INT_EN,
.signature = DSPS_SIGNATURE,
};
@@ -3773,12 +3844,6 @@
.name = "ppss_reg",
.flags = IORESOURCE_MEM,
},
- {
- .start = PPSS_WDOG_TIMER_IRQ,
- .end = PPSS_WDOG_TIMER_IRQ,
- .name = "ppss_wdog",
- .flags = IORESOURCE_IRQ,
- },
};
struct platform_device msm_dsps_device = {
@@ -4023,53 +4088,6 @@
.resource = msm_ebi1_ch1_erp_resources,
};
-static int msm8960_LPM_latency = 1000; /* >100 usec for WFI */
-
-struct platform_device msm8960_cpu_idle_device = {
- .name = "msm_cpu_idle",
- .id = -1,
- .dev = {
- .platform_data = &msm8960_LPM_latency,
- },
-};
-
-static struct msm_dcvs_freq_entry msm8960_freq[] = {
- { 384000, 166981, 345600},
- { 702000, 213049, 632502},
- {1026000, 285712, 925613},
- {1242000, 383945, 1176550},
- {1458000, 419729, 1465478},
- {1512000, 434116, 1546674},
-
-};
-
-static struct msm_dcvs_core_info msm8960_core_info = {
- .freq_tbl = &msm8960_freq[0],
- .core_param = {
- .max_time_us = 100000,
- .num_freq = ARRAY_SIZE(msm8960_freq),
- },
- .algo_param = {
- .slack_time_us = 58000,
- .scale_slack_time = 0,
- .scale_slack_time_pct = 0,
- .disable_pc_threshold = 1458000,
- .em_window_size = 100000,
- .em_max_util_pct = 97,
- .ss_window_size = 1000000,
- .ss_util_pct = 95,
- .ss_iobusy_conv = 100,
- },
-};
-
-struct platform_device msm8960_msm_gov_device = {
- .name = "msm_dcvs_gov",
- .id = -1,
- .dev = {
- .platform_data = &msm8960_core_info,
- },
-};
-
static struct resource msm_cache_erp_resources[] = {
{
.name = "l1_irq",
diff --git a/arch/arm/mach-msm/devices-9615.c b/arch/arm/mach-msm/devices-9615.c
index 0a9bbf6..fe3a4d5 100644
--- a/arch/arm/mach-msm/devices-9615.c
+++ b/arch/arm/mach-msm/devices-9615.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -64,6 +64,7 @@
#define MSM_GPIO_I2C_CLK 16
#define MSM_GPIO_I2C_SDA 17
+#define MSM9615_RPM_MASTER_STATS_BASE 0x10A700
static struct msm_watchdog_pdata msm_watchdog_pdata = {
.pet_time = 10000,
@@ -570,6 +571,15 @@
.name = "msm-dai-q6",
.id = PRIMARY_I2S_TX,
};
+struct platform_device msm_i2s_cpudai4 = {
+ .name = "msm-dai-q6",
+ .id = SECONDARY_I2S_RX,
+};
+
+struct platform_device msm_i2s_cpudai5 = {
+ .name = "msm-dai-q6",
+ .id = SECONDARY_I2S_TX,
+};
struct platform_device msm_voip = {
.name = "msm-voip-dsp",
.id = -1,
@@ -704,6 +714,41 @@
.id = -1,
};
+static struct resource msm_9615_q6_lpass_resources[] = {
+ {
+ .start = LPASS_Q6SS_WDOG_EXPIRED,
+ .end = LPASS_Q6SS_WDOG_EXPIRED,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device msm_9615_q6_lpass = {
+ .name = "pil-q6v4-lpass",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(msm_9615_q6_lpass_resources),
+ .resource = msm_9615_q6_lpass_resources,
+};
+
+static struct resource msm_9615_q6_mss_resources[] = {
+ {
+ .start = Q6FW_WDOG_EXPIRED_IRQ,
+ .end = Q6FW_WDOG_EXPIRED_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = Q6SW_WDOG_EXPIRED_IRQ,
+ .end = Q6SW_WDOG_EXPIRED_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device msm_9615_q6_mss = {
+ .name = "pil-q6v4-modem",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(msm_9615_q6_mss_resources),
+ .resource = msm_9615_q6_mss_resources,
+};
+
#ifdef CONFIG_HW_RANDOM_MSM
/* PRNG device */
#define MSM_PRNG_PHYS 0x1A500000
@@ -1316,8 +1361,8 @@
};
static struct msm_rpmstats_platform_data msm_rpm_stat_pdata = {
- .phys_addr_base = 0x0010D204,
- .phys_size = SZ_8K,
+ .phys_addr_base = 0x0010DD04,
+ .phys_size = SZ_256,
};
struct platform_device msm9615_rpm_stat_device = {
@@ -1328,6 +1373,35 @@
},
};
+static struct resource resources_rpm_master_stats[] = {
+ {
+ .start = MSM9615_RPM_MASTER_STATS_BASE,
+ .end = MSM9615_RPM_MASTER_STATS_BASE + SZ_256,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static char *master_names[] = {
+ "KPSS",
+ "MPSS",
+ "LPASS",
+};
+
+static struct msm_rpm_master_stats_platform_data msm_rpm_master_stat_pdata = {
+ .masters = master_names,
+ .nomasters = ARRAY_SIZE(master_names),
+};
+
+struct platform_device msm9615_rpm_master_stat_device = {
+ .name = "msm_rpm_master_stat",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(resources_rpm_master_stats),
+ .resource = resources_rpm_master_stats,
+ .dev = {
+ .platform_data = &msm_rpm_master_stat_pdata,
+ },
+};
+
static struct msm_rpm_log_platform_data msm_rpm_log_pdata = {
.phys_addr_base = 0x0010AC00,
.reg_offsets = {
diff --git a/arch/arm/mach-msm/devices-iommu.c b/arch/arm/mach-msm/devices-iommu.c
index 6434a63..63c1dbd 100644
--- a/arch/arm/mach-msm/devices-iommu.c
+++ b/arch/arm/mach-msm/devices-iommu.c
@@ -1017,13 +1017,13 @@
ARRAY_SIZE(msm_iommu_gfx2d_devs));
}
- if (cpu_is_apq8064() || cpu_is_msm8960ab() || cpu_is_apq8064ab()) {
+ if (soc_class_is_apq8064() || cpu_is_msm8960ab()) {
platform_add_devices(msm_iommu_jpegd_devs,
ARRAY_SIZE(msm_iommu_jpegd_devs));
platform_add_devices(msm_iommu_adreno3xx_gfx_devs,
ARRAY_SIZE(msm_iommu_adreno3xx_gfx_devs));
}
- if (cpu_is_apq8064() || cpu_is_apq8064ab())
+ if (soc_class_is_apq8064())
platform_add_devices(msm_iommu_vcap_devs,
ARRAY_SIZE(msm_iommu_vcap_devs));
@@ -1039,14 +1039,14 @@
ARRAY_SIZE(msm_iommu_gfx2d_ctx_devs));
}
- if (cpu_is_apq8064() || cpu_is_msm8960ab() || cpu_is_apq8064ab()) {
+ if (soc_class_is_apq8064() || cpu_is_msm8960ab()) {
platform_add_devices(msm_iommu_jpegd_ctx_devs,
ARRAY_SIZE(msm_iommu_jpegd_ctx_devs));
platform_add_devices(msm_iommu_adreno3xx_ctx_devs,
ARRAY_SIZE(msm_iommu_adreno3xx_ctx_devs));
}
- if (cpu_is_apq8064() || cpu_is_apq8064ab())
+ if (soc_class_is_apq8064())
platform_add_devices(msm_iommu_vcap_ctx_devs,
ARRAY_SIZE(msm_iommu_vcap_ctx_devs));
@@ -1081,12 +1081,12 @@
for (i = 0; i < ARRAY_SIZE(msm_iommu_jpegd_devs); i++)
platform_device_unregister(msm_iommu_jpegd_devs[i]);
}
- if (cpu_is_apq8064() || cpu_is_apq8064ab()) {
+ if (soc_class_is_apq8064()) {
for (i = 0; i < ARRAY_SIZE(msm_iommu_vcap_ctx_devs); i++)
platform_device_unregister(msm_iommu_vcap_ctx_devs[i]);
}
- if (cpu_is_apq8064() || cpu_is_msm8960ab() || cpu_is_apq8064ab()) {
+ if (soc_class_is_apq8064() || cpu_is_msm8960ab()) {
for (i = 0; i < ARRAY_SIZE(msm_iommu_adreno3xx_ctx_devs);
i++)
platform_device_unregister(
@@ -1097,7 +1097,7 @@
platform_device_unregister(
msm_iommu_jpegd_ctx_devs[i]);
- if (cpu_is_apq8064() || cpu_is_apq8064ab()) {
+ if (soc_class_is_apq8064()) {
for (i = 0; i < ARRAY_SIZE(msm_iommu_vcap_devs);
i++)
platform_device_unregister(
diff --git a/arch/arm/mach-msm/devices-msm7x27a.c b/arch/arm/mach-msm/devices-msm7x27a.c
index 01c4a9c..9943812 100644
--- a/arch/arm/mach-msm/devices-msm7x27a.c
+++ b/arch/arm/mach-msm/devices-msm7x27a.c
@@ -1606,6 +1606,26 @@
},
};
+static struct resource pl310_resources[] = {
+ {
+ .start = 0xC0400000,
+ .end = 0xC0400000 + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "l2_irq",
+ .start = MSM8625_INT_L2CC_INTR,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device pl310_erp_device = {
+ .name = "pl310_erp",
+ .id = -1,
+ .resource = pl310_resources,
+ .num_resources = ARRAY_SIZE(pl310_resources),
+};
+
enum {
MSM8625,
MSM8625A,
@@ -1664,12 +1684,13 @@
* These are various Vdd levels supported by PMIC
*/
static uint32_t msm_c2_pmic_mv[] __initdata = {
- 1300000, 1287500, 1275000, 1262500, 1250000,
- 1237500, 1225000, 1212500, 1200000, 1187500,
- 1175000, 1162500, 1150000, 1137500, 1125000,
- 1112500, 1100000, 1087500, 1075000, 1062500,
- 1050000, 1037500, 1025000, 1012500, 0, 0, 0,
- 0, 0, 0, 0, 1000,
+ 1350000, 1337500, 1325000, 1312500, 1300000,
+ 1287500, 1275000, 1262500, 1250000, 1237500,
+ 1225000, 1212500, 1200000, 1187500, 1175000,
+ 1162500, 1150000, 1137500, 1125000, 1112500,
+ 1100000, 1087500, 1075000, 1062500, 0,
+ 0, 0, 0, 0, 0,
+ 0, 1050000,
};
/**
@@ -1709,9 +1730,9 @@
.step_quot = ~0,
.tgt_volt_offset = 0,
.turbo_Vmax = 1350000,
- .turbo_Vmin = 1200000,
+ .turbo_Vmin = 1150000,
.nom_Vmax = 1350000,
- .nom_Vmin = 1050000,
+ .nom_Vmin = 1150000,
.calibrated_uV = 1300000,
},
};
@@ -1749,7 +1770,7 @@
static struct msm_cpr_config msm_cpr_pdata = {
.ref_clk_khz = 19200,
- .delay_us = 25000,
+ .delay_us = 1000,
.irq_line = 0,
.cpr_mode_data = msm_cpr_mode_data,
.tgt_count_div_N = 1,
@@ -1757,7 +1778,7 @@
.ceiling = 40,
.sw_vlevel = 20,
.up_threshold = 1,
- .dn_threshold = 3,
+ .dn_threshold = 4,
.up_margin = 0,
.dn_margin = 0,
.max_nom_freq = 700800,
@@ -1819,6 +1840,22 @@
* enough to represent the value of maximum quot
*/
msm_cpr_pdata.max_quot = cpr_info->turbo_quot * 10 + 600;
+ /**
+ * Fused Quot value for 1.2GHz on a 1.2GHz part is lower than
+ * the quot value calculated using the scaling factor formula for
+ * 1.2GHz when running on a 1.4GHz part. So, prop up the Quot for
+ * a 1.2GHz part by a chip characterization recommended value.
+ * Ditto for a 1.0GHz part.
+ */
+ if (msm8625_cpu_id() == MSM8625A) {
+ msm_cpr_pdata.max_quot += 100;
+ if (msm_cpr_pdata.max_quot > 1400)
+ msm_cpr_pdata.max_quot = 1400;
+ } else if (msm8625_cpu_id() == MSM8625) {
+ msm_cpr_pdata.max_quot += 120;
+ if (msm_cpr_pdata.max_quot > 1350)
+ msm_cpr_pdata.max_quot = 1350;
+ }
/**
* Bits 4:0 of pvs_fuse provide mapping to the safe boot up voltage.
@@ -1827,10 +1864,25 @@
msm_cpr_mode_data[TURBO_MODE].calibrated_uV =
msm_c2_pmic_mv[cpr_info->pvs_fuse & 0x1F];
+ if ((cpr_info->floor_fuse & 0x3) == 0x0) {
+ msm_cpr_mode_data[TURBO_MODE].nom_Vmin = 1000000;
+ msm_cpr_mode_data[TURBO_MODE].turbo_Vmin = 1100000;
+ } else if ((cpr_info->floor_fuse & 0x3) == 0x1) {
+ msm_cpr_mode_data[TURBO_MODE].nom_Vmin = 1050000;
+ msm_cpr_mode_data[TURBO_MODE].turbo_Vmin = 1100000;
+ } else if ((cpr_info->floor_fuse & 0x3) == 0x2) {
+ msm_cpr_mode_data[TURBO_MODE].nom_Vmin = 1100000;
+ msm_cpr_mode_data[TURBO_MODE].turbo_Vmin = 1100000;
+ }
+
pr_debug("%s: cpr: ring_osc: 0x%x\n", __func__,
msm_cpr_mode_data[TURBO_MODE].ring_osc);
pr_debug("%s: cpr: turbo_quot: 0x%x\n", __func__, cpr_info->turbo_quot);
pr_debug("%s: cpr: pvs_fuse: 0x%x\n", __func__, cpr_info->pvs_fuse);
+ pr_debug("%s: cpr: floor_fuse: 0x%x\n", __func__, cpr_info->floor_fuse);
+ pr_debug("%s: cpr: nom_Vmin: %d, turbo_Vmin: %d\n", __func__,
+ msm_cpr_mode_data[TURBO_MODE].nom_Vmin,
+ msm_cpr_mode_data[TURBO_MODE].turbo_Vmin);
kfree(cpr_info);
if (msm8625_cpu_id() == MSM8625A)
@@ -1937,6 +1989,11 @@
(SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 2))
msm_cpr_init();
+ if (!cpu_is_msm8625())
+ pl310_resources[1].start = INT_L2CC_INTR;
+
+ platform_device_register(&pl310_erp_device);
+
return 0;
}
diff --git a/arch/arm/mach-msm/devices-msm7x2xa.h b/arch/arm/mach-msm/devices-msm7x2xa.h
index 8b59b14..614037c 100644
--- a/arch/arm/mach-msm/devices-msm7x2xa.h
+++ b/arch/arm/mach-msm/devices-msm7x2xa.h
@@ -33,6 +33,6 @@
void __init msm8x25_spm_device_init(void);
void __init msm_pm_register_cpr_ops(void);
void __init msm8x25_kgsl_3d0_init(void);
-void __iomem *core1_reset_base(void);
+void __iomem *core_reset_base(unsigned int);
extern void setup_mm_for_reboot(void);
#endif
diff --git a/arch/arm/mach-msm/devices-msm8x60.c b/arch/arm/mach-msm/devices-msm8x60.c
index b88fb50..5554eb8 100644
--- a/arch/arm/mach-msm/devices-msm8x60.c
+++ b/arch/arm/mach-msm/devices-msm8x60.c
@@ -192,7 +192,9 @@
gic_init(0, GIC_PPI_START, MSM_QGIC_DIST_BASE, (void *)MSM_QGIC_CPU_BASE);
}
-#define MSM_LPASS_QDSP6SS_PHYS 0x28800000
+#define MSM_LPASS_QDSP6SS_PHYS 0x28800000
+#define MSM_LPASS_QDSP6SS_WDOG_PHYS 0x28882000
+#define MSM_LPASS_QDSP6SS_IM_PHYS 0x288A0000
static struct resource msm_8660_q6_resources[] = {
{
@@ -200,6 +202,21 @@
.end = MSM_LPASS_QDSP6SS_PHYS + SZ_256 - 1,
.flags = IORESOURCE_MEM,
},
+ {
+ .start = MSM_LPASS_QDSP6SS_IM_PHYS,
+ .end = MSM_LPASS_QDSP6SS_IM_PHYS + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MSM_LPASS_QDSP6SS_WDOG_PHYS,
+ .end = MSM_LPASS_QDSP6SS_WDOG_PHYS + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = LPASS_Q6SS_WDOG_EXPIRED,
+ .end = LPASS_Q6SS_WDOG_EXPIRED,
+ .flags = IORESOURCE_IRQ,
+ },
};
struct platform_device msm_pil_q6v3 = {
@@ -1605,16 +1622,6 @@
/* Sensors DSPS platform data */
#ifdef CONFIG_MSM_DSPS
-#define PPSS_DSPS_TCM_CODE_BASE 0x12000000
-#define PPSS_DSPS_TCM_CODE_SIZE 0x28000
-#define PPSS_DSPS_TCM_BUF_BASE 0x12040000
-#define PPSS_DSPS_TCM_BUF_SIZE 0x4000
-#define PPSS_DSPS_PIPE_BASE 0x12800000
-#define PPSS_DSPS_PIPE_SIZE 0x0 /* 8660 V2 does not use PIPE memory */
-#define PPSS_DSPS_DDR_BASE 0x8fe00000
-#define PPSS_DSPS_DDR_SIZE 0x0 /* 8660 V2 does not use DDR memory */
-#define PPSS_SMEM_BASE 0x40000000
-#define PPSS_SMEM_SIZE 0x4000
#define PPSS_REG_PHYS_BASE 0x12080000
#define PPSS_PAUSE_REG 0x1804
@@ -1679,16 +1686,6 @@
.regs = dsps_regs,
.regs_num = ARRAY_SIZE(dsps_regs),
.init = dsps_init1,
- .tcm_code_start = PPSS_DSPS_TCM_CODE_BASE,
- .tcm_code_size = PPSS_DSPS_TCM_CODE_SIZE,
- .tcm_buf_start = PPSS_DSPS_TCM_BUF_BASE,
- .tcm_buf_size = PPSS_DSPS_TCM_BUF_SIZE,
- .pipe_start = PPSS_DSPS_PIPE_BASE,
- .pipe_size = PPSS_DSPS_PIPE_SIZE,
- .ddr_start = PPSS_DSPS_DDR_BASE,
- .ddr_size = PPSS_DSPS_DDR_SIZE,
- .smem_start = PPSS_SMEM_BASE,
- .smem_size = PPSS_SMEM_SIZE,
.ppss_pause_reg = PPSS_PAUSE_REG,
.signature = DSPS_SIGNATURE,
};
diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h
index 0506217..8f02050 100644
--- a/arch/arm/mach-msm/devices.h
+++ b/arch/arm/mach-msm/devices.h
@@ -1,7 +1,7 @@
/* linux/arch/arm/mach-msm/devices.h
*
* Copyright (C) 2008 Google, Inc.
- * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-2012, 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
@@ -244,15 +244,17 @@
extern struct platform_device msm_cpudai_incall_record_tx;
extern struct platform_device msm_i2s_cpudai0;
extern struct platform_device msm_i2s_cpudai1;
-
+extern struct platform_device msm_i2s_cpudai4;
+extern struct platform_device msm_i2s_cpudai5;
extern struct platform_device msm_pil_q6v3;
extern struct platform_device msm_pil_modem;
extern struct platform_device msm_pil_tzapps;
extern struct platform_device msm_pil_dsps;
extern struct platform_device msm_pil_vidc;
extern struct platform_device msm_8960_q6_lpass;
-extern struct platform_device msm_8960_q6_mss_fw;
-extern struct platform_device msm_8960_q6_mss_sw;
+extern struct platform_device msm_9615_q6_lpass;
+extern struct platform_device msm_8960_q6_mss;
+extern struct platform_device msm_9615_q6_mss;
extern struct platform_device msm_8960_riva;
extern struct platform_device msm_gss;
@@ -335,10 +337,12 @@
extern struct platform_device msm8960_rpm_device;
extern struct platform_device msm8960_rpm_stat_device;
+extern struct platform_device msm8960_rpm_master_stat_device;
extern struct platform_device msm8960_rpm_log_device;
extern struct platform_device msm8930_rpm_device;
extern struct platform_device msm8930_rpm_stat_device;
+extern struct platform_device msm8930_rpm_master_stat_device;
extern struct platform_device msm8930_rpm_log_device;
extern struct platform_device msm8930_rpm_rbcpr_device;
@@ -348,10 +352,12 @@
extern struct platform_device msm9615_rpm_device;
extern struct platform_device msm9615_rpm_stat_device;
+extern struct platform_device msm9615_rpm_master_stat_device;
extern struct platform_device msm9615_rpm_log_device;
extern struct platform_device apq8064_rpm_device;
extern struct platform_device apq8064_rpm_stat_device;
+extern struct platform_device apq8064_rpm_master_stat_device;
extern struct platform_device apq8064_rpm_log_device;
extern struct platform_device msm_device_rng;
@@ -394,12 +400,6 @@
extern struct platform_device *msm_8974_stub_regulator_devices[];
extern int msm_8974_stub_regulator_devices_len;
-extern struct platform_device msm8960_cpu_idle_device;
-extern struct platform_device msm8930_cpu_idle_device;
-extern struct platform_device apq8064_cpu_idle_device;
-
-extern struct platform_device msm8960_msm_gov_device;
-extern struct platform_device msm8930_msm_gov_device;
extern struct platform_device apq8064_msm_gov_device;
extern struct platform_device msm_bus_8930_apps_fabric;
@@ -447,6 +447,8 @@
extern struct platform_device msm8960ab_device_acpuclk;
extern struct platform_device msm9615_device_acpuclk;
+extern struct platform_device apq8064_msm_mpd_device;
+
extern struct platform_device msm_gpio_device;
extern struct platform_device apq_cpudai_mi2s;
diff --git a/arch/arm/mach-msm/gss-8064.c b/arch/arm/mach-msm/gss-8064.c
deleted file mode 100644
index ba6af61..0000000
--- a/arch/arm/mach-msm/gss-8064.c
+++ /dev/null
@@ -1,265 +0,0 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/reboot.h>
-#include <linux/workqueue.h>
-#include <linux/io.h>
-#include <linux/jiffies.h>
-#include <linux/stringify.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/miscdevice.h>
-#include <linux/fs.h>
-
-#include <mach/irqs.h>
-#include <mach/msm_smsm.h>
-#include <mach/scm.h>
-#include <mach/peripheral-loader.h>
-#include <mach/subsystem_restart.h>
-#include <mach/subsystem_notif.h>
-#include <mach/socinfo.h>
-
-#include "smd_private.h"
-#include "modem_notifier.h"
-#include "ramdump.h"
-
-static struct gss_8064_data {
- struct miscdevice gss_dev;
- void *pil_handle;
- void *gss_ramdump_dev;
- void *smem_ramdump_dev;
-} gss_data;
-
-static int crash_shutdown;
-
-static struct subsys_device *gss_8064_dev;
-
-#define MAX_SSR_REASON_LEN 81U
-
-static void log_gss_sfr(void)
-{
- u32 size;
- char *smem_reason, reason[MAX_SSR_REASON_LEN];
-
- smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size);
- if (!smem_reason || !size) {
- pr_err("GSS subsystem failure reason: (unknown, smem_get_entry failed).\n");
- return;
- }
- if (!smem_reason[0]) {
- pr_err("GSS subsystem failure reason: (unknown, init string found).\n");
- return;
- }
-
- size = min(size, MAX_SSR_REASON_LEN-1);
- memcpy(reason, smem_reason, size);
- reason[size] = '\0';
- pr_err("GSS subsystem failure reason: %s.\n", reason);
-
- smem_reason[0] = '\0';
- wmb();
-}
-
-static void restart_gss(void)
-{
- log_gss_sfr();
- subsystem_restart_dev(gss_8064_dev);
-}
-
-static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
-{
- /* Ignore if we're the one that set SMSM_RESET */
- if (crash_shutdown)
- return;
-
- if (new_state & SMSM_RESET) {
- pr_err("GSS SMSM state changed to SMSM_RESET.\n"
- "Probable err_fatal on the GSS. "
- "Calling subsystem restart...\n");
- restart_gss();
- }
-}
-
-#define Q6_FW_WDOG_ENABLE 0x08882024
-#define Q6_SW_WDOG_ENABLE 0x08982024
-static int gss_shutdown(const struct subsys_desc *desc)
-{
- pil_force_shutdown("gss");
- disable_irq_nosync(GSS_A5_WDOG_EXPIRED);
-
- return 0;
-}
-
-static int gss_powerup(const struct subsys_desc *desc)
-{
- pil_force_boot("gss");
- enable_irq(GSS_A5_WDOG_EXPIRED);
- return 0;
-}
-
-void gss_crash_shutdown(const struct subsys_desc *desc)
-{
- crash_shutdown = 1;
- smsm_reset_modem(SMSM_RESET);
-}
-
-/* FIXME: Get address, size from PIL */
-static struct ramdump_segment gss_segments[] = {
- {0x89000000, 0x00D00000}
-};
-
-static struct ramdump_segment smem_segments[] = {
- {0x80000000, 0x00200000},
-};
-
-static int gss_ramdump(int enable,
- const struct subsys_desc *crashed_subsys)
-{
- int ret = 0;
-
- if (enable) {
- ret = do_ramdump(gss_data.gss_ramdump_dev, gss_segments,
- ARRAY_SIZE(gss_segments));
-
- if (ret < 0) {
- pr_err("Unable to dump gss memory (rc = %d).\n",
- ret);
- goto out;
- }
-
- ret = do_ramdump(gss_data.smem_ramdump_dev, smem_segments,
- ARRAY_SIZE(smem_segments));
-
- if (ret < 0) {
- pr_err("Unable to dump smem memory (rc = %d).\n", ret);
- goto out;
- }
- }
-
-out:
- return ret;
-}
-
-static irqreturn_t gss_wdog_bite_irq(int irq, void *dev_id)
-{
- pr_err("Watchdog bite received from GSS!\n");
- restart_gss();
-
- return IRQ_HANDLED;
-}
-
-static struct subsys_desc gss_8064 = {
- .name = "gss",
- .shutdown = gss_shutdown,
- .powerup = gss_powerup,
- .ramdump = gss_ramdump,
- .crash_shutdown = gss_crash_shutdown
-};
-
-static int gss_subsystem_restart_init(void)
-{
- gss_8064_dev = subsys_register(&gss_8064);
- if (IS_ERR(gss_8064_dev))
- return PTR_ERR(gss_8064_dev);
- return 0;
-}
-
-static int gss_open(struct inode *inode, struct file *filep)
-{
- void *ret;
- gss_data.pil_handle = ret = pil_get("gss");
- if (!ret)
- pr_debug("%s - pil_get returned NULL\n", __func__);
- return 0;
-}
-
-static int gss_release(struct inode *inode, struct file *filep)
-{
- pil_put(gss_data.pil_handle);
- pr_debug("%s pil_put called on GSS\n", __func__);
- return 0;
-}
-
-const struct file_operations gss_file_ops = {
- .open = gss_open,
- .release = gss_release,
-};
-
-static int __init gss_8064_init(void)
-{
- int ret;
-
- if (!(cpu_is_apq8064() || cpu_is_apq8064ab()))
- return -ENODEV;
-
- ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
- smsm_state_cb, 0);
-
- if (ret < 0)
- pr_err("%s: Unable to register SMSM callback! (%d)\n",
- __func__, ret);
-
- ret = request_irq(GSS_A5_WDOG_EXPIRED, gss_wdog_bite_irq,
- IRQF_TRIGGER_RISING, "gss_a5_wdog", NULL);
-
- if (ret < 0) {
- pr_err("%s: Unable to request gss watchdog IRQ. (%d)\n",
- __func__, ret);
- disable_irq_nosync(GSS_A5_WDOG_EXPIRED);
- goto out;
- }
-
- ret = gss_subsystem_restart_init();
-
- if (ret < 0) {
- pr_err("%s: Unable to reg with subsystem restart. (%d)\n",
- __func__, ret);
- goto out;
- }
-
- gss_data.gss_dev.minor = MISC_DYNAMIC_MINOR;
- gss_data.gss_dev.name = "gss";
- gss_data.gss_dev.fops = &gss_file_ops;
- ret = misc_register(&gss_data.gss_dev);
-
- if (ret) {
- pr_err("%s: misc_registers failed for %s (%d)", __func__,
- gss_data.gss_dev.name, ret);
- goto out;
- }
-
- gss_data.gss_ramdump_dev = create_ramdump_device("gss");
-
- if (!gss_data.gss_ramdump_dev) {
- pr_err("%s: Unable to create gss ramdump device. (%d)\n",
- __func__, -ENOMEM);
- ret = -ENOMEM;
- goto out;
- }
-
- gss_data.smem_ramdump_dev = create_ramdump_device("smem-gss");
-
- if (!gss_data.smem_ramdump_dev) {
- pr_err("%s: Unable to create smem ramdump device. (%d)\n",
- __func__, -ENOMEM);
- ret = -ENOMEM;
- goto out;
- }
-
- pr_info("%s: gss fatal driver init'ed.\n", __func__);
-out:
- return ret;
-}
-
-module_init(gss_8064_init);
diff --git a/arch/arm/mach-msm/headsmp.S b/arch/arm/mach-msm/headsmp.S
index e5ad312..0537421 100644
--- a/arch/arm/mach-msm/headsmp.S
+++ b/arch/arm/mach-msm/headsmp.S
@@ -35,8 +35,6 @@
* we've been released from the holding pen: secondary_stack
* should now contain the SVC stack for this core
*/
- mvn r7, #0 @ -1 to registers
- str r7,[r6] @ back to the pen for ack
b secondary_startup
ENDPROC(msm_secondary_startup)
diff --git a/arch/arm/mach-msm/hotplug.c b/arch/arm/mach-msm/hotplug.c
index d1d9f4b..f296aae 100644
--- a/arch/arm/mach-msm/hotplug.c
+++ b/arch/arm/mach-msm/hotplug.c
@@ -43,7 +43,7 @@
{
}
-static inline void platform_do_lowpower(unsigned int cpu)
+static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
{
/* Just enter wfi for now. TODO: Properly shut off the cpu. */
for (;;) {
@@ -53,9 +53,6 @@
/*
* OK, proper wakeup, we're done
*/
- pen_release = -1;
- dmac_flush_range((char *)&pen_release,
- (char *)&pen_release + sizeof(pen_release));
break;
}
@@ -67,9 +64,7 @@
* possible, since we are currently running incoherently, and
* therefore cannot safely call printk() or anything else
*/
- dmac_inv_range((char *)&pen_release,
- (char *)&pen_release + sizeof(pen_release));
- pr_debug("CPU%u: spurious wakeup call\n", cpu);
+ (*spurious)++;
}
}
@@ -85,6 +80,8 @@
*/
void platform_cpu_die(unsigned int cpu)
{
+ int spurious = 0;
+
if (unlikely(cpu != smp_processor_id())) {
pr_crit("%s: running on %u, should be %u\n",
__func__, smp_processor_id(), cpu);
@@ -95,10 +92,13 @@
* we're ready for shutdown now, so do it
*/
cpu_enter_lowpower();
- platform_do_lowpower(cpu);
+ platform_do_lowpower(cpu, &spurious);
pr_debug("CPU%u: %s: normal wakeup\n", cpu, __func__);
cpu_leave_lowpower();
+
+ if (spurious)
+ pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious);
}
int platform_cpu_disable(unsigned int cpu)
diff --git a/arch/arm/mach-msm/idle-v7.S b/arch/arm/mach-msm/idle-v7.S
index 8eed48d..1d8f313 100644
--- a/arch/arm/mach-msm/idle-v7.S
+++ b/arch/arm/mach-msm/idle-v7.S
@@ -330,9 +330,14 @@
ldr pc, [r1] /* jump */
ENTRY(msm_pm_set_l2_flush_flag)
- ldr r1, =msm_pm_flush_l2_flag
- str r0, [r1]
- bx lr
+ ldr r1, =msm_pm_flush_l2_flag
+ str r0, [r1]
+ bx lr
+
+ENTRY(msm_pm_get_l2_flush_flag)
+ ldr r1, =msm_pm_flush_l2_flag
+ ldr r0, [r1]
+ bx lr
.data
diff --git a/arch/arm/mach-msm/idle.h b/arch/arm/mach-msm/idle.h
index 4abdd04..7a939ab 100644
--- a/arch/arm/mach-msm/idle.h
+++ b/arch/arm/mach-msm/idle.h
@@ -38,6 +38,7 @@
#ifdef CONFIG_CPU_V7
void msm_pm_boot_entry(void);
void msm_pm_set_l2_flush_flag(unsigned int flag);
+int msm_pm_get_l2_flush_flag(void);
extern unsigned long msm_pm_pc_pgd;
extern unsigned long msm_pm_boot_vector[NR_CPUS];
extern uint32_t target_type;
diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h
index 433fee3..ff4776a 100644
--- a/arch/arm/mach-msm/include/mach/board.h
+++ b/arch/arm/mach-msm/include/mach/board.h
@@ -398,6 +398,7 @@
u32 splash_screen_addr;
u32 splash_screen_size;
char mdp_iommu_split_domain;
+ u32 avtimer_phy;
};
@@ -586,6 +587,8 @@
void msm_map_msm8226_io(void);
void msm8226_init_irq(void);
void msm8226_init_gpiomux(void);
+void msm_map_msm8910_io(void);
+void msm8910_init_irq(void);
struct mmc_platform_data;
int msm_add_sdcc(unsigned int controller,
diff --git a/arch/arm/mach-msm/include/mach/iommu.h b/arch/arm/mach-msm/include/mach/iommu.h
index f63af64..69ddeb9 100644
--- a/arch/arm/mach-msm/include/mach/iommu.h
+++ b/arch/arm/mach-msm/include/mach/iommu.h
@@ -20,9 +20,11 @@
extern pgprot_t pgprot_kernel;
extern struct platform_device *msm_iommu_root_dev;
+extern struct bus_type msm_iommu_sec_bus_type;
/* Domain attributes */
#define MSM_IOMMU_DOMAIN_PT_CACHEABLE 0x1
+#define MSM_IOMMU_DOMAIN_PT_SECURE 0x2
/* Mask for the cache policy attribute */
#define MSM_IOMMU_CP_MASK 0x03
@@ -88,7 +90,6 @@
* @aclk: Alternate clock for this IOMMU core, if any
* @name: Human-readable name of this IOMMU device
* @gdsc: Regulator needed to power this HW block (v2 only)
- * @nsmr: Size of the SMT on this HW block (v2 only)
* @bfb_settings: Optional BFB performance tuning parameters
*
* A msm_iommu_drvdata holds the global driver data about a single piece
@@ -103,8 +104,8 @@
struct clk *aclk;
const char *name;
struct regulator *gdsc;
- unsigned int nsmr;
struct msm_iommu_bfb_settings *bfb_settings;
+ int sec_id;
};
/**
diff --git a/arch/arm/mach-msm/include/mach/iommu_domains.h b/arch/arm/mach-msm/include/mach/iommu_domains.h
index 1d538f2..01bc479 100644
--- a/arch/arm/mach-msm/include/mach/iommu_domains.h
+++ b/arch/arm/mach-msm/include/mach/iommu_domains.h
@@ -15,6 +15,8 @@
#include <linux/memory_alloc.h>
+#define MSM_IOMMU_DOMAIN_SECURE 0x1
+
enum {
VIDEO_DOMAIN,
CAMERA_DOMAIN,
@@ -69,6 +71,7 @@
int npartitions;
const char *client_name;
unsigned int domain_flags;
+ unsigned int is_secure;
};
#if defined(CONFIG_MSM_IOMMU)
diff --git a/arch/arm/mach-msm/include/mach/irqs-8625.h b/arch/arm/mach-msm/include/mach/irqs-8625.h
index 413a778..2a61118 100644
--- a/arch/arm/mach-msm/include/mach/irqs-8625.h
+++ b/arch/arm/mach-msm/include/mach/irqs-8625.h
@@ -16,6 +16,10 @@
#define GIC_PPI_START 16
#define GIC_SPI_START 32
+#ifdef CONFIG_MSM_FIQ
+#define FIQ_START 0
+#endif
+
/* As per QGIC2 PPI 16 aka 0 is reserved */
#define MSM8625_INT_A5_PMU_IRQ (GIC_PPI_START + 1)
#define MSM8625_INT_DEBUG_TIMER_EXP (GIC_PPI_START + 2)
@@ -87,6 +91,8 @@
#define MSM8625_INT_CPR_IRQ0 (GIC_SPI_START + 32 + 25)
#define MSM8625_INT_CPR_IRQ1 (GIC_SPI_START + 32 + 26)
#define MSM8625_INT_CPR_IRQ2 (GIC_SPI_START + 32 + 27)
+#define MSM8625_INT_ACSR_MP_CORE_IPC2 (GIC_SPI_START + 32 + 28)
+#define MSM8625_INT_ACSR_MP_CORE_IPC3 (GIC_SPI_START + 32 + 29)
#define MSM8625_INT_ADSP_A11_SMSM MSM8625_INT_ADSP_A11
#endif
diff --git a/arch/arm/mach-msm/include/mach/irqs-8910.h b/arch/arm/mach-msm/include/mach/irqs-8910.h
new file mode 100644
index 0000000..22fdc16
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/irqs-8910.h
@@ -0,0 +1,40 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ASM_ARCH_MSM_IRQS_8910_H
+#define __ASM_ARCH_MSM_IRQS_8910_H
+
+/* MSM ACPU Interrupt Numbers */
+
+/*
+ * 0-15: STI/SGI (software triggered/generated interrupts)
+ * 16-31: PPI (private peripheral interrupts)
+ * 32+: SPI (shared peripheral interrupts)
+ */
+
+#define GIC_PPI_START 16
+#define GIC_SPI_START 32
+
+#define INT_ARMQC_PERFMON (GIC_PPI_START + 10)
+
+#define APCC_QGICL2PERFMONIRPTREQ (GIC_SPI_START + 1)
+#define SC_SICL2PERFMONIRPTREQ APCC_QGICL2PERFMONIRPTREQ
+#define TLMM_MSM_SUMMARY_IRQ (GIC_SPI_START + 208)
+
+#define NR_MSM_IRQS 256
+#define NR_GPIO_IRQS 146
+#define NR_QPNP_IRQS 32768 /* SPARSE_IRQ is required to support this */
+#define NR_BOARD_IRQS NR_QPNP_IRQS
+#define NR_TLMM_MSM_DIR_CONN_IRQ 8
+#define NR_MSM_GPIOS NR_GPIO_IRQS
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/irqs.h b/arch/arm/mach-msm/include/mach/irqs.h
index f562c40..7aff770 100644
--- a/arch/arm/mach-msm/include/mach/irqs.h
+++ b/arch/arm/mach-msm/include/mach/irqs.h
@@ -60,6 +60,8 @@
#if defined(CONFIG_ARCH_MSM8974)
#include "irqs-8974.h"
+#elif defined(CONFIG_ARCH_MSM8910)
+#include "irqs-8910.h"
#elif defined(CONFIG_ARCH_MPQ8092)
#include "irqs-8092.h"
#elif defined(CONFIG_ARCH_MSM9615)
diff --git a/arch/arm/mach-msm/include/mach/msm-krait-l2-accessors.h b/arch/arm/mach-msm/include/mach/msm-krait-l2-accessors.h
index f835e82..a35ff4d 100644
--- a/arch/arm/mach-msm/include/mach/msm-krait-l2-accessors.h
+++ b/arch/arm/mach-msm/include/mach/msm-krait-l2-accessors.h
@@ -2,7 +2,7 @@
#define __ASM_ARCH_MSM_MSM_KRAIT_L2_ACCESSORS_H
/*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011,2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -13,8 +13,21 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
+
+#ifdef CONFIG_ARCH_MSM_KRAIT
extern void set_l2_indirect_reg(u32 reg_addr, u32 val);
extern u32 get_l2_indirect_reg(u32 reg_addr);
extern u32 set_get_l2_indirect_reg(u32 reg_addr, u32 val);
+#else
+static inline void set_l2_indirect_reg(u32 reg_addr, u32 val) {}
+static inline u32 get_l2_indirect_reg(u32 reg_addr)
+{
+ return 0;
+}
+static inline u32 set_get_l2_indirect_reg(u32 reg_addr, u32 val)
+{
+ return 0;
+}
+#endif
#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_dcvs.h b/arch/arm/mach-msm/include/mach/msm_dcvs.h
index fa7e6f0..e81cee4 100644
--- a/arch/arm/mach-msm/include/mach/msm_dcvs.h
+++ b/arch/arm/mach-msm/include/mach/msm_dcvs.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -18,6 +18,12 @@
#define CORE_NAME_MAX (32)
#define CORES_MAX (10)
+#define CPU_OFFSET 1 /* used to notify TZ the core number */
+#define GPU_OFFSET (CORES_MAX * 2/3) /* there will be more cpus than gpus,
+ * let the GPU be assigned fewer core
+ * elements and start later
+ */
+
enum msm_core_idle_state {
MSM_DCVS_IDLE_ENTER,
MSM_DCVS_IDLE_EXIT,
@@ -30,43 +36,14 @@
MSM_DCVS_DISABLE_HIGH_LATENCY_MODES,
};
-/**
- * struct msm_dcvs_idle
- *
- * API for idle code to register and send idle enter/exit
- * notifications to msm_dcvs driver.
- */
-struct msm_dcvs_idle {
- const char *core_name;
- /* Enable/Disable idle state/notifications */
- int (*enable)(struct msm_dcvs_idle *self,
- enum msm_core_control_event event);
+struct msm_gov_platform_data {
+ struct msm_dcvs_core_info *info;
+ int latency;
};
/**
- * msm_dcvs_idle_source_register
- * @drv: Pointer to the source driver
- * @return: Handle to be used for sending idle state notifications.
- *
- * Register the idle driver with the msm_dcvs driver to send idle
- * state notifications for the core.
- */
-extern int msm_dcvs_idle_source_register(struct msm_dcvs_idle *drv);
-
-/**
- * msm_dcvs_idle_source_unregister
- * @drv: Pointer to the source driver
- * @return:
- * 0 on success
- * -EINVAL
- *
- * Description: Unregister the idle driver with the msm_dcvs driver
- */
-extern int msm_dcvs_idle_source_unregister(struct msm_dcvs_idle *drv);
-
-/**
* msm_dcvs_idle
- * @handle: Handle provided back at registration
+ * @dcvs_core_id: The id returned by msm_dcvs_register_core
* @state: The enter/exit idle state the core is in
* @iowaited: iowait in us
* on iMSM_DCVS_IDLE_EXIT.
@@ -78,7 +55,7 @@
*
* Send idle state notifications to the msm_dcvs driver
*/
-int msm_dcvs_idle(int handle, enum msm_core_idle_state state,
+int msm_dcvs_idle(int dcvs_core_id, enum msm_core_idle_state state,
uint32_t iowaited);
/**
@@ -88,16 +65,22 @@
* before the sink driver can be registered.
*/
struct msm_dcvs_core_info {
- struct msm_dcvs_freq_entry *freq_tbl;
- struct msm_dcvs_core_param core_param;
- struct msm_dcvs_algo_param algo_param;
+ int num_cores;
+ int *sensors;
+ int thermal_poll_ms;
+ struct msm_dcvs_freq_entry *freq_tbl;
+ struct msm_dcvs_core_param core_param;
+ struct msm_dcvs_algo_param algo_param;
+ struct msm_dcvs_energy_curve_coeffs energy_coeffs;
+ struct msm_dcvs_power_params power_param;
};
/**
* msm_dcvs_register_core
- * @core_name: Unique name identifier for the core.
- * @group_id: Cores that are to be grouped for synchronized frequency scaling
+ * @type: whether this is a CPU or a GPU
+ * @type_core_num: The number of the core for a type
* @info: The core specific algorithm parameters.
+ * @sensor: The thermal sensor number of the core in question
* @return :
* 0 on success,
* -ENOSYS,
@@ -106,37 +89,29 @@
* Register the core with msm_dcvs driver. Done once at init before calling
* msm_dcvs_freq_sink_register
* Cores that need to run synchronously must share the same group id.
- * If a core doesnt care to be in any group, the group_id should be 0.
*/
-extern int msm_dcvs_register_core(const char *core_name, uint32_t group_id,
- struct msm_dcvs_core_info *info);
+extern int msm_dcvs_register_core(
+ enum msm_dcvs_core_type type,
+ int type_core_num,
+ struct msm_dcvs_core_info *info,
+ int (*set_frequency)(int type_core_num, unsigned int freq),
+ unsigned int (*get_frequency)(int type_core_num),
+ int (*idle_enable)(int type_core_num,
+ enum msm_core_control_event event),
+ int sensor);
/**
- * struct msm_dcvs_freq
- *
- * API for clock driver code to register and receive frequency change
- * request for the core from the msm_dcvs driver.
- */
-struct msm_dcvs_freq {
- const char *core_name;
- /* Callback from msm_dcvs to set the core frequency */
- int (*set_frequency)(struct msm_dcvs_freq *self,
- unsigned int freq);
- unsigned int (*get_frequency)(struct msm_dcvs_freq *self);
-};
-
-/**
- * msm_dcvs_freq_sink_register
+ * msm_dcvs_freq_sink_start
* @drv: The sink driver
* @return: Handle unique to the core.
*
* Register the clock driver code with the msm_dvs driver to get notified about
* frequency change requests.
*/
-extern int msm_dcvs_freq_sink_register(struct msm_dcvs_freq *drv);
+extern int msm_dcvs_freq_sink_start(int dcvs_core_id);
/**
- * msm_dcvs_freq_sink_unregister
+ * msm_dcvs_freq_sink_stop
* @drv: The sink driver
* @return:
* 0 on success,
@@ -145,6 +120,13 @@
* Unregister the sink driver for the core. This will cause the source driver
* for the core to stop sending idle pulses.
*/
-extern int msm_dcvs_freq_sink_unregister(struct msm_dcvs_freq *drv);
+extern int msm_dcvs_freq_sink_stop(int dcvs_core_id);
+/**
+ * msm_dcvs_update_limits
+ * @drv: The sink driver
+ *
+ * Update the frequency known to dcvs when the limits are changed.
+ */
+extern void msm_dcvs_update_limits(int dcvs_core_id);
#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_dcvs_scm.h b/arch/arm/mach-msm/include/mach/msm_dcvs_scm.h
index 3cc2595..597fdc0 100644
--- a/arch/arm/mach-msm/include/mach/msm_dcvs_scm.h
+++ b/arch/arm/mach-msm/include/mach/msm_dcvs_scm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -13,38 +13,87 @@
#ifndef _ARCH_ARM_MACH_MSM_MSM_DCVS_SCM_H
#define _ARCH_ARM_MACH_MSM_MSM_DCVS_SCM_H
+enum msm_dcvs_core_type {
+ MSM_DCVS_CORE_TYPE_CPU = 0,
+ MSM_DCVS_CORE_TYPE_GPU = 1,
+};
+
+enum msm_dcvs_algo_param_type {
+ MSM_DCVS_ALGO_DCVS_PARAM = 0,
+ MSM_DCVS_ALGO_MPD_PARAM = 1,
+};
+
enum msm_dcvs_scm_event {
- MSM_DCVS_SCM_IDLE_ENTER,
- MSM_DCVS_SCM_IDLE_EXIT,
- MSM_DCVS_SCM_QOS_TIMER_EXPIRED,
- MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
- MSM_DCVS_SCM_ENABLE_CORE,
- MSM_DCVS_SCM_RESET_CORE,
+ MSM_DCVS_SCM_IDLE_ENTER = 0, /* Core enters idle */
+ MSM_DCVS_SCM_IDLE_EXIT = 1, /* Core exits idle */
+ MSM_DCVS_SCM_QOS_TIMER_EXPIRED = 2, /* Core slack timer expired */
+ MSM_DCVS_SCM_CLOCK_FREQ_UPDATE = 3, /* Core freq change complete */
+ MSM_DCVS_SCM_CORE_ONLINE = 4, /* Core is online */
+ MSM_DCVS_SCM_CORE_OFFLINE = 5, /* Core is offline */
+ MSM_DCVS_SCM_CORE_UNAVAILABLE = 6, /* Core is offline + unavailable */
+ MSM_DCVS_SCM_DCVS_ENABLE = 7, /* DCVS is enabled/disabled for core */
+ MSM_DCVS_SCM_MPD_ENABLE = 8, /* Enable/disable MP Decision */
+ MSM_DCVS_SCM_RUNQ_UPDATE = 9, /* Update running threads */
+ MSM_DCVS_SCM_MPD_QOS_TIMER_EXPIRED = 10, /* MPDecision slack timer */
};
struct msm_dcvs_algo_param {
- uint32_t slack_time_us;
- uint32_t scale_slack_time;
- uint32_t scale_slack_time_pct;
uint32_t disable_pc_threshold;
- uint32_t em_window_size;
+ uint32_t em_win_size_min_us;
+ uint32_t em_win_size_max_us;
uint32_t em_max_util_pct;
- uint32_t ss_window_size;
- uint32_t ss_util_pct;
+ uint32_t group_id;
+ uint32_t max_freq_chg_time_us;
+ uint32_t slack_mode_dynamic;
+ uint32_t slack_time_min_us;
+ uint32_t slack_time_max_us;
+ uint32_t slack_weight_thresh_pct;
uint32_t ss_iobusy_conv;
+ uint32_t ss_win_size_min_us;
+ uint32_t ss_win_size_max_us;
+ uint32_t ss_util_pct;
};
struct msm_dcvs_freq_entry {
- uint32_t freq; /* Core freq in MHz */
- uint32_t idle_energy;
- uint32_t active_energy;
+ uint32_t freq;
+ uint32_t voltage;
+ uint32_t is_trans_level;
+ uint32_t active_energy_offset;
+ uint32_t leakage_energy_offset;
};
-struct msm_dcvs_core_param {
- uint32_t max_time_us;
+struct msm_dcvs_energy_curve_coeffs {
+ int32_t active_coeff_a;
+ int32_t active_coeff_b;
+ int32_t active_coeff_c;
+
+ int32_t leakage_coeff_a;
+ int32_t leakage_coeff_b;
+ int32_t leakage_coeff_c;
+ int32_t leakage_coeff_d;
+};
+
+struct msm_dcvs_power_params {
+ uint32_t current_temp;
uint32_t num_freq; /* number of msm_dcvs_freq_entry passed */
};
+struct msm_dcvs_core_param {
+ uint32_t core_type;
+ uint32_t core_bitmask_id;
+};
+
+struct msm_mpd_algo_param {
+ uint32_t em_win_size_min_us;
+ uint32_t em_win_size_max_us;
+ uint32_t em_max_util_pct;
+ uint32_t mp_em_rounding_point_min;
+ uint32_t mp_em_rounding_point_max;
+ uint32_t online_util_pct_min;
+ uint32_t online_util_pct_max;
+ uint32_t slack_time_min_us;
+ uint32_t slack_time_max_us;
+};
#ifdef CONFIG_MSM_DCVS
/**
@@ -61,20 +110,9 @@
extern int msm_dcvs_scm_init(size_t size);
/**
- * Create an empty core group
- *
- * @return:
- * 0 on success.
- * -ENOMEM: Insufficient memory.
- * -EINVAL: Invalid args.
- */
-extern int msm_dcvs_scm_create_group(uint32_t id);
-
-/**
- * Registers cores as part of a group
+ * Registers cores with the DCVS algo.
*
* @core_id: The core identifier that will be used for communication with DCVS
- * @group_id: The group to which this core will be added to.
* @param: The core parameters
* @freq: Array of frequency and energy values
*
@@ -83,9 +121,8 @@
* -ENOMEM: Insufficient memory.
* -EINVAL: Invalid args.
*/
-extern int msm_dcvs_scm_register_core(uint32_t core_id, uint32_t group_id,
- struct msm_dcvs_core_param *param,
- struct msm_dcvs_freq_entry *freq);
+extern int msm_dcvs_scm_register_core(uint32_t core_id,
+ struct msm_dcvs_core_param *param);
/**
* Set DCVS algorithm parameters
@@ -101,6 +138,33 @@
struct msm_dcvs_algo_param *param);
/**
+ * Set MPDecision algorithm parameters
+ *
+ * @param: The param data structure
+ * 0 on success.
+ * -EINVAL: Invalid args.
+ */
+extern int msm_mpd_scm_set_algo_params(struct msm_mpd_algo_param *param);
+
+/**
+ * Set frequency and power characteristics for the core.
+ *
+ * @param core_id: The core identifier that will be used to interace with the
+ * DCVS algo.
+ * @param pwr_param: power params
+ * @param freq_entry: frequency characteristics desired
+ * @param coeffs: Coefficients that will describe the power curve
+ *
+ * @return int
+ * 0 on success.
+ * -EINVAL: Invalid args.
+ */
+extern int msm_dcvs_scm_set_power_params(uint32_t core_id,
+ struct msm_dcvs_power_params *pwr_param,
+ struct msm_dcvs_freq_entry *freq_entry,
+ struct msm_dcvs_energy_curve_coeffs *coeffs);
+
+/**
* Do an SCM call.
*
* @core_id: The core identifier.
@@ -126,19 +190,44 @@
* @param1: time taken in usec to switch to the frequency
* @ret0: New QoS timer value for the core in usec
* @ret1: unused
- * MSM_DCVS_SCM_ENABLE_CORE
- * @param0: enable(1) or disable(0) core
- * @param1: active clock frequency of the core in KHz
- * @ret0: New clock frequency for the core in KHz
- * @ret1: unused
- * MSM_DCVS_SCM_RESET_CORE
+ * MSM_DCVS_SCM_CORE_ONLINE
* @param0: active clock frequency of the core in KHz
+ * @param1: time taken to online the core
+ * @ret0: unused
+ * @ret1: unused
+ * MSM_DCVS_SCM_CORE_OFFLINE
+ * @param0: time taken to offline the core
+ * @param1: unused
+ * @ret0: unused
+ * @ret1: unused
+ * MSM_DCVS_SCM_CORE_UNAVAILABLE
+ * @param0: TODO:bitmask
+ * @param1: unused
+ * @ret0: Bitmask of cores to bring online/offline.
+ * @ret1: Mp Decision slack time. Common to all cores.
+ * MSM_DCVS_SCM_DCVS_ENABLE
+ * @param0: 1 to enable; 0 to disable DCVS
* @param1: unused
* @ret0: New clock frequency for the core in KHz
* @ret1: unused
- * @return:
- * 0 on success,
- * SCM return values
+ * MSM_DCVS_SCM_MPD_ENABLE
+ * @param0: 1 to enable; 0 to disable MP Decision
+ * @param1: unused
+ * @ret0: unused
+ * @ret1: unused
+ * MSM_DCVS_SCM_RUNQ_UPDATE
+ * @param0: run q value
+ * @param1: unused
+ * @ret0: Bitmask of cores online
+ * @ret1: New QoS timer for MP Decision (usec)
+ * MSM_DCVS_SCM_MPD_QOS_TIMER_EXPIRED
+ * @param0: unused
+ * @param1: unused
+ * @ret0: Bitmask of cores online
+ * @ret1: New QoS timer for MP Decision (usec)
+ * @return:
+ * 0 on success,
+ * SCM return values
*/
extern int msm_dcvs_scm_event(uint32_t core_id,
enum msm_dcvs_scm_event event_id,
@@ -148,16 +237,21 @@
#else
static inline int msm_dcvs_scm_init(uint32_t phy, size_t bytes)
{ return -ENOSYS; }
-static inline int msm_dcvs_scm_create_group(uint32_t id)
-{ return -ENOSYS; }
static inline int msm_dcvs_scm_register_core(uint32_t core_id,
- uint32_t group_id,
struct msm_dcvs_core_param *param,
struct msm_dcvs_freq_entry *freq)
{ return -ENOSYS; }
static inline int msm_dcvs_scm_set_algo_params(uint32_t core_id,
struct msm_dcvs_algo_param *param)
{ return -ENOSYS; }
+static inline int msm_mpd_scm_set_algo_params(
+ struct msm_mpd_algo_param *param)
+{ return -ENOSYS; }
+static inline int msm_dcvs_set_power_params(uint32_t core_id,
+ struct msm_dcvs_power_params *pwr_param,
+ struct msm_dcvs_freq_entry *freq_entry,
+ struct msm_dcvs_energy_curve_coeffs *coeffs)
+{ return -ENOSYS; }
static inline int msm_dcvs_scm_event(uint32_t core_id,
enum msm_dcvs_scm_event event_id,
uint32_t param0, uint32_t param1,
diff --git a/arch/arm/mach-msm/include/mach/msm_dsps.h b/arch/arm/mach-msm/include/mach/msm_dsps.h
index a876798..ac81616 100644
--- a/arch/arm/mach-msm/include/mach/msm_dsps.h
+++ b/arch/arm/mach-msm/include/mach/msm_dsps.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. 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,16 +75,6 @@
* @regs_num - number of regulators.
* @dsps_pwr_ctl_en - to enable DSPS to do power control if set 1
* otherwise the apps will do power control
- * @tcm_code_start - start of the TCM code region as physical address
- * @tcm_code_size - size of the TCM code region in bytes
- * @tcm_buf_start - start of the TCM buf region as physical address
- * @tcm_buf_size - size of the TCM buf region in bytes
- * @pipe_start - start of the PIPE region as physical address
- * @pipe_size - size of the PIPE region in bytes
- * @ddr_start - start of the DDR region as physical address
- * @ddr_size - size of the DDR region in bytes
- * @smem_start - start of the smem region as physical address
- * @smem_size - size of the smem region in bytes
* @ppss_pause_reg - Offset to the PPSS_PAUSE register
* @ppss_wdog_unmasked_int_en_reg - Offset to PPSS_WDOG_UNMASKED_INT_EN register
* @signature - signature for validity check.
@@ -99,16 +89,6 @@
int regs_num;
int dsps_pwr_ctl_en;
void (*init)(struct msm_dsps_platform_data *data);
- int tcm_code_start;
- int tcm_code_size;
- int tcm_buf_start;
- int tcm_buf_size;
- int pipe_start;
- int pipe_size;
- int ddr_start;
- int ddr_size;
- int smem_start;
- int smem_size;
int ppss_pause_reg;
int ppss_wdog_unmasked_int_en_reg;
u32 signature;
diff --git a/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h b/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h
index 82542b2..831b40e 100644
--- a/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h
+++ b/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h
@@ -100,6 +100,8 @@
#define CONFIG_MAX_PKT(n) ((n) << 16)
#define CONFIG_ZLT (1 << 29) /* stop on zero-len xfer */
#define CONFIG_IOS (1 << 15) /* IRQ on setup */
+#define CONFIG_MULT (3 << 30)
+#define CONFIG_MULT_SHIFT 11
struct ept_queue_item {
unsigned next;
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-8910.h b/arch/arm/mach-msm/include/mach/msm_iomap-8910.h
new file mode 100644
index 0000000..e4cd312
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-8910.h
@@ -0,0 +1,41 @@
+/* Copyright (c) 2012, 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
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ASM_ARCH_MSM_IOMAP_8910_H
+#define __ASM_ARCH_MSM_IOMAP_8910_H
+
+/* Physical base address and size of peripherals.
+ * Ordered by the virtual base addresses they will be mapped at.
+ *
+ * If you add or remove entries here, you'll want to edit the
+ * io desc array in arch/arm/mach-msm/io.c to reflect your
+ * changes.
+ *
+ */
+
+#define MSM8910_MSM_SHARED_RAM_PHYS 0x0FA00000
+
+#define MSM8910_APCS_GCC_PHYS 0xF9011000
+#define MSM8910_APCS_GCC_SIZE SZ_4K
+
+#define MSM8910_TLMM_PHYS 0xFD510000
+#define MSM8910_TLMM_SIZE SZ_16K
+
+#define MSM8910_IMEM_PHYS 0xFC42B000
+#define MSM8910_IMEM_SIZE SZ_4K
+
+#ifdef CONFIG_DEBUG_MSM8910_UART
+#define MSM_DEBUG_UART_BASE IOMEM(0xFA71E000)
+#define MSM_DEBUG_UART_PHYS 0xF991E000
+#endif
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-9625.h b/arch/arm/mach-msm/include/mach/msm_iomap-9625.h
index eecdd67..89252a5 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-9625.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-9625.h
@@ -25,6 +25,12 @@
#define MSM9625_SHARED_RAM_PHYS 0x00000000
+#define MSM9625_QGIC_DIST_PHYS 0xF9000000
+#define MSM9625_QGIC_DIST_SIZE SZ_4K
+
+#define MSM9625_QGIC_CPU_PHYS 0xF9002000
+#define MSM9625_QGIC_CPU_SIZE SZ_4K
+
#define MSM9625_APCS_GCC_PHYS 0xF9011000
#define MSM9625_APCS_GCC_SIZE SZ_4K
@@ -34,9 +40,30 @@
#define MSM9625_TLMM_PHYS 0xFD510000
#define MSM9625_TLMM_SIZE SZ_16K
+/*
+ * TODO: Revert IMEM_PHYS back to actual
+ * address 0xfe807800
+ * after IMEM issues resolved.
+ *
+ */
+#define MSM9625_IMEM_PHYS 0xFC42B000
+#define MSM9625_IMEM_SIZE SZ_2K
+
+#define MSM9625_MPM2_PSHOLD_PHYS 0xFC4AB000
+#define MSM9625_MPM2_PSHOLD_SIZE SZ_4K
+
#ifdef CONFIG_DEBUG_MSM9625_UART
#define MSM_DEBUG_UART_BASE IOMEM(0xFA71E000)
#define MSM_DEBUG_UART_PHYS 0xF991E000
#endif
+/*
+ * IMEM is retained for secure watchdog reset
+ * Debug Image looks at actual IMEM to
+ * do memory dumping.
+ */
+
+#define MSM9625_DBG_IMEM_PHYS 0xFE807800
+#define MSM9625_DBG_IMEM_SIZE SZ_4K
+
#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap.h b/arch/arm/mach-msm/include/mach/msm_iomap.h
index 6f925d4..f372b1e 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap.h
@@ -54,7 +54,7 @@
defined(CONFIG_ARCH_MSM7X25) || defined(CONFIG_ARCH_MSM7X01A) || \
defined(CONFIG_ARCH_MSM8625) || defined(CONFIG_ARCH_MSM7X30) || \
defined(CONFIG_ARCH_MSM9625) || defined(CONFIG_ARCH_MPQ8092) || \
- defined(CONFIG_ARCH_MSM8226)
+ defined(CONFIG_ARCH_MSM8226) || defined(CONFIG_ARCH_MSM8910)
/* Unified iomap */
@@ -101,7 +101,7 @@
#define MSM_DBG_IMEM_BASE IOMEM(0xFB600000) /* 4K */
#define MSM_STRONGLY_ORDERED_PAGE 0xFA0F0000
-#define MSM8625_SECONDARY_PHYS 0x0FE00000
+#define MSM8625_CPU_PHYS 0x0FE00000
#if defined(CONFIG_ARCH_MSM9615) || defined(CONFIG_ARCH_MSM7X27) \
@@ -122,6 +122,7 @@
#include "msm_iomap-9625.h"
#include "msm_iomap-8092.h"
#include "msm_iomap-8226.h"
+#include "msm_iomap-8910.h"
#else
/* Legacy single-target iomap */
diff --git a/arch/arm/mach-msm/include/mach/msm_smsm.h b/arch/arm/mach-msm/include/mach/msm_smsm.h
index be8f6c1..44b52b6 100644
--- a/arch/arm/mach-msm/include/mach/msm_smsm.h
+++ b/arch/arm/mach-msm/include/mach/msm_smsm.h
@@ -57,8 +57,7 @@
#define SMSM_PWRC 0x00000200
#define SMSM_TIMEWAIT 0x00000400
#define SMSM_TIMEINIT 0x00000800
-#define SMSM_PWRC_EARLY_EXIT 0x00001000
-#define SMSM_LTE_COEX_AWAKE 0x00001000
+#define SMSM_PROC_AWAKE 0x00001000
#define SMSM_WFPI 0x00002000
#define SMSM_SLEEP 0x00004000
#define SMSM_SLEEPEXIT 0x00008000
diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/codec_utils.h b/arch/arm/mach-msm/include/mach/qdsp5v2/codec_utils.h
index 92dfe12..2cfdabf 100644
--- a/arch/arm/mach-msm/include/mach/qdsp5v2/codec_utils.h
+++ b/arch/arm/mach-msm/include/mach/qdsp5v2/codec_utils.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010, 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010, 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -123,7 +123,8 @@
uint64_t bytecount_given;
uint64_t bytecount_query;
- struct list_head pmem_region_queue; /* protected by lock */
+ struct list_head ion_region_queue; /* protected by lock */
+ struct ion_client *client;
int eq_enable;
int eq_needs_commit;
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h
index 487e814..da639ce 100644
--- a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h
@@ -78,7 +78,7 @@
} __packed;
/* Max number of static located transparent data (bytes) */
-#define USM_MAX_CFG_DATA_SIZE 20
+#define USM_MAX_CFG_DATA_SIZE 100
struct usm_encode_cfg_blk {
u32 frames_per_buf;
u32 format_id;
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h b/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
index 3d33350..d34536d 100644
--- a/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
@@ -25,6 +25,13 @@
MAX_AUDPROC_TYPES
};
+enum {
+ VOCPROC_CAL,
+ VOCSTRM_CAL,
+ VOCVOL_CAL,
+ MAX_VOCPROC_TYPES
+};
+
struct acdb_cal_block {
uint32_t cal_size;
uint32_t cal_kvaddr;
@@ -47,16 +54,20 @@
uint32_t get_adm_rx_topology(void);
uint32_t get_adm_tx_topology(void);
uint32_t get_asm_topology(void);
+void get_voice_cal_allocation(struct acdb_cal_block *cal_block);
void get_all_voice_cal(struct acdb_cal_block *cal_block);
void get_all_cvp_cal(struct acdb_cal_block *cal_block);
void get_all_vocproc_cal(struct acdb_cal_block *cal_block);
void get_all_vocstrm_cal(struct acdb_cal_block *cal_block);
void get_all_vocvol_cal(struct acdb_cal_block *cal_block);
+void get_voice_col_data(uint32_t vocproc_type,
+ struct acdb_cal_block *cal_block);
void get_anc_cal(struct acdb_cal_block *cal_block);
void get_afe_cal(int32_t path, struct acdb_cal_block *cal_block);
void get_audproc_cal(int32_t path, struct acdb_cal_block *cal_block);
void get_audstrm_cal(int32_t path, struct acdb_cal_block *cal_block);
void get_audvol_cal(int32_t path, struct acdb_cal_block *cal_block);
+void get_vocproc_dev_cfg_cal(struct acdb_cal_block *cal_block);
void get_vocproc_cal(struct acdb_cal_data *cal_data);
void get_vocstrm_cal(struct acdb_cal_data *cal_data);
void get_vocvol_cal(struct acdb_cal_data *cal_data);
diff --git a/arch/arm/mach-msm/include/mach/rpm-regulator.h b/arch/arm/mach-msm/include/mach/rpm-regulator.h
index f6e082d..075d20f 100644
--- a/arch/arm/mach-msm/include/mach/rpm-regulator.h
+++ b/arch/arm/mach-msm/include/mach/rpm-regulator.h
@@ -32,7 +32,8 @@
RPM_VREG_VERSION_9615,
RPM_VREG_VERSION_8930,
RPM_VREG_VERSION_8930_PM8917,
- RPM_VREG_VERSION_MAX = RPM_VREG_VERSION_8930_PM8917,
+ RPM_VREG_VERSION_8960_PM8917,
+ RPM_VREG_VERSION_MAX = RPM_VREG_VERSION_8960_PM8917,
};
#define RPM_VREG_PIN_CTRL_NONE 0x00
diff --git a/arch/arm/mach-msm/include/mach/socinfo.h b/arch/arm/mach-msm/include/mach/socinfo.h
index 225440c..5c88101 100644
--- a/arch/arm/mach-msm/include/mach/socinfo.h
+++ b/arch/arm/mach-msm/include/mach/socinfo.h
@@ -54,6 +54,12 @@
of_machine_is_compatible("qcom,msm8226")
#define machine_is_msm8226_sim() \
of_machine_is_compatible("qcom,msm8226-sim")
+#define early_machine_is_msm8910() \
+ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8910")
+#define machine_is_msm8910() \
+ of_machine_is_compatible("qcom,msm8910")
+#define machine_is_msm8910_sim() \
+ of_machine_is_compatible("qcom,msm8910-sim")
#else
#define early_machine_is_msm8974() 0
#define machine_is_msm8974() 0
@@ -66,6 +72,9 @@
#define early_machine_is_msm8226() 0
#define machine_is_msm8226() 0
#define machine_is_msm8226_sim() 0
+#define early_machine_is_msm8910() 0
+#define machine_is_msm8910() 0
+#define machine_is_msm8910_sim() 0
#endif
@@ -99,7 +108,8 @@
MSM_CPU_8625,
MSM_CPU_9625,
MSM_CPU_8092,
- MSM_CPU_8226
+ MSM_CPU_8226,
+ MSM_CPU_8910,
};
enum pmic_model {
@@ -415,4 +425,31 @@
#endif
}
+static inline int cpu_is_msm8910(void)
+{
+#ifdef CONFIG_ARCH_MSM8910
+ enum msm_cpu cpu = socinfo_get_msm_cpu();
+
+ BUG_ON(cpu == MSM_CPU_UNKNOWN);
+ return cpu == MSM_CPU_8910;
+#else
+ return 0;
+#endif
+}
+
+static inline int soc_class_is_msm8960(void)
+{
+ return cpu_is_msm8960() || cpu_is_msm8960ab();
+}
+
+static inline int soc_class_is_apq8064(void)
+{
+ return cpu_is_apq8064() || cpu_is_apq8064ab();
+}
+
+static inline int soc_class_is_msm8930(void)
+{
+ return cpu_is_msm8930() || cpu_is_msm8930aa() || cpu_is_msm8627();
+}
+
#endif
diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c
index 5ee7068..c2c9233 100644
--- a/arch/arm/mach-msm/io.c
+++ b/arch/arm/mach-msm/io.c
@@ -25,6 +25,7 @@
#include <mach/hardware.h>
#include <asm/page.h>
#include <mach/msm_iomap.h>
+#include <mach/memory.h>
#include <asm/mach/map.h>
#include <linux/dma-mapping.h>
@@ -99,6 +100,7 @@
asm("mcr p15, 0, %0, c15, c2, 4" : : "r" (0));
#endif
msm_map_io(msm_io_desc, ARRAY_SIZE(msm_io_desc));
+ map_page_strongly_ordered();
}
#endif
@@ -452,6 +454,7 @@
void __init msm_map_msm8625_io(void)
{
msm_map_io(msm8625_io_desc, ARRAY_SIZE(msm8625_io_desc));
+ map_page_strongly_ordered();
}
#else
void __init msm_map_msm8625_io(void) { return; }
@@ -460,8 +463,12 @@
#ifdef CONFIG_ARCH_MSM9625
static struct map_desc msm9625_io_desc[] __initdata = {
MSM_CHIP_DEVICE(APCS_GCC, MSM9625),
+ MSM_CHIP_DEVICE(QGIC_DIST, MSM9625),
+ MSM_CHIP_DEVICE(QGIC_CPU, MSM9625),
MSM_CHIP_DEVICE(TLMM, MSM9625),
+ MSM_CHIP_DEVICE(MPM2_PSHOLD, MSM9625),
MSM_CHIP_DEVICE(TMR, MSM9625),
+ MSM_CHIP_DEVICE(IMEM, MSM9625),
{
.virtual = (unsigned long) MSM_SHARED_RAM_BASE,
.length = MSM_SHARED_RAM_SIZE,
@@ -470,6 +477,7 @@
#ifdef CONFIG_DEBUG_MSM9625_UART
MSM_DEVICE(DEBUG_UART),
#endif
+ MSM_CHIP_DEVICE(DBG_IMEM, MSM9625),
};
void __init msm_map_msm9625_io(void)
@@ -527,3 +535,22 @@
msm_map_io(msm_8226_io_desc, ARRAY_SIZE(msm_8226_io_desc));
}
#endif /* CONFIG_ARCH_MSM8226 */
+
+#ifdef CONFIG_ARCH_MSM8910
+static struct map_desc msm8910_io_desc[] __initdata = {
+ MSM_CHIP_DEVICE(APCS_GCC, MSM8910),
+ MSM_CHIP_DEVICE(TLMM, MSM8910),
+ MSM_CHIP_DEVICE(IMEM, MSM8910),
+ {
+ .virtual = (unsigned long) MSM_SHARED_RAM_BASE,
+ .length = MSM_SHARED_RAM_SIZE,
+ .type = MT_DEVICE,
+ },
+};
+
+void __init msm_map_msm8910_io(void)
+{
+ msm_shared_ram_phys = MSM8910_MSM_SHARED_RAM_PHYS;
+ msm_map_io(msm8910_io_desc, ARRAY_SIZE(msm8910_io_desc));
+}
+#endif /* CONFIG_ARCH_MSM8910 */
diff --git a/arch/arm/mach-msm/iommu_domains.c b/arch/arm/mach-msm/iommu_domains.c
index 3acb6d8..75e56fe 100644
--- a/arch/arm/mach-msm/iommu_domains.c
+++ b/arch/arm/mach-msm/iommu_domains.c
@@ -267,6 +267,7 @@
else
return NULL;
}
+EXPORT_SYMBOL(msm_get_iommu_domain);
int msm_allocate_iova_address(unsigned int iommu_domain,
unsigned int partition_no,
@@ -344,6 +345,7 @@
int i;
struct msm_iova_data *data;
struct mem_pool *pools;
+ struct bus_type *bus;
if (!layout)
return -EINVAL;
@@ -389,11 +391,14 @@
}
}
+ bus = layout->is_secure == MSM_IOMMU_DOMAIN_SECURE ?
+ &msm_iommu_sec_bus_type :
+ &platform_bus_type;
+
data->pools = pools;
data->npools = layout->npartitions;
data->domain_num = atomic_inc_return(&domain_nums);
- data->domain = iommu_domain_alloc(&platform_bus_type,
- layout->domain_flags);
+ data->domain = iommu_domain_alloc(bus, layout->domain_flags);
add_domain(data);
diff --git a/arch/arm/mach-msm/lpass-8660.c b/arch/arm/mach-msm/lpass-8660.c
deleted file mode 100644
index be18b68..0000000
--- a/arch/arm/mach-msm/lpass-8660.c
+++ /dev/null
@@ -1,172 +0,0 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/reboot.h>
-#include <linux/workqueue.h>
-#include <linux/io.h>
-#include <linux/jiffies.h>
-#include <linux/stringify.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/err.h>
-
-#include <mach/irqs.h>
-#include <mach/scm.h>
-#include <mach/peripheral-loader.h>
-#include <mach/subsystem_restart.h>
-#include <mach/subsystem_notif.h>
-
-#include "smd_private.h"
-#include "modem_notifier.h"
-#include "ramdump.h"
-
-#define Q6SS_WDOG_ENABLE 0x28882024
-#define Q6SS_SOFT_INTR_WAKEUP 0x288A001C
-#define MODULE_NAME "lpass_8x60"
-#define SCM_Q6_NMI_CMD 0x1
-
-static struct subsys_device *subsys_8x60_q6_dev;
-
-/* Subsystem restart: QDSP6 data, functions */
-static void *q6_ramdump_dev;
-static void q6_fatal_fn(struct work_struct *);
-static DECLARE_WORK(q6_fatal_work, q6_fatal_fn);
-static void __iomem *q6_wakeup_intr;
-
-static void q6_fatal_fn(struct work_struct *work)
-{
- pr_err("%s: Watchdog bite received from Q6!\n", MODULE_NAME);
- subsystem_restart_dev(subsys_8x60_q6_dev);
- enable_irq(LPASS_Q6SS_WDOG_EXPIRED);
-}
-
-static void send_q6_nmi(void)
-{
- /* Send NMI to QDSP6 via an SCM call. */
- scm_call_atomic1(SCM_SVC_UTIL, SCM_Q6_NMI_CMD, 0x1);
-
- /* Wakeup the Q6 */
- if (q6_wakeup_intr)
- writel_relaxed(0x2000, q6_wakeup_intr);
- else
- pr_warn("lpass-8660: Unable to send wakeup interrupt to Q6.\n");
-
- /* Q6 requires atleast 100ms to dump caches etc.*/
- mdelay(100);
-
- pr_info("subsystem-fatal-8x60: Q6 NMI was sent.\n");
-}
-
-int subsys_q6_shutdown(const struct subsys_desc *crashed_subsys)
-{
- void __iomem *q6_wdog_addr =
- ioremap_nocache(Q6SS_WDOG_ENABLE, 8);
-
- send_q6_nmi();
- writel_relaxed(0x0, q6_wdog_addr);
- /* The write needs to go through before the q6 is shutdown. */
- mb();
- iounmap(q6_wdog_addr);
-
- pil_force_shutdown("q6");
- disable_irq_nosync(LPASS_Q6SS_WDOG_EXPIRED);
-
- return 0;
-}
-
-int subsys_q6_powerup(const struct subsys_desc *crashed_subsys)
-{
- int ret = pil_force_boot("q6");
- enable_irq(LPASS_Q6SS_WDOG_EXPIRED);
- return ret;
-}
-
-/* FIXME: Get address, size from PIL */
-static struct ramdump_segment q6_segments[] = { {0x46700000, 0x47F00000 -
- 0x46700000}, {0x28400000, 0x12800} };
-static int subsys_q6_ramdump(int enable,
- const struct subsys_desc *crashed_subsys)
-{
- if (enable)
- return do_ramdump(q6_ramdump_dev, q6_segments,
- ARRAY_SIZE(q6_segments));
- else
- return 0;
-}
-
-void subsys_q6_crash_shutdown(const struct subsys_desc *crashed_subsys)
-{
- send_q6_nmi();
-}
-
-static irqreturn_t lpass_wdog_bite_irq(int irq, void *dev_id)
-{
- int ret;
-
- ret = schedule_work(&q6_fatal_work);
- disable_irq_nosync(LPASS_Q6SS_WDOG_EXPIRED);
-
- return IRQ_HANDLED;
-}
-
-static struct subsys_desc subsys_8x60_q6 = {
- .name = "lpass",
- .shutdown = subsys_q6_shutdown,
- .powerup = subsys_q6_powerup,
- .ramdump = subsys_q6_ramdump,
- .crash_shutdown = subsys_q6_crash_shutdown
-};
-
-static void __exit lpass_fatal_exit(void)
-{
- subsys_unregister(subsys_8x60_q6_dev);
- iounmap(q6_wakeup_intr);
- free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
-}
-
-static int __init lpass_fatal_init(void)
-{
- int ret;
-
- ret = request_irq(LPASS_Q6SS_WDOG_EXPIRED, lpass_wdog_bite_irq,
- IRQF_TRIGGER_RISING, "q6_wdog", NULL);
-
- if (ret < 0) {
- pr_err("%s: Unable to request LPASS_Q6SS_WDOG_EXPIRED irq.",
- __func__);
- goto out;
- }
-
- q6_ramdump_dev = create_ramdump_device("lpass");
-
- if (!q6_ramdump_dev) {
- ret = -ENOMEM;
- goto out;
- }
-
- q6_wakeup_intr = ioremap_nocache(Q6SS_SOFT_INTR_WAKEUP, 8);
-
- if (!q6_wakeup_intr)
- pr_warn("lpass-8660: Unable to ioremap q6 wakeup address.");
-
- subsys_8x60_q6_dev = subsys_register(&subsys_8x60_q6);
- if (IS_ERR(subsys_8x60_q6_dev))
- ret = PTR_ERR(subsys_8x60_q6_dev);
-out:
- return ret;
-}
-
-module_init(lpass_fatal_init);
-module_exit(lpass_fatal_exit);
-
diff --git a/arch/arm/mach-msm/lpass-8960.c b/arch/arm/mach-msm/lpass-8960.c
deleted file mode 100644
index b714a7f..0000000
--- a/arch/arm/mach-msm/lpass-8960.c
+++ /dev/null
@@ -1,290 +0,0 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/reboot.h>
-#include <linux/workqueue.h>
-#include <linux/io.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/err.h>
-
-#include <mach/irqs.h>
-#include <mach/scm.h>
-#include <mach/peripheral-loader.h>
-#include <mach/subsystem_restart.h>
-#include <mach/subsystem_notif.h>
-
-#include "smd_private.h"
-#include "ramdump.h"
-#include "sysmon.h"
-
-#define SCM_Q6_NMI_CMD 0x1
-#define MODULE_NAME "lpass_8960"
-#define MAX_BUF_SIZE 0x51
-
-/* Subsystem restart: QDSP6 data, functions */
-static void lpass_fatal_fn(struct work_struct *);
-static DECLARE_WORK(lpass_fatal_work, lpass_fatal_fn);
-struct lpass_ssr {
- void *lpass_ramdump_dev;
-} lpass_ssr;
-
-static struct lpass_ssr lpass_ssr_8960;
-static int q6_crash_shutdown;
-
-static int riva_notifier_cb(struct notifier_block *this, unsigned long code,
- void *ss_handle)
-{
- int ret;
- switch (code) {
- case SUBSYS_BEFORE_SHUTDOWN:
- pr_debug("%s: R-Notify: Shutdown started\n", __func__);
- ret = sysmon_send_event(SYSMON_SS_LPASS, "wcnss",
- SUBSYS_BEFORE_SHUTDOWN);
- if (ret < 0)
- pr_err("%s: sysmon_send_event error %d", __func__,
- ret);
- break;
- }
- return NOTIFY_DONE;
-}
-
-static void *ssr_notif_hdle;
-static struct notifier_block rnb = {
- .notifier_call = riva_notifier_cb,
-};
-
-static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
- void *ss_handle)
-{
- int ret;
- switch (code) {
- case SUBSYS_BEFORE_SHUTDOWN:
- pr_debug("%s: M-Notify: Shutdown started\n", __func__);
- ret = sysmon_send_event(SYSMON_SS_LPASS, "modem",
- SUBSYS_BEFORE_SHUTDOWN);
- if (ret < 0)
- pr_err("%s: sysmon_send_event error %d", __func__,
- ret);
- break;
- }
- return NOTIFY_DONE;
-}
-
-static void *ssr_modem_notif_hdle;
-static struct notifier_block mnb = {
- .notifier_call = modem_notifier_cb,
-};
-
-static void lpass_log_failure_reason(void)
-{
- char *reason;
- char buffer[MAX_BUF_SIZE];
- unsigned size;
-
- reason = smem_get_entry(SMEM_SSR_REASON_LPASS0, &size);
-
- if (!reason) {
- pr_err("%s: subsystem failure reason: (unknown, smem_get_entry failed).",
- MODULE_NAME);
- return;
- }
-
- if (reason[0] == '\0') {
- pr_err("%s: subsystem failure reason: (unknown, init value found)",
- MODULE_NAME);
- return;
- }
-
- size = size < MAX_BUF_SIZE ? size : (MAX_BUF_SIZE-1);
- memcpy(buffer, reason, size);
- buffer[size] = '\0';
- pr_err("%s: subsystem failure reason: %s", MODULE_NAME, buffer);
- memset((void *)reason, 0x0, size);
- wmb();
-}
-
-static void lpass_fatal_fn(struct work_struct *work)
-{
- pr_err("%s %s: Watchdog bite received from Q6!\n", MODULE_NAME,
- __func__);
- lpass_log_failure_reason();
- panic(MODULE_NAME ": Resetting the SoC");
-}
-
-static void lpass_smsm_state_cb(void *data, uint32_t old_state,
- uint32_t new_state)
-{
- /* Ignore if we're the one that set SMSM_RESET */
- if (q6_crash_shutdown)
- return;
-
- if (new_state & SMSM_RESET) {
- pr_err("%s: LPASS SMSM state changed to SMSM_RESET,"
- " new_state = 0x%x, old_state = 0x%x\n", __func__,
- new_state, old_state);
- lpass_log_failure_reason();
- panic(MODULE_NAME ": Resetting the SoC");
- }
-}
-
-static void send_q6_nmi(void)
-{
- /* Send NMI to QDSP6 via an SCM call. */
- uint32_t cmd = 0x1;
-
- scm_call(SCM_SVC_UTIL, SCM_Q6_NMI_CMD,
- &cmd, sizeof(cmd), NULL, 0);
-
- /* Q6 requires worstcase 100ms to dump caches etc.*/
- mdelay(100);
- pr_debug("%s: Q6 NMI was sent.\n", __func__);
-}
-
-static int lpass_shutdown(const struct subsys_desc *subsys)
-{
- send_q6_nmi();
- pil_force_shutdown("q6");
- disable_irq_nosync(LPASS_Q6SS_WDOG_EXPIRED);
-
- return 0;
-}
-
-static int lpass_powerup(const struct subsys_desc *subsys)
-{
- int ret = pil_force_boot("q6");
- enable_irq(LPASS_Q6SS_WDOG_EXPIRED);
- return ret;
-}
-/* RAM segments - address and size for 8960 */
-static struct ramdump_segment q6_segments[] = { {0x8da00000, 0x8f200000 -
- 0x8da00000}, {0x28400000, 0x20000} };
-static int lpass_ramdump(int enable, const struct subsys_desc *subsys)
-{
- pr_debug("%s: enable[%d]\n", __func__, enable);
- if (enable)
- return do_ramdump(lpass_ssr_8960.lpass_ramdump_dev,
- q6_segments,
- ARRAY_SIZE(q6_segments));
- else
- return 0;
-}
-
-static void lpass_crash_shutdown(const struct subsys_desc *subsys)
-{
- q6_crash_shutdown = 1;
- send_q6_nmi();
-}
-
-static irqreturn_t lpass_wdog_bite_irq(int irq, void *dev_id)
-{
- int ret;
-
- pr_debug("%s: rxed irq[0x%x]", __func__, irq);
- disable_irq_nosync(LPASS_Q6SS_WDOG_EXPIRED);
- ret = schedule_work(&lpass_fatal_work);
-
- return IRQ_HANDLED;
-}
-
-static struct subsys_device *lpass_8960_dev;
-
-static struct subsys_desc lpass_8960 = {
- .name = "lpass",
- .shutdown = lpass_shutdown,
- .powerup = lpass_powerup,
- .ramdump = lpass_ramdump,
- .crash_shutdown = lpass_crash_shutdown
-};
-
-static int __init lpass_restart_init(void)
-{
- lpass_8960_dev = subsys_register(&lpass_8960);
- if (IS_ERR(lpass_8960_dev))
- return PTR_ERR(lpass_8960_dev);
- return 0;
-}
-
-static int __init lpass_fatal_init(void)
-{
- int ret;
-
- ret = smsm_state_cb_register(SMSM_Q6_STATE, SMSM_RESET,
- lpass_smsm_state_cb, 0);
-
- if (ret < 0)
- pr_err("%s: Unable to register SMSM callback! (%d)\n",
- __func__, ret);
-
- ret = request_irq(LPASS_Q6SS_WDOG_EXPIRED, lpass_wdog_bite_irq,
- IRQF_TRIGGER_RISING, "q6_wdog", NULL);
-
- if (ret < 0) {
- pr_err("%s: Unable to request LPASS_Q6SS_WDOG_EXPIRED irq.",
- __func__);
- goto out;
- }
- ret = lpass_restart_init();
- if (ret < 0) {
- pr_err("%s: Unable to reg with lpass ssr. (%d)\n",
- __func__, ret);
- goto out;
- }
-
- lpass_ssr_8960.lpass_ramdump_dev = create_ramdump_device("lpass");
-
- if (!lpass_ssr_8960.lpass_ramdump_dev) {
- pr_err("%s: Unable to create ramdump device.\n",
- __func__);
- ret = -ENOMEM;
- goto out;
- }
- ssr_notif_hdle = subsys_notif_register_notifier("riva",
- &rnb);
- if (IS_ERR(ssr_notif_hdle) < 0) {
- ret = PTR_ERR(ssr_notif_hdle);
- pr_err("%s: subsys_register_notifier for Riva: err = %d\n",
- __func__, ret);
- free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
- goto out;
- }
-
- ssr_modem_notif_hdle = subsys_notif_register_notifier("modem",
- &mnb);
- if (IS_ERR(ssr_modem_notif_hdle) < 0) {
- ret = PTR_ERR(ssr_modem_notif_hdle);
- pr_err("%s: subsys_register_notifier for Modem: err = %d\n",
- __func__, ret);
- subsys_notif_unregister_notifier(ssr_notif_hdle, &rnb);
- free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
- goto out;
- }
-
- pr_info("%s: lpass SSR driver init'ed.\n", __func__);
-out:
- return ret;
-}
-
-static void __exit lpass_fatal_exit(void)
-{
- subsys_notif_unregister_notifier(ssr_notif_hdle, &rnb);
- subsys_notif_unregister_notifier(ssr_modem_notif_hdle, &mnb);
- subsys_unregister(lpass_8960_dev);
- free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
-}
-
-module_init(lpass_fatal_init);
-module_exit(lpass_fatal_exit);
-
-MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/lpm_levels.c b/arch/arm/mach-msm/lpm_levels.c
index 8218a42..4854fc48 100644
--- a/arch/arm/mach-msm/lpm_levels.c
+++ b/arch/arm/mach-msm/lpm_levels.c
@@ -22,6 +22,18 @@
#include "pm.h"
#include "rpm-notifier.h"
+
+enum {
+ MSM_LPM_LVL_DBG_SUSPEND_LIMITS = BIT(0),
+ MSM_LPM_LVL_DBG_IDLE_LIMITS = BIT(1),
+};
+
+static int msm_lpm_lvl_dbg_msk;
+
+module_param_named(
+ debug_mask, msm_lpm_lvl_dbg_msk, int, S_IRUGO | S_IWUSR | S_IWGRP
+);
+
static struct msm_rpmrs_level *msm_lpm_levels;
static int msm_lpm_level_count;
@@ -41,6 +53,7 @@
bool from_idle, bool notify_rpm)
{
int ret = 0;
+ int debug_mask;
struct msm_rpmrs_limits *l = (struct msm_rpmrs_limits *)limits;
ret = msm_rpm_enter_sleep();
@@ -49,6 +62,21 @@
__func__, ret);
goto bail;
}
+ if (from_idle)
+ debug_mask = msm_lpm_lvl_dbg_msk &
+ MSM_LPM_LVL_DBG_IDLE_LIMITS;
+ else
+ debug_mask = msm_lpm_lvl_dbg_msk &
+ MSM_LPM_LVL_DBG_SUSPEND_LIMITS;
+
+ if (debug_mask)
+ pr_info("%s(): pxo:%d l2:%d mem:0x%x(0x%x) dig:0x%x(0x%x)\n",
+ __func__, l->pxo, l->l2_cache,
+ l->vdd_mem_lower_bound,
+ l->vdd_mem_upper_bound,
+ l->vdd_dig_lower_bound,
+ l->vdd_dig_upper_bound);
+
ret = msm_lpmrs_enter_sleep(sclk_count, l, from_idle, notify_rpm);
bail:
return ret;
diff --git a/arch/arm/mach-msm/lpm_resources.c b/arch/arm/mach-msm/lpm_resources.c
index 48d31f3..2db92f3 100644
--- a/arch/arm/mach-msm/lpm_resources.c
+++ b/arch/arm/mach-msm/lpm_resources.c
@@ -104,6 +104,11 @@
struct msm_rpm_request *handle;
};
+enum {
+ MSM_LPM_RPM_RS_TYPE = 0,
+ MSM_LPM_LOCAL_RS_TYPE = 1,
+};
+
struct msm_lpm_resource {
struct msm_lpm_rs_data rs_data;
uint32_t sleep_value;
@@ -126,7 +131,7 @@
.aggregate = msm_lpm_aggregate_l2,
.flush = msm_lpm_flush_l2,
.notify = NULL,
- .valid = true,
+ .valid = false,
.rs_data = {
.value = MSM_LPM_L2_CACHE_ACTIVE,
.default_value = MSM_LPM_L2_CACHE_ACTIVE,
@@ -382,7 +387,7 @@
static bool msm_lpm_beyond_limits_l2(struct msm_rpmrs_limits *limits)
{
uint32_t l2;
- bool ret = true;
+ bool ret = false;
struct msm_lpm_resource *rs = &msm_lpm_l2;
if (rs->valid) {
@@ -664,7 +669,7 @@
msm_lpm_get_rpm_notif = false;
for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
rs = msm_lpm_resources[i];
- if (rs->flush)
+ if (rs->valid && rs->flush)
rs->flush(notify_rpm);
}
msm_lpm_get_rpm_notif = true;
@@ -787,6 +792,7 @@
struct msm_lpm_resource *rs = NULL;
const char *val;
int i;
+ uint32_t resource_type;
key = "qcom,name";
ret = of_property_read_string(node, key, &val);
@@ -810,49 +816,73 @@
continue;
}
- key = "qcom,type";
- ret = of_property_read_u32(node, key, &rs->rs_data.type);
+ key = "qcom,resource-type";
+ ret = of_property_read_u32(node, key, &resource_type);
if (ret) {
- pr_err("Failed to read type\n");
+ pr_err("Failed to read resource-type\n");
goto fail;
}
- key = "qcom,id";
- ret = of_property_read_u32(node, key, &rs->rs_data.id);
- if (ret) {
- pr_err("Failed to read id\n");
+ switch (resource_type) {
+ case MSM_LPM_RPM_RS_TYPE:
+ key = "qcom,type";
+ ret = of_property_read_u32(node, key,
+ &rs->rs_data.type);
+ if (ret) {
+ pr_err("Failed to read type\n");
+ goto fail;
+ }
+
+ key = "qcom,id";
+ ret = of_property_read_u32(node, key, &rs->rs_data.id);
+ if (ret) {
+ pr_err("Failed to read id\n");
+ goto fail;
+ }
+
+ key = "qcom,key";
+ ret = of_property_read_u32(node, key, &rs->rs_data.key);
+ if (ret) {
+ pr_err("Failed to read key\n");
+ goto fail;
+ }
+
+ rs->rs_data.handle = msm_lpm_create_rpm_request(
+ rs->rs_data.type,
+ rs->rs_data.id);
+
+ if (!rs->rs_data.handle) {
+ pr_err("%s: Failed to allocate handle for %s\n",
+ __func__, rs->name);
+ ret = -1;
+ goto fail;
+ }
+ /* fall through */
+
+ case MSM_LPM_LOCAL_RS_TYPE:
+ rs->valid = true;
+ break;
+ default:
+ pr_err("%s: Invalid resource type %d", __func__,
+ resource_type);
goto fail;
}
-
- key = "qcom,key";
- ret = of_property_read_u32(node, key, &rs->rs_data.key);
- if (ret) {
- pr_err("Failed to read key\n");
- goto fail;
- }
-
- rs->rs_data.handle = msm_lpm_create_rpm_request(
- rs->rs_data.type, rs->rs_data.id);
-
- if (!rs->rs_data.handle) {
- pr_err("%s: Failed to allocate handle for %s\n",
- __func__, rs->name);
- ret = -1;
- goto fail;
- }
-
- rs->valid = true;
}
msm_rpm_register_notifier(&msm_lpm_rpm_nblk);
msm_lpm_init_rpm_ctl();
- register_hotcpu_notifier(&msm_lpm_cpu_nblk);
- /* For UP mode, set the default to HSFS OPEN*/
- if (num_possible_cpus() == 1) {
- msm_lpm_l2.rs_data.default_value = MSM_LPM_L2_CACHE_HSFS_OPEN;
- msm_lpm_l2.rs_data.value = MSM_LPM_L2_CACHE_HSFS_OPEN;
- }
- msm_pm_set_l2_flush_flag(0);
- return 0;
+
+ if (msm_lpm_l2.valid) {
+ register_hotcpu_notifier(&msm_lpm_cpu_nblk);
+ /* For UP mode, set the default to HSFS OPEN*/
+ if (num_possible_cpus() == 1) {
+ msm_lpm_l2.rs_data.default_value =
+ MSM_LPM_L2_CACHE_HSFS_OPEN;
+ msm_lpm_l2.rs_data.value = MSM_LPM_L2_CACHE_HSFS_OPEN;
+ }
+ msm_pm_set_l2_flush_flag(0);
+ } else
+ msm_pm_set_l2_flush_flag(1);
+
fail:
return ret;
}
diff --git a/arch/arm/mach-msm/memory.c b/arch/arm/mach-msm/memory.c
index 74c1c4a..7d7380b 100644
--- a/arch/arm/mach-msm/memory.c
+++ b/arch/arm/mach-msm/memory.c
@@ -44,14 +44,14 @@
#include <../../mm/mm.h>
#include <linux/fmem.h>
-void *strongly_ordered_page;
-char strongly_ordered_mem[PAGE_SIZE*2-4];
+#if defined(CONFIG_ARCH_MSM7X27)
+static void *strongly_ordered_page;
+static char strongly_ordered_mem[PAGE_SIZE*2-4];
-void map_page_strongly_ordered(void)
+void __init map_page_strongly_ordered(void)
{
-#if defined(CONFIG_ARCH_MSM7X27) && !defined(CONFIG_ARCH_MSM7X27A)
long unsigned int phys;
- struct map_desc map;
+ struct map_desc map[1];
if (strongly_ordered_page)
return;
@@ -59,33 +59,26 @@
strongly_ordered_page = (void*)PFN_ALIGN((int)&strongly_ordered_mem);
phys = __pa(strongly_ordered_page);
- map.pfn = __phys_to_pfn(phys);
- map.virtual = MSM_STRONGLY_ORDERED_PAGE;
- map.length = PAGE_SIZE;
- map.type = MT_DEVICE_STRONGLY_ORDERED;
- create_mapping(&map);
+ map[0].pfn = __phys_to_pfn(phys);
+ map[0].virtual = MSM_STRONGLY_ORDERED_PAGE;
+ map[0].length = PAGE_SIZE;
+ map[0].type = MT_MEMORY_SO;
+ iotable_init(map, ARRAY_SIZE(map));
printk(KERN_ALERT "Initialized strongly ordered page successfully\n");
-#endif
}
-EXPORT_SYMBOL(map_page_strongly_ordered);
+#else
+void map_page_strongly_ordered(void) { }
+#endif
+#if defined(CONFIG_ARCH_MSM7X27)
void write_to_strongly_ordered_memory(void)
{
-#if defined(CONFIG_ARCH_MSM7X27) && !defined(CONFIG_ARCH_MSM7X27A)
- if (!strongly_ordered_page) {
- if (!in_interrupt())
- map_page_strongly_ordered();
- else {
- printk(KERN_ALERT "Cannot map strongly ordered page in "
- "Interrupt Context\n");
- /* capture it here before the allocation fails later */
- BUG();
- }
- }
*(int *)MSM_STRONGLY_ORDERED_PAGE = 0;
-#endif
}
+#else
+void write_to_strongly_ordered_memory(void) { }
+#endif
EXPORT_SYMBOL(write_to_strongly_ordered_memory);
/* These cache related routines make the assumption (if outer cache is
@@ -173,63 +166,29 @@
struct reserve_info *reserve_info;
-static unsigned long stable_size(struct membank *mb,
- unsigned long unstable_limit)
-{
- unsigned long upper_limit = mb->start + mb->size;
-
- if (!unstable_limit)
- return mb->size;
-
- /* Check for 32 bit roll-over */
- if (upper_limit >= mb->start) {
- /* If we didn't roll over we can safely make the check below */
- if (upper_limit <= unstable_limit)
- return mb->size;
- }
-
- if (mb->start >= unstable_limit)
- return 0;
- return unstable_limit - mb->start;
-}
-
-/* stable size of all memory banks contiguous to and below this one */
-static unsigned long total_stable_size(unsigned long bank)
-{
- int i;
- struct membank *mb = &meminfo.bank[bank];
- int memtype = reserve_info->paddr_to_memtype(mb->start);
- unsigned long size;
-
- size = stable_size(mb, reserve_info->low_unstable_address);
- for (i = bank - 1, mb = &meminfo.bank[bank - 1]; i >= 0; i--, mb--) {
- if (mb->start + mb->size != (mb + 1)->start)
- break;
- if (reserve_info->paddr_to_memtype(mb->start) != memtype)
- break;
- size += stable_size(mb, reserve_info->low_unstable_address);
- }
- return size;
-}
-
+/**
+ * calculate_reserve_limits() - calculate reserve limits for all
+ * memtypes
+ *
+ * for each memtype in the reserve_info->memtype_reserve_table, sets
+ * the `limit' field to the largest size of any memblock of that
+ * memtype.
+ */
static void __init calculate_reserve_limits(void)
{
- int i;
- struct membank *mb;
+ struct memblock_region *mr;
int memtype;
struct memtype_reserve *mt;
- unsigned long size;
- for (i = 0, mb = &meminfo.bank[0]; i < meminfo.nr_banks; i++, mb++) {
- memtype = reserve_info->paddr_to_memtype(mb->start);
+ for_each_memblock(memory, mr) {
+ memtype = reserve_info->paddr_to_memtype(mr->base);
if (memtype == MEMTYPE_NONE) {
- pr_warning("unknown memory type for bank at %lx\n",
- (long unsigned int)mb->start);
+ pr_warning("unknown memory type for region at %lx\n",
+ (long unsigned int)mr->base);
continue;
}
mt = &reserve_info->memtype_reserve_table[memtype];
- size = total_stable_size(i);
- mt->limit = max(mt->limit, size);
+ mt->limit = max_t(unsigned long, mt->limit, mr->size);
}
}
@@ -252,50 +211,38 @@
static void __init reserve_memory_for_mempools(void)
{
- int i, memtype, membank_type;
+ int memtype, memreg_type;
struct memtype_reserve *mt;
- struct membank *mb;
+ struct memblock_region *mr, *mr_candidate = NULL;
int ret;
- unsigned long size;
mt = &reserve_info->memtype_reserve_table[0];
for (memtype = 0; memtype < MEMTYPE_MAX; memtype++, mt++) {
if (mt->flags & MEMTYPE_FLAGS_FIXED || !mt->size)
continue;
- /* We know we will find memory bank(s) of the proper size
- * as we have limited the size of the memory pool for
- * each memory type to the largest total size of the memory
- * banks which are contiguous and of the correct memory type.
- * Choose the memory bank with the highest physical
+ /* Choose the memory block with the highest physical
* address which is large enough, so that we will not
* take memory from the lowest memory bank which the kernel
* is in (and cause boot problems) and so that we might
* be able to steal memory that would otherwise become
- * highmem. However, do not use unstable memory.
+ * highmem.
*/
- for (i = meminfo.nr_banks - 1; i >= 0; i--) {
- mb = &meminfo.bank[i];
- membank_type =
- reserve_info->paddr_to_memtype(mb->start);
- if (memtype != membank_type)
+ for_each_memblock(memory, mr) {
+ memreg_type =
+ reserve_info->paddr_to_memtype(mr->base);
+ if (memtype != memreg_type)
continue;
- size = total_stable_size(i);
- if (size >= mt->size) {
- size = stable_size(mb,
- reserve_info->low_unstable_address);
- if (!size)
- continue;
- /* mt->size may be larger than size, all this
- * means is that we are carving the memory pool
- * out of multiple contiguous memory banks.
- */
- mt->start = mb->start + (size - mt->size);
- ret = memblock_remove(mt->start, mt->size);
- BUG_ON(ret);
- break;
- }
+ if (mr->size >= mt->size
+ && (mr_candidate == NULL
+ || mr->base > mr_candidate->base))
+ mr_candidate = mr;
}
+ BUG_ON(mr_candidate == NULL);
+ /* bump mt up against the top of the region */
+ mt->start = mr_candidate->base + mr_candidate->size - mt->size;
+ ret = memblock_remove(mt->start, mt->size);
+ BUG_ON(ret);
}
}
@@ -324,7 +271,8 @@
unsigned long msm_fixed_area_start;
memory_pool_init();
- reserve_info->calculate_reserve_sizes();
+ if (reserve_info->calculate_reserve_sizes)
+ reserve_info->calculate_reserve_sizes();
msm_fixed_area_size = reserve_info->fixed_area_size;
msm_fixed_area_start = reserve_info->fixed_area_start;
diff --git a/arch/arm/mach-msm/modem-8960.c b/arch/arm/mach-msm/modem-8960.c
deleted file mode 100644
index 83b3bc4..0000000
--- a/arch/arm/mach-msm/modem-8960.c
+++ /dev/null
@@ -1,332 +0,0 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/reboot.h>
-#include <linux/workqueue.h>
-#include <linux/io.h>
-#include <linux/jiffies.h>
-#include <linux/stringify.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/debugfs.h>
-
-#include <mach/irqs.h>
-#include <mach/scm.h>
-#include <mach/peripheral-loader.h>
-#include <mach/subsystem_restart.h>
-#include <mach/subsystem_notif.h>
-#include <mach/socinfo.h>
-#include <mach/msm_smsm.h>
-
-#include "smd_private.h"
-#include "modem_notifier.h"
-#include "ramdump.h"
-
-static int crash_shutdown;
-
-static struct subsys_device *modem_8960_dev;
-
-#define MAX_SSR_REASON_LEN 81U
-
-static void log_modem_sfr(void)
-{
- u32 size;
- char *smem_reason, reason[MAX_SSR_REASON_LEN];
-
- smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size);
- if (!smem_reason || !size) {
- pr_err("modem subsystem failure reason: (unknown, smem_get_entry failed).\n");
- return;
- }
- if (!smem_reason[0]) {
- pr_err("modem subsystem failure reason: (unknown, init string found).\n");
- return;
- }
-
- size = min(size, MAX_SSR_REASON_LEN-1);
- memcpy(reason, smem_reason, size);
- reason[size] = '\0';
- pr_err("modem subsystem failure reason: %s.\n", reason);
-
- smem_reason[0] = '\0';
- wmb();
-}
-
-static void restart_modem(void)
-{
- log_modem_sfr();
- subsystem_restart_dev(modem_8960_dev);
-}
-
-static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
-{
- /* Ignore if we're the one that set SMSM_RESET */
- if (crash_shutdown)
- return;
-
- if (new_state & SMSM_RESET) {
- pr_err("Probable fatal error on the modem.\n");
- restart_modem();
- }
-}
-
-#define Q6_FW_WDOG_ENABLE 0x08882024
-#define Q6_SW_WDOG_ENABLE 0x08982024
-
-static int modem_shutdown(const struct subsys_desc *subsys)
-{
- void __iomem *q6_fw_wdog_addr;
- void __iomem *q6_sw_wdog_addr;
-
- /*
- * Disable the modem watchdog since it keeps running even after the
- * modem is shutdown.
- */
- q6_fw_wdog_addr = ioremap_nocache(Q6_FW_WDOG_ENABLE, 4);
- if (!q6_fw_wdog_addr)
- return -ENOMEM;
-
- q6_sw_wdog_addr = ioremap_nocache(Q6_SW_WDOG_ENABLE, 4);
- if (!q6_sw_wdog_addr) {
- iounmap(q6_fw_wdog_addr);
- return -ENOMEM;
- }
-
- writel_relaxed(0x0, q6_fw_wdog_addr);
- writel_relaxed(0x0, q6_sw_wdog_addr);
- mb();
- iounmap(q6_sw_wdog_addr);
- iounmap(q6_fw_wdog_addr);
-
- pil_force_shutdown("modem");
- pil_force_shutdown("modem_fw");
- disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
- disable_irq_nosync(Q6SW_WDOG_EXPIRED_IRQ);
-
- return 0;
-}
-
-#define MODEM_WDOG_CHECK_TIMEOUT_MS 10000
-
-static int modem_powerup(const struct subsys_desc *subsys)
-{
- pil_force_boot("modem_fw");
- pil_force_boot("modem");
- enable_irq(Q6FW_WDOG_EXPIRED_IRQ);
- enable_irq(Q6SW_WDOG_EXPIRED_IRQ);
- return 0;
-}
-
-void modem_crash_shutdown(const struct subsys_desc *subsys)
-{
- crash_shutdown = 1;
- smsm_reset_modem(SMSM_RESET);
-}
-
-/* FIXME: Get address, size from PIL */
-static struct ramdump_segment modemsw_segments[] = {
- {0x89000000, 0x8D400000 - 0x89000000},
-};
-
-static struct ramdump_segment modemfw_segments[] = {
- {0x8D400000, 0x8DA00000 - 0x8D400000},
-};
-
-static struct ramdump_segment smem_segments[] = {
- {0x80000000, 0x00200000},
-};
-
-static void *modemfw_ramdump_dev;
-static void *modemsw_ramdump_dev;
-static void *smem_ramdump_dev;
-
-static int modem_ramdump(int enable, const struct subsys_desc *crashed_subsys)
-{
- int ret = 0;
-
- if (enable) {
- ret = do_ramdump(modemsw_ramdump_dev, modemsw_segments,
- ARRAY_SIZE(modemsw_segments));
-
- if (ret < 0) {
- pr_err("Unable to dump modem sw memory (rc = %d).\n",
- ret);
- goto out;
- }
-
- ret = do_ramdump(modemfw_ramdump_dev, modemfw_segments,
- ARRAY_SIZE(modemfw_segments));
-
- if (ret < 0) {
- pr_err("Unable to dump modem fw memory (rc = %d).\n",
- ret);
- goto out;
- }
-
- ret = do_ramdump(smem_ramdump_dev, smem_segments,
- ARRAY_SIZE(smem_segments));
-
- if (ret < 0) {
- pr_err("Unable to dump smem memory (rc = %d).\n", ret);
- goto out;
- }
- }
-
-out:
- return ret;
-}
-
-static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
-{
- switch (irq) {
-
- case Q6SW_WDOG_EXPIRED_IRQ:
- pr_err("Watchdog bite received from modem software!\n");
- restart_modem();
- break;
- case Q6FW_WDOG_EXPIRED_IRQ:
- pr_err("Watchdog bite received from modem firmware!\n");
- restart_modem();
- break;
- break;
-
- default:
- pr_err("%s: Unknown IRQ!\n", __func__);
- }
-
- return IRQ_HANDLED;
-}
-
-static struct subsys_desc modem_8960 = {
- .name = "modem",
- .shutdown = modem_shutdown,
- .powerup = modem_powerup,
- .ramdump = modem_ramdump,
- .crash_shutdown = modem_crash_shutdown
-};
-
-static int modem_subsystem_restart_init(void)
-{
- modem_8960_dev = subsys_register(&modem_8960);
- if (IS_ERR(modem_8960_dev))
- return PTR_ERR(modem_8960_dev);
- return 0;
-}
-
-static int modem_debug_set(void *data, u64 val)
-{
- if (val == 1)
- subsystem_restart_dev(modem_8960_dev);
-
- return 0;
-}
-
-static int modem_debug_get(void *data, u64 *val)
-{
- *val = 0;
- return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(modem_debug_fops, modem_debug_get, modem_debug_set,
- "%llu\n");
-
-static int modem_debugfs_init(void)
-{
- struct dentry *dent;
- dent = debugfs_create_dir("modem_debug", 0);
-
- if (IS_ERR(dent))
- return PTR_ERR(dent);
-
- debugfs_create_file("reset_modem", 0644, dent, NULL,
- &modem_debug_fops);
- return 0;
-}
-
-static int __init modem_8960_init(void)
-{
- int ret;
-
- if (cpu_is_apq8064() || cpu_is_apq8064ab())
- return -ENODEV;
-
- ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
- smsm_state_cb, 0);
-
- if (ret < 0)
- pr_err("%s: Unable to register SMSM callback! (%d)\n",
- __func__, ret);
-
- ret = request_irq(Q6FW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq,
- IRQF_TRIGGER_RISING, "modem_wdog_fw", NULL);
-
- if (ret < 0) {
- pr_err("%s: Unable to request q6fw watchdog IRQ. (%d)\n",
- __func__, ret);
- goto out;
- }
-
- ret = request_irq(Q6SW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq,
- IRQF_TRIGGER_RISING, "modem_wdog_sw", NULL);
-
- if (ret < 0) {
- pr_err("%s: Unable to request q6sw watchdog IRQ. (%d)\n",
- __func__, ret);
- disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
- goto out;
- }
-
- ret = modem_subsystem_restart_init();
-
- if (ret < 0) {
- pr_err("%s: Unable to reg with subsystem restart. (%d)\n",
- __func__, ret);
- goto out;
- }
-
- modemfw_ramdump_dev = create_ramdump_device("modem_fw");
-
- if (!modemfw_ramdump_dev) {
- pr_err("%s: Unable to create modem fw ramdump device. (%d)\n",
- __func__, -ENOMEM);
- ret = -ENOMEM;
- goto out;
- }
-
- modemsw_ramdump_dev = create_ramdump_device("modem_sw");
-
- if (!modemsw_ramdump_dev) {
- pr_err("%s: Unable to create modem sw ramdump device. (%d)\n",
- __func__, -ENOMEM);
- ret = -ENOMEM;
- goto out;
- }
-
- smem_ramdump_dev = create_ramdump_device("smem-modem");
-
- if (!smem_ramdump_dev) {
- pr_err("%s: Unable to create smem ramdump device. (%d)\n",
- __func__, -ENOMEM);
- ret = -ENOMEM;
- goto out;
- }
-
- ret = modem_debugfs_init();
-
- pr_info("%s: modem fatal driver init'ed.\n", __func__);
-out:
- return ret;
-}
-
-module_init(modem_8960_init);
diff --git a/arch/arm/mach-msm/modem-ssr-8974.c b/arch/arm/mach-msm/modem-ssr-8974.c
deleted file mode 100644
index 942eca5..0000000
--- a/arch/arm/mach-msm/modem-ssr-8974.c
+++ /dev/null
@@ -1,212 +0,0 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/err.h>
-
-#include <mach/peripheral-loader.h>
-#include <mach/subsystem_restart.h>
-#include <mach/msm_smsm.h>
-
-#include "ramdump.h"
-
-static int crash_shutdown;
-static int modem_ssr_ignore_errors;
-static struct subsys_device *modem_ssr_dev;
-
-#define MAX_SSR_REASON_LEN 81U
-#define Q6SS_WDOG_ENABLE 0xFC802004
-#define MSS_Q6SS_WDOG_EXP_IRQ 56
-
-static void log_modem_sfr(void)
-{
- u32 size;
- char *smem_reason, reason[MAX_SSR_REASON_LEN];
-
- smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size);
- if (!smem_reason || !size) {
- pr_err("modem subsystem failure reason: (unknown, smem_get_entry failed).\n");
- return;
- }
- if (!smem_reason[0]) {
- pr_err("modem subsystem failure reason: (unknown, empty string found).\n");
- return;
- }
-
- strlcpy(reason, smem_reason, min(size, sizeof(reason)));
- pr_err("modem subsystem failure reason: %s.\n", reason);
-
- smem_reason[0] = '\0';
- wmb();
-}
-
-static void restart_modem(void)
-{
- log_modem_sfr();
- modem_ssr_ignore_errors = 1;
- subsystem_restart("modem");
-}
-
-static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
-{
- /* Ignore if we're the one that set SMSM_RESET */
- if (crash_shutdown)
- return;
-
- if (new_state & SMSM_RESET) {
- pr_err("Probable fatal error on the modem.\n");
- restart_modem();
- }
-}
-
-static int modem_shutdown(const struct subsys_desc *subsys)
-{
- pil_force_shutdown("modem");
- pil_force_shutdown("mba");
- return 0;
-}
-
-static int modem_powerup(const struct subsys_desc *subsys)
-{
- /*
- * At this time, the modem is shutdown. Therefore this function cannot
- * run concurrently with either the watchdog bite error handler or the
- * SMSM callback, making it safe to unset the flag below.
- */
- modem_ssr_ignore_errors = 0;
- pil_force_boot("mba");
- pil_force_boot("modem");
- return 0;
-}
-
-void modem_crash_shutdown(const struct subsys_desc *subsys)
-{
- crash_shutdown = 1;
- smsm_reset_modem(SMSM_RESET);
-}
-
-static struct ramdump_segment modem_segments[] = {
- {0x08400000, 0x0D100000 - 0x08400000},
-};
-
-static struct ramdump_segment smem_segments[] = {
- {0x0FA00000, 0x0FC00000 - 0x0FA00000},
-};
-
-static void *modem_ramdump_dev;
-static void *smem_ramdump_dev;
-
-static int modem_ramdump(int enable, const struct subsys_desc *crashed_subsys)
-{
- int ret = 0;
-
- if (!enable)
- return ret;
-
- pil_force_boot("mba");
-
- ret = do_ramdump(modem_ramdump_dev, modem_segments,
- ARRAY_SIZE(modem_segments));
-
- if (ret < 0) {
- pr_err("Unable to dump modem fw memory (rc = %d).\n",
- ret);
- goto out;
- }
-
- ret = do_ramdump(smem_ramdump_dev, smem_segments,
- ARRAY_SIZE(smem_segments));
-
- if (ret < 0) {
- pr_err("Unable to dump smem memory (rc = %d).\n", ret);
- goto out;
- }
-
-out:
- pil_force_shutdown("mba");
- return ret;
-}
-
-static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
-{
- if (modem_ssr_ignore_errors)
- return IRQ_HANDLED;
- pr_err("Watchdog bite received from the modem!\n");
- restart_modem();
- return IRQ_HANDLED;
-}
-
-static struct subsys_desc modem_8974 = {
- .name = "modem",
- .shutdown = modem_shutdown,
- .powerup = modem_powerup,
- .ramdump = modem_ramdump,
- .crash_shutdown = modem_crash_shutdown
-};
-
-static int __init modem_8974_init(void)
-{
- int ret;
-
- ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
- smsm_state_cb, 0);
-
- if (ret < 0) {
- pr_err("%s: Unable to register SMSM callback! (%d)\n",
- __func__, ret);
- goto out;
- }
-
- ret = request_irq(MSS_Q6SS_WDOG_EXP_IRQ, modem_wdog_bite_irq,
- IRQF_TRIGGER_RISING, "modem_wdog_sw", NULL);
-
- if (ret < 0) {
- pr_err("%s: Unable to request q6sw watchdog IRQ. (%d)\n",
- __func__, ret);
- goto out;
- }
-
- modem_ssr_dev = subsys_register(&modem_8974);
-
- if (IS_ERR_OR_NULL(modem_ssr_dev)) {
- pr_err("%s: Unable to reg with subsystem restart. (%ld)\n",
- __func__, PTR_ERR(modem_ssr_dev));
- ret = PTR_ERR(modem_ssr_dev);
- goto out;
- }
-
- modem_ramdump_dev = create_ramdump_device("modem");
-
- if (!modem_ramdump_dev) {
- pr_err("%s: Unable to create a modem ramdump device.\n",
- __func__);
- ret = -ENOMEM;
- goto out;
- }
-
- smem_ramdump_dev = create_ramdump_device("smem-modem");
-
- if (!smem_ramdump_dev) {
- pr_err("%s: Unable to create an smem ramdump device.\n",
- __func__);
- ret = -ENOMEM;
- goto out;
- }
-
- pr_info("%s: modem subsystem restart driver init'ed.\n", __func__);
-out:
- return ret;
-}
-
-module_init(modem_8974_init);
diff --git a/arch/arm/mach-msm/mpm-8625.c b/arch/arm/mach-msm/mpm-8625.c
index fe7ffff..c70ff5c 100644
--- a/arch/arm/mach-msm/mpm-8625.c
+++ b/arch/arm/mach-msm/mpm-8625.c
@@ -92,6 +92,7 @@
[MSM8625_INT_GPIO_GROUP2] = SMSM_FAKE_IRQ,
[MSM8625_INT_A9_M2A_0] = SMSM_FAKE_IRQ,
[MSM8625_INT_A9_M2A_1] = SMSM_FAKE_IRQ,
+ [MSM8625_INT_A9_M2A_2] = SMSM_FAKE_IRQ,
[MSM8625_INT_A9_M2A_5] = SMSM_FAKE_IRQ,
[MSM8625_INT_GP_TIMER_EXP] = SMSM_FAKE_IRQ,
[MSM8625_INT_DEBUG_TIMER_EXP] = SMSM_FAKE_IRQ,
diff --git a/arch/arm/mach-msm/msm-krait-l2-accessors.c b/arch/arm/mach-msm/msm-krait-l2-accessors.c
index 3da155a..2c66ea0 100644
--- a/arch/arm/mach-msm/msm-krait-l2-accessors.c
+++ b/arch/arm/mach-msm/msm-krait-l2-accessors.c
@@ -18,75 +18,12 @@
DEFINE_RAW_SPINLOCK(l2_access_lock);
-#define L2CPMR 0x500
-#define L2CPUCPMR 0x501
-#define L2CPUVRF8 0x708
-#define CPUNDX_MASK (0x7 << 12)
-
-/*
- * For Krait versions found in APQ8064v1.x, save L2CPUVRF8 before
- * L2CPMR or L2CPUCPMR writes and restore it after to work around an
- * issue where L2CPUVRF8 becomes corrupt.
- */
-static bool l2cpuvrf8_needs_fix(u32 reg_addr)
-{
- switch (read_cpuid_id()) {
- case 0x510F06F0: /* KR28M4A10 */
- case 0x510F06F1: /* KR28M4A10B */
- case 0x510F06F2: /* KR28M4A11 */
- break;
- default:
- return false;
- };
-
- switch (reg_addr & ~CPUNDX_MASK) {
- case L2CPMR:
- case L2CPUCPMR:
- return true;
- default:
- return false;
- }
-}
-
-static u32 l2cpuvrf8_fix_save(u32 reg_addr, u32 *l2cpuvrf8_val)
-{
- u32 l2cpuvrf8_addr = L2CPUVRF8 | (reg_addr & CPUNDX_MASK);
-
- mb();
- asm volatile ("mcr p15, 3, %[l2cpselr], c15, c0, 6\n\t"
- "isb\n\t"
- "mrc p15, 3, %[l2cpdr], c15, c0, 7\n\t"
- : [l2cpdr]"=r" (*l2cpuvrf8_val)
- : [l2cpselr]"r" (l2cpuvrf8_addr)
- );
-
- return l2cpuvrf8_addr;
-}
-
-static void l2cpuvrf8_fix_restore(u32 l2cpuvrf8_addr, u32 l2cpuvrf8_val)
-{
- mb();
- asm volatile ("mcr p15, 3, %[l2cpselr], c15, c0, 6\n\t"
- "isb\n\t"
- "mcr p15, 3, %[l2cpdr], c15, c0, 7\n\t"
- "isb\n\t"
- :
- : [l2cpselr]"r" (l2cpuvrf8_addr),
- [l2cpdr]"r" (l2cpuvrf8_val)
- );
-}
-
u32 set_get_l2_indirect_reg(u32 reg_addr, u32 val)
{
unsigned long flags;
- u32 uninitialized_var(l2cpuvrf8_val), l2cpuvrf8_addr = 0;
u32 ret_val;
raw_spin_lock_irqsave(&l2_access_lock, flags);
-
- if (l2cpuvrf8_needs_fix(reg_addr))
- l2cpuvrf8_addr = l2cpuvrf8_fix_save(reg_addr, &l2cpuvrf8_val);
-
mb();
asm volatile ("mcr p15, 3, %[l2cpselr], c15, c0, 6\n\t"
"isb\n\t"
@@ -96,10 +33,6 @@
: [l2cpdr_read]"=r" (ret_val)
: [l2cpselr]"r" (reg_addr), [l2cpdr]"r" (val)
);
-
- if (l2cpuvrf8_addr)
- l2cpuvrf8_fix_restore(l2cpuvrf8_addr, l2cpuvrf8_val);
-
raw_spin_unlock_irqrestore(&l2_access_lock, flags);
return ret_val;
@@ -109,13 +42,8 @@
void set_l2_indirect_reg(u32 reg_addr, u32 val)
{
unsigned long flags;
- u32 uninitialized_var(l2cpuvrf8_val), l2cpuvrf8_addr = 0;
raw_spin_lock_irqsave(&l2_access_lock, flags);
-
- if (l2cpuvrf8_needs_fix(reg_addr))
- l2cpuvrf8_addr = l2cpuvrf8_fix_save(reg_addr, &l2cpuvrf8_val);
-
mb();
asm volatile ("mcr p15, 3, %[l2cpselr], c15, c0, 6\n\t"
"isb\n\t"
@@ -124,10 +52,6 @@
:
: [l2cpselr]"r" (reg_addr), [l2cpdr]"r" (val)
);
-
- if (l2cpuvrf8_addr)
- l2cpuvrf8_fix_restore(l2cpuvrf8_addr, l2cpuvrf8_val);
-
raw_spin_unlock_irqrestore(&l2_access_lock, flags);
}
EXPORT_SYMBOL(set_l2_indirect_reg);
diff --git a/arch/arm/mach-msm/msm7k_fiq.c b/arch/arm/mach-msm/msm7k_fiq.c
new file mode 100644
index 0000000..421b4f9
--- /dev/null
+++ b/arch/arm/mach-msm/msm7k_fiq.c
@@ -0,0 +1,86 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <asm/fiq.h>
+#include <asm/hardware/gic.h>
+#include <asm/cacheflush.h>
+#include <mach/irqs-8625.h>
+#include <mach/socinfo.h>
+
+#include "msm_watchdog.h"
+
+#define MODULE_NAME "MSM7K_FIQ"
+
+struct msm_watchdog_dump msm_dump_cpu_ctx;
+static int fiq_counter;
+void *msm7k_fiq_stack;
+
+/* Called from the FIQ asm handler */
+void msm7k_fiq_handler(void)
+{
+ struct irq_data *d;
+ struct irq_chip *c;
+
+ pr_info("Fiq is received %s\n", __func__);
+ fiq_counter++;
+ d = irq_get_irq_data(MSM8625_INT_A9_M2A_2);
+ c = irq_data_get_irq_chip(d);
+ c->irq_mask(d);
+ local_irq_disable();
+
+ /* Clear the IRQ from the ENABLE_SET */
+ gic_clear_irq_pending(MSM8625_INT_A9_M2A_2);
+ local_irq_enable();
+ flush_cache_all();
+ outer_flush_all();
+ return;
+}
+
+struct fiq_handler msm7k_fh = {
+ .name = MODULE_NAME,
+};
+
+static int __init msm_setup_fiq_handler(void)
+{
+ int ret = 0;
+
+ claim_fiq(&msm7k_fh);
+ set_fiq_handler(&msm7k_fiq_start, msm7k_fiq_length);
+ msm7k_fiq_stack = (void *)__get_free_pages(GFP_KERNEL,
+ THREAD_SIZE_ORDER);
+ if (msm7k_fiq_stack == NULL) {
+ pr_err("FIQ STACK SETUP IS NOT SUCCESSFUL\n");
+ return -ENOMEM;
+ }
+
+ fiq_set_type(MSM8625_INT_A9_M2A_2, IRQF_TRIGGER_RISING);
+ gic_set_irq_secure(MSM8625_INT_A9_M2A_2);
+ enable_irq(MSM8625_INT_A9_M2A_2);
+ pr_info("%s : msm7k fiq setup--done\n", __func__);
+ return ret;
+}
+
+static int __init init7k_fiq(void)
+{
+ if (!cpu_is_msm8625())
+ return 0;
+
+ if (msm_setup_fiq_handler())
+ pr_err("MSM7K FIQ INIT FAILED\n");
+
+ return 0;
+}
+late_initcall(init7k_fiq);
diff --git a/arch/arm/mach-msm/msm7k_fiq_handler.S b/arch/arm/mach-msm/msm7k_fiq_handler.S
new file mode 100644
index 0000000..e2da9cf
--- /dev/null
+++ b/arch/arm/mach-msm/msm7k_fiq_handler.S
@@ -0,0 +1,94 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+
+#define VERSION_ID 0x1
+#define MAGIC 0xDEAD0000 | VERSION_ID
+ .text
+ .align 3
+
+ENTRY(msm7k_fiq_start)
+ sub r14, r14, #4 @return address
+ ldr r8, Lmsm_fiq_stack
+ ldr sp, [r8] @get stack
+ stmfa sp!, {r0-r7, lr}
+ stmfa sp!, {r8-r9}
+ ldr r8, Ldump_cpu_ctx
+ @ store magic to indicate a valid dump
+ ldr r9, Lmagic
+ str r9, [r8], #4
+ @ get the current cpsr
+ mrs r9, cpsr
+ str r9, [r8],#4
+ stmia r8!, {r0-r7} @ get the USR r0-r7
+ mov r4, r8
+ mov r5, #PSR_I_BIT | PSR_F_BIT | SYSTEM_MODE
+ msr cpsr_c, r5 @ select SYSTEM mode
+ stmia r4!, {r8-r14}
+ mov r5, #PSR_I_BIT | PSR_F_BIT | IRQ_MODE
+ msr cpsr_c, r5 @ select IRQ mode
+ mrs r5, spsr
+ str r5, [r4], #4
+ stmia r4!, {r13-r14}
+ mov r5, #PSR_I_BIT | PSR_F_BIT | SVC_MODE
+ msr cpsr_c, r5 @ select SVC mode
+ mrs r5, spsr
+ str r5, [r4], #4
+ stmia r4!, {r13-r14}
+ mov r0, r13
+ mov r1, r14
+ mov r5, #PSR_I_BIT | PSR_F_BIT | FIQ_MODE
+ msr cpsr_c, r5 @ select FIQ mode
+ stmfa sp!, {r0-r1}
+ mov r5, #PSR_I_BIT | PSR_F_BIT | ABT_MODE
+ msr cpsr_c, r5 @ select ABT mode
+ mrs r5, spsr
+ str r5, [r4], #4
+ stmia r4!, {r13-r14}
+ mov r5, #PSR_I_BIT | PSR_F_BIT | UND_MODE
+ msr cpsr_c, r5 @ select UND mode
+ mrs r5, spsr
+ str r5, [r4], #4
+ stmia r4!, {r13-r14}
+ mov r5, #PSR_I_BIT | PSR_F_BIT | FIQ_MODE
+ msr cpsr_c, r5 @ select FIQ mode
+ mrs r5, spsr
+ str r5, [r4], #4
+ stmia r4!, {r8-r14}
+ dsb
+ mov r5, #PSR_I_BIT | PSR_F_BIT | SVC_MODE
+ msr cpsr_c, r5 @ select SVC mode
+ ldr r2, Lmsm_fiq_handler
+ blx r2
+ mov r5, #PSR_I_BIT | PSR_F_BIT | FIQ_MODE
+ msr cpsr_c, r5 @ select FIQ mode
+ ldmfa sp!, {r0, r1}
+ mov r5, #PSR_I_BIT | PSR_F_BIT | SVC_MODE
+ msr cpsr_c, r5 @ select SVC mode
+ mov r13, r0
+ mov r14, r1
+ mov r5, #PSR_I_BIT | PSR_F_BIT | FIQ_MODE
+ msr cpsr_c, r5 @ select SVC mode
+ ldmfa sp!, {r8-r9}
+ ldmfa sp!, {r0-r7, pc}^
+Ldump_cpu_ctx:
+ .word msm_dump_cpu_ctx
+Lmsm_fiq_stack:
+ .word msm7k_fiq_stack
+Lmagic:
+ .word MAGIC
+Lmsm_fiq_handler:
+ .word msm7k_fiq_handler
+ENTRY(msm7k_fiq_length)
+ .word . - msm7k_fiq_start
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
index 2072cb1..97299a0 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
@@ -485,7 +485,7 @@
};
#define M_PRIOLVL_OVERRIDE_ADDR(b, n) \
- (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000220)
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000230)
enum bimc_m_priolvl_override {
M_PRIOLVL_OVERRIDE_RMSK = 0x301,
M_PRIOLVL_OVERRIDE_BMSK = 0x300,
@@ -495,10 +495,10 @@
};
#define M_RD_CMD_OVERRIDE_ADDR(b, n) \
- (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000230)
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000240)
enum bimc_m_read_command_override {
- M_RD_CMD_OVERRIDE_RMSK = 0x37f3f,
- M_RD_CMD_OVERRIDE_AREQPRIO_BMSK = 0x300000,
+ M_RD_CMD_OVERRIDE_RMSK = 0x3071f7f,
+ M_RD_CMD_OVERRIDE_AREQPRIO_BMSK = 0x3000000,
M_RD_CMD_OVERRIDE_AREQPRIO_SHFT = 0x18,
M_RD_CMD_OVERRIDE_AMEMTYPE_BMSK = 0x70000,
M_RD_CMD_OVERRIDE_AMEMTYPE_SHFT = 0x10,
@@ -529,13 +529,15 @@
};
#define M_WR_CMD_OVERRIDE_ADDR(b, n) \
- (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000240)
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000250)
enum bimc_m_write_command_override {
- M_WR_CMD_OVERRIDE_RMSK = 0x37f3f,
- M_WR_CMD_OVERRIDE_AREQPRIO_BMSK = 0x30000,
- M_WR_CMD_OVERRIDE_AREQPRIO_SHFT = 0x10,
- M_WR_CMD_OVERRIDE_AMEMTYPE_BMSK = 0x7000,
- M_WR_CMD_OVERRIDE_AMEMTYPE_SHFT = 0xc,
+ M_WR_CMD_OVERRIDE_RMSK = 0x3071f7f,
+ M_WR_CMD_OVERRIDE_AREQPRIO_BMSK = 0x3000000,
+ M_WR_CMD_OVERRIDE_AREQPRIO_SHFT = 0x18,
+ M_WR_CMD_OVERRIDE_AMEMTYPE_BMSK = 0x70000,
+ M_WR_CMD_OVERRIDE_AMEMTYPE_SHFT = 0x10,
+ M_WR_CMD_OVERRIDE_ATRANSIENT_BMSK = 0x1000,
+ M_WR_CMD_OVERRIDE_ATRANSIENT_SHFT = 0xc,
M_WR_CMD_OVERRIDE_ASHARED_BMSK = 0x800,
M_WR_CMD_OVERRIDE_ASHARED_SHFT = 0xb,
M_WR_CMD_OVERRIDE_AREDIRECT_BMSK = 0x400,
@@ -544,8 +546,10 @@
M_WR_CMD_OVERRIDE_AOOO_SHFT = 0x9,
M_WR_CMD_OVERRIDE_AINNERSHARED_BMSK = 0x100,
M_WR_CMD_OVERRIDE_AINNERSHARED_SHFT = 0x8,
- M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK = 0x20,
- M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_SHFT = 0x5,
+ M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK = 0x40,
+ M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_SHFT = 0x6,
+ M_WR_CMD_OVERRIDE_OVERRIDE_ATRANSIENT_BMSK = 0x20,
+ M_WR_CMD_OVERRIDE_OVERRIDE_ATRANSIENT_SHFT = 0x5,
M_WR_CMD_OVERRIDE_OVERRIDE_AMEMTYPE_BMSK = 0x10,
M_WR_CMD_OVERRIDE_OVERRIDE_AMEMTYPE_SHFT = 0x4,
M_WR_CMD_OVERRIDE_OVERRIDE_ASHARED_BMSK = 0x8,
@@ -1454,7 +1458,7 @@
* boundary in future
*/
wmb();
- set_qos_mode(binfo->base, mas_index, 1, 1, 1);
+ set_qos_mode(binfo->base, mas_index, 0, 1, 1);
break;
case BIMC_QOS_MODE_BYPASS:
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_board_8974.c b/arch/arm/mach-msm/msm_bus/msm_bus_board_8974.c
index 1b8c07e..cfd84eb 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_board_8974.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_board_8974.c
@@ -641,6 +641,7 @@
.mode = NOC_QOS_MODE_FIXED,
.qport = qports_crypto_c0,
.mas_hw_id = MAS_CRYPTO_CORE0,
+ .hw_sel = MSM_BUS_NOC,
},
{
.id = MSM_BUS_MASTER_CRYPTO_CORE1,
@@ -651,6 +652,7 @@
.mode = NOC_QOS_MODE_FIXED,
.qport = qports_crypto_c1,
.mas_hw_id = MAS_CRYPTO_CORE1,
+ .hw_sel = MSM_BUS_NOC,
},
{
.id = MSM_BUS_MASTER_LPASS_PROC,
@@ -719,6 +721,7 @@
.mas_hw_id = MAS_USB3,
.prio_rd = 2,
.prio_wr = 2,
+ .hw_sel = MSM_BUS_NOC,
},
{
.id = MSM_BUS_SLAVE_AMPSS,
@@ -1046,9 +1049,8 @@
.qport = qports_kmpss,
.ws = 10000,
.mas_hw_id = MAS_APPSS_PROC,
- .prio_lvl = 0,
- .prio_rd = 2,
- .prio_wr = 2,
+ .prio_rd = 1,
+ .prio_wr = 1,
},
{
.id = MSM_BUS_MASTER_AMPSS_M1,
@@ -1061,6 +1063,8 @@
.qport = qports_kmpss,
.ws = 10000,
.mas_hw_id = MAS_APPSS_PROC,
+ .prio_rd = 1,
+ .prio_wr = 1,
},
{
.id = MSM_BUS_MASTER_MSS_PROC,
diff --git a/arch/arm/mach-msm/msm_cpr.c b/arch/arm/mach-msm/msm_cpr.c
index af74f72..c00352d 100644
--- a/arch/arm/mach-msm/msm_cpr.c
+++ b/arch/arm/mach-msm/msm_cpr.c
@@ -525,6 +525,10 @@
*/
error_step -= 2;
+ /* Keep down step upto two per interrupt to avoid any spike */
+ if (error_step > 2)
+ error_step = 2;
+
/* Calculte new PMIC voltage */
new_volt = curr_volt - (error_step * cpr->vp->step_size);
msm_cpr_debug(MSM_CPR_DEBUG_STEPS,
@@ -642,6 +646,11 @@
cpr->config->delay_us);
cpr_write_reg(cpr, RBCPR_TIMER_INTERVAL, delay_count);
+ /* Use Consecutive Down to avoid any interrupt due to spike */
+ cpr_write_reg(cpr, RBIF_TIMER_ADJUST, (0x2 << RBIF_CONS_DN_SHIFT));
+ msm_cpr_debug(MSM_CPR_DEBUG_CONFIG, "RBIF_TIMER_ADJUST: 0x%x\n",
+ readl_relaxed(cpr->base + RBIF_TIMER_ADJUST));
+
/* Enable the Timer */
cpr_modify_reg(cpr, RBCPR_CTL, TIMER_M, ENABLE_TIMER);
@@ -743,6 +752,9 @@
"RBIF_IRQ_STATUS: 0x%x\n",
cpr_read_reg(cpr, RBIF_IRQ_STATUS));
+ /* Clear all the interrupts */
+ cpr_write_reg(cpr, RBIF_IRQ_CLEAR, ALL_CPR_IRQ);
+
cpr_enable(cpr);
break;
default:
@@ -776,6 +788,9 @@
cpr_write_reg(cpr, RBCPR_CTL,
cpr_save_state.rbcpr_ctl);
+ /* Clear all the interrupts */
+ cpr_write_reg(cpr, RBIF_IRQ_CLEAR, ALL_CPR_IRQ);
+
enable_irq(cpr->irq);
cpr_enable(cpr);
@@ -792,6 +807,9 @@
cpr_disable(cpr);
disable_irq(cpr->irq);
+ /* Clear all the interrupts */
+ cpr_write_reg(cpr, RBIF_IRQ_CLEAR, ALL_CPR_IRQ);
+
cpr_save_state.rbif_timer_interval =
cpr_read_reg(cpr, RBCPR_TIMER_INTERVAL);
cpr_save_state.rbif_int_en =
diff --git a/arch/arm/mach-msm/msm_cpr.h b/arch/arm/mach-msm/msm_cpr.h
index e690c63..005d9b1 100644
--- a/arch/arm/mach-msm/msm_cpr.h
+++ b/arch/arm/mach-msm/msm_cpr.h
@@ -72,6 +72,9 @@
#define SW_AUTO_CONT_ACK_EN BIT(5)
#define SW_AUTO_CONT_NACK_DN_EN BIT(6)
+/* Shift Values */
+#define RBIF_CONS_DN_SHIFT (0x4)
+
/* Test values for RBCPR RUMI Testing */
#define GNT_CNT 0xC0
#define TARGET 0xEFF
diff --git a/arch/arm/mach-msm/msm_dcvs.c b/arch/arm/mach-msm/msm_dcvs.c
index 0c158de..310197e 100644
--- a/arch/arm/mach-msm/msm_dcvs.c
+++ b/arch/arm/mach-msm/msm_dcvs.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -14,7 +14,6 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/kthread.h>
#include <linux/kobject.h>
#include <linux/ktime.h>
@@ -23,167 +22,285 @@
#include <linux/spinlock.h>
#include <linux/stringify.h>
#include <linux/debugfs.h>
+#include <linux/msm_tsens.h>
#include <asm/atomic.h>
#include <asm/page.h>
#include <mach/msm_dcvs.h>
+#include <trace/events/mpdcvs_trace.h>
#define CORE_HANDLE_OFFSET (0xA0)
#define __err(f, ...) pr_err("MSM_DCVS: %s: " f, __func__, __VA_ARGS__)
#define __info(f, ...) pr_info("MSM_DCVS: %s: " f, __func__, __VA_ARGS__)
#define MAX_PENDING (5)
-enum {
- MSM_DCVS_DEBUG_NOTIFIER = BIT(0),
- MSM_DCVS_DEBUG_IDLE_PULSE = BIT(1),
- MSM_DCVS_DEBUG_FREQ_CHANGE = BIT(2),
-};
-
struct core_attribs {
- struct kobj_attribute idle_enabled;
- struct kobj_attribute freq_change_enabled;
- struct kobj_attribute actual_freq;
struct kobj_attribute freq_change_us;
- struct kobj_attribute max_time_us;
-
- struct kobj_attribute slack_time_us;
- struct kobj_attribute scale_slack_time;
- struct kobj_attribute scale_slack_time_pct;
struct kobj_attribute disable_pc_threshold;
- struct kobj_attribute em_window_size;
+ struct kobj_attribute em_win_size_min_us;
+ struct kobj_attribute em_win_size_max_us;
struct kobj_attribute em_max_util_pct;
- struct kobj_attribute ss_window_size;
- struct kobj_attribute ss_util_pct;
+ struct kobj_attribute group_id;
+ struct kobj_attribute max_freq_chg_time_us;
+ struct kobj_attribute slack_mode_dynamic;
+ struct kobj_attribute slack_time_min_us;
+ struct kobj_attribute slack_time_max_us;
+ struct kobj_attribute slack_weight_thresh_pct;
struct kobj_attribute ss_iobusy_conv;
+ struct kobj_attribute ss_win_size_min_us;
+ struct kobj_attribute ss_win_size_max_us;
+ struct kobj_attribute ss_util_pct;
+
+ struct kobj_attribute active_coeff_a;
+ struct kobj_attribute active_coeff_b;
+ struct kobj_attribute active_coeff_c;
+ struct kobj_attribute leakage_coeff_a;
+ struct kobj_attribute leakage_coeff_b;
+ struct kobj_attribute leakage_coeff_c;
+ struct kobj_attribute leakage_coeff_d;
+
+ struct kobj_attribute thermal_poll_ms;
struct attribute_group attrib_group;
};
+enum pending_freq_state {
+ /*
+ * used by the thread to check if pending_freq was updated while it was
+ * setting previous frequency - this is written to and used by the
+ * freq updating thread
+ */
+ NO_OUTSTANDING_FREQ_CHANGE = 0,
+
+ /*
+ * This request is set to indicate that the governor is stopped and no
+ * more frequency change requests are accepted untill it starts again.
+ * This is checked/used by the threads that want to change the freq
+ */
+ STOP_FREQ_CHANGE = -1,
+
+ /*
+ * Any other +ve value means that a freq change was requested and the
+ * thread has not gotten around to update it
+ *
+ * Any other -ve value means that this is the last freq change i.e. a
+ * freq change was requested but the thread has not run yet and
+ * meanwhile the governor was stopped.
+ */
+};
+
struct dcvs_core {
+ spinlock_t idle_state_change_lock;
+ /* 0 when not idle (busy) 1 when idle and -1 when governor starts and
+ * we dont know whether the next call is going to be idle enter or exit
+ */
+ int idle_entered;
+
+ enum msm_dcvs_core_type type;
+ /* this is the number in each type for example cpu 0,1,2 and gpu 0,1 */
+ int type_core_num;
char core_name[CORE_NAME_MAX];
- uint32_t new_freq[MAX_PENDING];
uint32_t actual_freq;
uint32_t freq_change_us;
uint32_t max_time_us; /* core param */
struct msm_dcvs_algo_param algo_param;
- struct msm_dcvs_idle *idle_driver;
- struct msm_dcvs_freq *freq_driver;
+ struct msm_dcvs_energy_curve_coeffs coeffs;
/* private */
- int64_t time_start;
- struct mutex lock;
- spinlock_t cpu_lock;
+ ktime_t time_start;
struct task_struct *task;
struct core_attribs attrib;
- uint32_t handle;
- uint32_t group_id;
- uint32_t freq_pending;
- struct hrtimer timer;
- int32_t timer_disabled;
- /* track if kthread for change_freq is active */
- int32_t change_freq_activated;
+ uint32_t dcvs_core_id;
+ struct msm_dcvs_core_info *info;
+ int sensor;
+ wait_queue_head_t wait_q;
+
+ int (*set_frequency)(int type_core_num, unsigned int freq);
+ unsigned int (*get_frequency)(int type_core_num);
+ int (*idle_enable)(int type_core_num,
+ enum msm_core_control_event event);
+
+ spinlock_t pending_freq_lock;
+ int pending_freq;
+
+ struct hrtimer slack_timer;
+ struct delayed_work temperature_work;
};
-static int msm_dcvs_debug;
static int msm_dcvs_enabled = 1;
module_param_named(enable, msm_dcvs_enabled, int, S_IRUGO | S_IWUSR | S_IWGRP);
-static struct dentry *debugfs_base;
+static struct dentry *debugfs_base;
static struct dcvs_core core_list[CORES_MAX];
-static DEFINE_MUTEX(core_list_lock);
static struct kobject *cores_kobj;
-static struct dcvs_core *core_handles[CORES_MAX];
-/* Change core frequency, called with core mutex locked */
+static void force_stop_slack_timer(struct dcvs_core *core)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&core->idle_state_change_lock, flags);
+ hrtimer_cancel(&core->slack_timer);
+ spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
+}
+
+static void force_start_slack_timer(struct dcvs_core *core, int slack_us)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&core->idle_state_change_lock, flags);
+
+ /*
+ * only start the timer if governor is not stopped
+ */
+ if (slack_us != 0) {
+ ret = hrtimer_start(&core->slack_timer,
+ ktime_set(0, slack_us * 1000),
+ HRTIMER_MODE_REL_PINNED);
+ if (ret) {
+ pr_err("%s Failed to start timer ret = %d\n",
+ core->core_name, ret);
+ }
+ }
+
+ spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
+}
+
+static void stop_slack_timer(struct dcvs_core *core)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&core->idle_state_change_lock, flags);
+ /* err only for cpu type's GPU's can do idle exit consecutively */
+ if (core->idle_entered == 1 && !(core->dcvs_core_id >= GPU_OFFSET))
+ __err("%s trying to reenter idle", core->core_name);
+ core->idle_entered = 1;
+ hrtimer_cancel(&core->slack_timer);
+ core->idle_entered = 1;
+ spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
+}
+
+static void start_slack_timer(struct dcvs_core *core, int slack_us)
+{
+ unsigned long flags1, flags2;
+ int ret;
+
+ spin_lock_irqsave(&core->idle_state_change_lock, flags2);
+
+ spin_lock_irqsave(&core->pending_freq_lock, flags1);
+
+ /* err only for cpu type's GPU's can do idle enter consecutively */
+ if (core->idle_entered == 0 && !(core->dcvs_core_id >= GPU_OFFSET))
+ __err("%s trying to reexit idle", core->core_name);
+ core->idle_entered = 0;
+ /*
+ * only start the timer if governor is not stopped
+ */
+ if (slack_us != 0
+ && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
+ ret = hrtimer_start(&core->slack_timer,
+ ktime_set(0, slack_us * 1000),
+ HRTIMER_MODE_REL_PINNED);
+ if (ret) {
+ pr_err("%s Failed to start timer ret = %d\n",
+ core->core_name, ret);
+ }
+ }
+ spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
+
+ spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
+}
+
+static void restart_slack_timer(struct dcvs_core *core, int slack_us)
+{
+ unsigned long flags1, flags2;
+ int ret;
+
+ spin_lock_irqsave(&core->idle_state_change_lock, flags2);
+
+ hrtimer_cancel(&core->slack_timer);
+
+ spin_lock_irqsave(&core->pending_freq_lock, flags1);
+
+ /*
+ * only start the timer if idle is not entered
+ * and governor is not stopped
+ */
+ if (slack_us != 0 && (core->idle_entered != 1)
+ && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
+ ret = hrtimer_start(&core->slack_timer,
+ ktime_set(0, slack_us * 1000),
+ HRTIMER_MODE_REL_PINNED);
+ if (ret) {
+ pr_err("%s Failed to start timer ret = %d\n",
+ core->core_name, ret);
+ }
+ }
+ spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
+ spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
+}
+
static int __msm_dcvs_change_freq(struct dcvs_core *core)
{
int ret = 0;
unsigned long flags = 0;
- unsigned int requested_freq = 0;
- unsigned int prev_freq = 0;
- int64_t time_start = 0;
- int64_t time_end = 0;
+ int requested_freq = 0;
+ ktime_t time_start;
uint32_t slack_us = 0;
uint32_t ret1 = 0;
- if (!core->freq_driver || !core->freq_driver->set_frequency) {
- /* Core may have unregistered or hotplugged */
- return -ENODEV;
- }
+ spin_lock_irqsave(&core->pending_freq_lock, flags);
repeat:
- spin_lock_irqsave(&core->cpu_lock, flags);
- if (unlikely(!core->freq_pending)) {
- spin_unlock_irqrestore(&core->cpu_lock, flags);
- return ret;
- }
- requested_freq = core->new_freq[core->freq_pending - 1];
- if (unlikely(core->freq_pending > 1) &&
- (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)) {
- int i;
- for (i = 0; i < core->freq_pending - 1; i++) {
- __info("Core %s missing freq %u\n",
- core->core_name, core->new_freq[i]);
- }
- }
+ BUG_ON(!core->pending_freq);
+ if (core->pending_freq == STOP_FREQ_CHANGE)
+ BUG();
+
+ requested_freq = core->pending_freq;
time_start = core->time_start;
- core->time_start = 0;
- core->freq_pending = 0;
- /**
- * Cancel the timers, we dont want the timer firing as we are
- * changing the clock rate. Dont let idle_exit and others setup
- * timers as well.
- */
- hrtimer_cancel(&core->timer);
- core->timer_disabled = 1;
- spin_unlock_irqrestore(&core->cpu_lock, flags);
+ core->time_start = ns_to_ktime(0);
+
+ if (requested_freq < 0) {
+ requested_freq = -1 * requested_freq;
+ core->pending_freq = STOP_FREQ_CHANGE;
+ } else {
+ core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
+ }
if (requested_freq == core->actual_freq)
- return ret;
+ goto out;
+
+ spin_unlock_irqrestore(&core->pending_freq_lock, flags);
/**
* Call the frequency sink driver to change the frequency
* We will need to get back the actual frequency in KHz and
* the record the time taken to change it.
*/
- ret = core->freq_driver->set_frequency(core->freq_driver,
- requested_freq);
- if (ret <= 0) {
+ ret = core->set_frequency(core->type_core_num, requested_freq);
+ if (ret <= 0)
__err("Core %s failed to set freq %u\n",
core->core_name, requested_freq);
/* continue to call TZ to get updated slack timer */
- } else {
- prev_freq = core->actual_freq;
+ else
core->actual_freq = ret;
- }
- time_end = ktime_to_ns(ktime_get());
- if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
- __info("Core %s Time end %llu Time start: %llu\n",
- core->core_name, time_end, time_start);
- time_end -= time_start;
- do_div(time_end, NSEC_PER_USEC);
- core->freq_change_us = (uint32_t)time_end;
+ core->freq_change_us = (uint32_t)ktime_to_us(
+ ktime_sub(ktime_get(), time_start));
/**
* Disable low power modes if the actual frequency is >
* disable_pc_threshold.
*/
- if (core->actual_freq >
- core->algo_param.disable_pc_threshold) {
- core->idle_driver->enable(core->idle_driver,
+ if (core->actual_freq > core->algo_param.disable_pc_threshold) {
+ core->idle_enable(core->type_core_num,
MSM_DCVS_DISABLE_HIGH_LATENCY_MODES);
- if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
- __info("Disabling LPM for %s\n", core->core_name);
- } else if (core->actual_freq <=
- core->algo_param.disable_pc_threshold) {
- core->idle_driver->enable(core->idle_driver,
+ } else if (core->actual_freq <= core->algo_param.disable_pc_threshold) {
+ core->idle_enable(core->type_core_num,
MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
- if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
- __info("Enabling LPM for %s\n", core->core_name);
}
/**
@@ -191,108 +308,165 @@
* to this frequency and that will get us the new slack
* timer
*/
- ret = msm_dcvs_scm_event(core->handle, MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
- core->actual_freq, (uint32_t)time_end, &slack_us, &ret1);
- if (!ret) {
- /* Reset the slack timer */
- if (slack_us) {
- core->timer_disabled = 0;
- ret = hrtimer_start(&core->timer,
- ktime_set(0, slack_us * 1000),
- HRTIMER_MODE_REL_PINNED);
- if (ret)
- __err("Failed to register timer for core %s\n",
- core->core_name);
- }
- } else {
- __err("Error sending core (%s) freq change (%u)\n",
- core->core_name, core->actual_freq);
+ ret = msm_dcvs_scm_event(core->dcvs_core_id,
+ MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
+ core->actual_freq, core->freq_change_us,
+ &slack_us, &ret1);
+ if (ret) {
+ __err("Error sending core (%s) dcvs_core_id = %d freq change (%u) reqfreq = %d slack_us=%d ret = %d\n",
+ core->core_name, core->dcvs_core_id,
+ core->actual_freq, requested_freq,
+ slack_us, ret);
}
- if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
- __info("Freq %u requested for core %s (actual %u prev %u) "
- "change time %u us slack time %u us\n",
- requested_freq, core->core_name,
- core->actual_freq, prev_freq,
- core->freq_change_us, slack_us);
+ /* TODO confirm that we get a valid freq from SM even when the above
+ * FREQ_UPDATE fails
+ */
+ restart_slack_timer(core, slack_us);
+ spin_lock_irqsave(&core->pending_freq_lock, flags);
/**
* By the time we are done with freq changes, we could be asked to
* change again. Check before exiting.
*/
- if (core->freq_pending)
+ if (core->pending_freq != NO_OUTSTANDING_FREQ_CHANGE
+ && core->pending_freq != STOP_FREQ_CHANGE) {
goto repeat;
+ }
- core->change_freq_activated = 0;
+out: /* should always be jumped to with the spin_lock held */
+ spin_unlock_irqrestore(&core->pending_freq_lock, flags);
+
return ret;
}
+static void msm_dcvs_report_temp_work(struct work_struct *work)
+{
+ struct dcvs_core *core = container_of(work,
+ struct dcvs_core,
+ temperature_work.work);
+ struct msm_dcvs_core_info *info = core->info;
+ struct tsens_device tsens_dev;
+ int ret;
+ unsigned long temp = 0;
+ int interval_ms;
+
+ tsens_dev.sensor_num = core->sensor;
+ ret = tsens_get_temp(&tsens_dev, &temp);
+ if (!temp) {
+ tsens_dev.sensor_num = 0;
+ ret = tsens_get_temp(&tsens_dev, &temp);
+ if (!temp)
+ goto out;
+ }
+
+ if (temp == info->power_param.current_temp)
+ goto out;
+ info->power_param.current_temp = temp;
+
+ ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
+ &info->power_param,
+ &info->freq_tbl[0], &core->coeffs);
+out:
+ if (info->thermal_poll_ms == 0)
+ interval_ms = 60000;
+ else if (info->thermal_poll_ms < 1000)
+ interval_ms = 1000;
+ else
+ interval_ms = info->thermal_poll_ms;
+
+ schedule_delayed_work(&core->temperature_work,
+ msecs_to_jiffies(interval_ms));
+}
+
static int msm_dcvs_do_freq(void *data)
{
struct dcvs_core *core = (struct dcvs_core *)data;
static struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
sched_setscheduler(current, SCHED_FIFO, ¶m);
- set_current_state(TASK_UNINTERRUPTIBLE);
while (!kthread_should_stop()) {
- mutex_lock(&core->lock);
- __msm_dcvs_change_freq(core);
- mutex_unlock(&core->lock);
-
- schedule();
+ wait_event(core->wait_q, !(core->pending_freq == 0 ||
+ core->pending_freq == -1) ||
+ kthread_should_stop());
if (kthread_should_stop())
break;
- set_current_state(TASK_UNINTERRUPTIBLE);
+ __msm_dcvs_change_freq(core);
}
- __set_current_state(TASK_RUNNING);
-
return 0;
}
+/* freq_pending_lock should be held */
+static void request_freq_change(struct dcvs_core *core, int new_freq)
+{
+ if (new_freq == NO_OUTSTANDING_FREQ_CHANGE) {
+ if (core->pending_freq != STOP_FREQ_CHANGE) {
+ __err("%s gov started with earlier pending freq %d\n",
+ core->core_name, core->pending_freq);
+ }
+ core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
+ return;
+ }
+
+ if (new_freq == STOP_FREQ_CHANGE) {
+ if (core->pending_freq == NO_OUTSTANDING_FREQ_CHANGE)
+ core->pending_freq = STOP_FREQ_CHANGE;
+ else if (core->pending_freq > 0)
+ core->pending_freq = -1 * core->pending_freq;
+ return;
+ }
+
+ if (core->pending_freq < 0) {
+ /* a value less than 0 means that the governor has stopped
+ * and no more freq changes should be requested
+ */
+ return;
+ }
+
+ if (core->actual_freq != new_freq && core->pending_freq != new_freq) {
+ core->pending_freq = new_freq;
+ core->time_start = ktime_get();
+ wake_up(&core->wait_q);
+ }
+}
+
static int msm_dcvs_update_freq(struct dcvs_core *core,
enum msm_dcvs_scm_event event, uint32_t param0,
- uint32_t *ret1, int *freq_changed)
+ uint32_t *ret1)
{
int ret = 0;
unsigned long flags = 0;
- uint32_t new_freq = 0;
+ uint32_t new_freq = -EINVAL;
- spin_lock_irqsave(&core->cpu_lock, flags);
- ret = msm_dcvs_scm_event(core->handle, event, param0,
+ spin_lock_irqsave(&core->pending_freq_lock, flags);
+
+ ret = msm_dcvs_scm_event(core->dcvs_core_id, event, param0,
core->actual_freq, &new_freq, ret1);
if (ret) {
- __err("Error (%d) sending SCM event %d for core %s\n",
+ if (ret == -13)
+ ret = 0;
+ else
+ __err("Error (%d) sending SCM event %d for core %s\n",
ret, event, core->core_name);
- goto freq_done;
+ goto out;
}
- if ((core->actual_freq != new_freq) &&
- (core->new_freq[core->freq_pending] != new_freq)) {
- if (core->freq_pending >= MAX_PENDING - 1)
- core->freq_pending = MAX_PENDING - 1;
- core->new_freq[core->freq_pending++] = new_freq;
- core->time_start = ktime_to_ns(ktime_get());
-
- /* Schedule the frequency change */
- if (!core->task)
- __err("Uninitialized task for core %s\n",
- core->core_name);
- else {
- if (freq_changed)
- *freq_changed = 1;
- core->change_freq_activated = 1;
- wake_up_process(core->task);
- }
- } else {
- if (freq_changed)
- *freq_changed = 0;
+ if (new_freq == 0) {
+ /*
+ * sometimes TZ gives us a 0 freq back,
+ * do not queue up a request
+ */
+ goto out;
}
-freq_done:
- spin_unlock_irqrestore(&core->cpu_lock, flags);
+
+ request_freq_change(core, new_freq);
+
+out:
+ spin_unlock_irqrestore(&core->pending_freq_lock, flags);
return ret;
}
@@ -300,19 +474,17 @@
static enum hrtimer_restart msm_dcvs_core_slack_timer(struct hrtimer *timer)
{
int ret = 0;
- struct dcvs_core *core = container_of(timer, struct dcvs_core, timer);
+ struct dcvs_core *core = container_of(timer,
+ struct dcvs_core, slack_timer);
uint32_t ret1;
- uint32_t ret2;
- if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
- __info("Slack timer fired for core %s\n", core->core_name);
-
+ trace_printk("dcvs: Slack timer fired for core=%s\n", core->core_name);
/**
* Timer expired, notify TZ
* Dont care about the third arg.
*/
ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0,
- &ret1, &ret2);
+ &ret1);
if (ret)
__err("Timer expired for core %s but failed to notify.\n",
core->core_name);
@@ -333,6 +505,28 @@
return snprintf(buf, PAGE_SIZE, "%d\n", v); \
}
+#define DCVS_PARAM_STORE(_name) \
+static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
+ struct kobj_attribute *attr, char *buf) \
+{ \
+ struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
+ return snprintf(buf, PAGE_SIZE, "%d\n", core->info->_name); \
+} \
+static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
+ struct kobj_attribute *attr, const char *buf, size_t count) \
+{ \
+ int ret = 0; \
+ uint32_t val = 0; \
+ struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
+ ret = kstrtouint(buf, 10, &val); \
+ if (ret) { \
+ __err("Invalid input %s for %s\n", buf, __stringify(_name));\
+ } else { \
+ core->info->_name = val; \
+ } \
+ return count; \
+}
+
#define DCVS_ALGO_PARAM(_name) \
static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
struct kobj_attribute *attr, char *buf) \
@@ -346,14 +540,13 @@
int ret = 0; \
uint32_t val = 0; \
struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
- mutex_lock(&core->lock); \
ret = kstrtouint(buf, 10, &val); \
if (ret) { \
__err("Invalid input %s for %s\n", buf, __stringify(_name));\
} else { \
uint32_t old_val = core->algo_param._name; \
core->algo_param._name = val; \
- ret = msm_dcvs_scm_set_algo_params(core->handle, \
+ ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id, \
&core->algo_param); \
if (ret) { \
core->algo_param._name = old_val; \
@@ -361,7 +554,37 @@
ret, val, __stringify(_name)); \
} \
} \
- mutex_unlock(&core->lock); \
+ return count; \
+}
+
+#define DCVS_ENERGY_PARAM(_name) \
+static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
+ struct kobj_attribute *attr, char *buf) \
+{ \
+ struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
+ return snprintf(buf, PAGE_SIZE, "%d\n", core->coeffs._name); \
+} \
+static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
+ struct kobj_attribute *attr, const char *buf, size_t count) \
+{ \
+ int ret = 0; \
+ int32_t val = 0; \
+ struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
+ ret = kstrtoint(buf, 10, &val); \
+ if (ret) { \
+ __err("Invalid input %s for %s\n", buf, __stringify(_name));\
+ } else { \
+ int32_t old_val = core->coeffs._name; \
+ core->coeffs._name = val; \
+ ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id, \
+ &core->info->power_param, &core->info->freq_tbl[0], \
+ &core->coeffs); \
+ if (ret) { \
+ core->coeffs._name = old_val; \
+ __err("Error(%d) in setting %d for coeffs param %s\n",\
+ ret, val, __stringify(_name)); \
+ } \
+ } \
return count; \
}
@@ -383,27 +606,38 @@
* Function declarations for different attributes.
* Gets used when setting the attribute show and store parameters.
*/
-DCVS_PARAM_SHOW(idle_enabled, (core->idle_driver != NULL))
-DCVS_PARAM_SHOW(freq_change_enabled, (core->freq_driver != NULL))
-DCVS_PARAM_SHOW(actual_freq, (core->actual_freq))
DCVS_PARAM_SHOW(freq_change_us, (core->freq_change_us))
-DCVS_PARAM_SHOW(max_time_us, (core->max_time_us))
-DCVS_ALGO_PARAM(slack_time_us)
-DCVS_ALGO_PARAM(scale_slack_time)
-DCVS_ALGO_PARAM(scale_slack_time_pct)
DCVS_ALGO_PARAM(disable_pc_threshold)
-DCVS_ALGO_PARAM(em_window_size)
+DCVS_ALGO_PARAM(em_win_size_min_us)
+DCVS_ALGO_PARAM(em_win_size_max_us)
DCVS_ALGO_PARAM(em_max_util_pct)
-DCVS_ALGO_PARAM(ss_window_size)
-DCVS_ALGO_PARAM(ss_util_pct)
+DCVS_ALGO_PARAM(group_id)
+DCVS_ALGO_PARAM(max_freq_chg_time_us)
+DCVS_ALGO_PARAM(slack_mode_dynamic)
+DCVS_ALGO_PARAM(slack_time_min_us)
+DCVS_ALGO_PARAM(slack_time_max_us)
+DCVS_ALGO_PARAM(slack_weight_thresh_pct)
DCVS_ALGO_PARAM(ss_iobusy_conv)
+DCVS_ALGO_PARAM(ss_win_size_min_us)
+DCVS_ALGO_PARAM(ss_win_size_max_us)
+DCVS_ALGO_PARAM(ss_util_pct)
+
+DCVS_ENERGY_PARAM(active_coeff_a)
+DCVS_ENERGY_PARAM(active_coeff_b)
+DCVS_ENERGY_PARAM(active_coeff_c)
+DCVS_ENERGY_PARAM(leakage_coeff_a)
+DCVS_ENERGY_PARAM(leakage_coeff_b)
+DCVS_ENERGY_PARAM(leakage_coeff_c)
+DCVS_ENERGY_PARAM(leakage_coeff_d)
+
+DCVS_PARAM_STORE(thermal_poll_ms)
static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
{
int ret = 0;
struct kobject *core_kobj = NULL;
- const int attr_count = 15;
+ const int attr_count = 24;
BUG_ON(!cores_kobj);
@@ -415,23 +649,33 @@
goto done;
}
- DCVS_RO_ATTRIB(0, idle_enabled);
- DCVS_RO_ATTRIB(1, freq_change_enabled);
- DCVS_RO_ATTRIB(2, actual_freq);
- DCVS_RO_ATTRIB(3, freq_change_us);
- DCVS_RO_ATTRIB(4, max_time_us);
+ DCVS_RO_ATTRIB(0, freq_change_us);
- DCVS_RW_ATTRIB(5, slack_time_us);
- DCVS_RW_ATTRIB(6, scale_slack_time);
- DCVS_RW_ATTRIB(7, scale_slack_time_pct);
- DCVS_RW_ATTRIB(8, disable_pc_threshold);
- DCVS_RW_ATTRIB(9, em_window_size);
- DCVS_RW_ATTRIB(10, em_max_util_pct);
- DCVS_RW_ATTRIB(11, ss_window_size);
- DCVS_RW_ATTRIB(12, ss_util_pct);
- DCVS_RW_ATTRIB(13, ss_iobusy_conv);
+ DCVS_RW_ATTRIB(1, disable_pc_threshold);
+ DCVS_RW_ATTRIB(2, em_win_size_min_us);
+ DCVS_RW_ATTRIB(3, em_win_size_max_us);
+ DCVS_RW_ATTRIB(4, em_max_util_pct);
+ DCVS_RW_ATTRIB(5, group_id);
+ DCVS_RW_ATTRIB(6, max_freq_chg_time_us);
+ DCVS_RW_ATTRIB(7, slack_mode_dynamic);
+ DCVS_RW_ATTRIB(8, slack_weight_thresh_pct);
+ DCVS_RW_ATTRIB(9, slack_time_min_us);
+ DCVS_RW_ATTRIB(10, slack_time_max_us);
+ DCVS_RW_ATTRIB(11, ss_iobusy_conv);
+ DCVS_RW_ATTRIB(12, ss_win_size_min_us);
+ DCVS_RW_ATTRIB(13, ss_win_size_max_us);
+ DCVS_RW_ATTRIB(14, ss_util_pct);
- core->attrib.attrib_group.attrs[14] = NULL;
+ DCVS_RW_ATTRIB(15, active_coeff_a);
+ DCVS_RW_ATTRIB(16, active_coeff_b);
+ DCVS_RW_ATTRIB(17, active_coeff_c);
+ DCVS_RW_ATTRIB(18, leakage_coeff_a);
+ DCVS_RW_ATTRIB(19, leakage_coeff_b);
+ DCVS_RW_ATTRIB(20, leakage_coeff_c);
+ DCVS_RW_ATTRIB(21, leakage_coeff_d);
+ DCVS_RW_ATTRIB(22, thermal_poll_ms);
+
+ core->attrib.attrib_group.attrs[23] = NULL;
core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
if (!core_kobj) {
@@ -442,8 +686,6 @@
ret = sysfs_create_group(core_kobj, &core->attrib.attrib_group);
if (ret)
__err("Cannot create core %s attr group\n", core->core_name);
- else if (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER)
- __info("Setting up attributes for core %s\n", core->core_name);
done:
if (ret) {
@@ -454,273 +696,301 @@
return ret;
}
-/* Return the core if found or add to list if @add_to_list is true */
-static struct dcvs_core *msm_dcvs_get_core(const char *name, int add_to_list)
+static int get_core_offset(enum msm_dcvs_core_type type, int num)
+{
+ int offset = -EINVAL;
+
+ switch (type) {
+ case MSM_DCVS_CORE_TYPE_CPU:
+ offset = CPU_OFFSET + num;
+ BUG_ON(offset >= GPU_OFFSET);
+ break;
+ case MSM_DCVS_CORE_TYPE_GPU:
+ offset = GPU_OFFSET + num;
+ BUG_ON(offset >= CORES_MAX);
+ break;
+ default:
+ BUG();
+ }
+
+ return offset;
+}
+
+/* Return the core and initialize non platform data specific numbers in it */
+static struct dcvs_core *msm_dcvs_add_core(enum msm_dcvs_core_type type,
+ int num)
{
struct dcvs_core *core = NULL;
int i;
- int empty = -1;
+ char name[CORE_NAME_MAX];
- if (!name[0] ||
- (strnlen(name, CORE_NAME_MAX - 1) == CORE_NAME_MAX - 1))
- return core;
-
- mutex_lock(&core_list_lock);
- for (i = 0; i < CORES_MAX; i++) {
- core = &core_list[i];
- if ((empty < 0) && !core->core_name[0]) {
- empty = i;
- continue;
- }
- if (!strncmp(name, core->core_name, CORE_NAME_MAX))
- break;
- }
-
- /* Check for core_list full */
- if ((i == CORES_MAX) && (empty < 0)) {
- mutex_unlock(&core_list_lock);
+ i = get_core_offset(type, num);
+ if (i < 0)
return NULL;
- }
- if (i == CORES_MAX && add_to_list) {
- core = &core_list[empty];
- strlcpy(core->core_name, name, CORE_NAME_MAX);
- mutex_init(&core->lock);
- spin_lock_init(&core->cpu_lock);
- core->handle = empty + CORE_HANDLE_OFFSET;
- hrtimer_init(&core->timer,
- CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
- core->timer.function = msm_dcvs_core_slack_timer;
- }
- mutex_unlock(&core_list_lock);
+ if (type == MSM_DCVS_CORE_TYPE_CPU)
+ snprintf(name, CORE_NAME_MAX, "cpu%d", num);
+ else
+ snprintf(name, CORE_NAME_MAX, "gpu%d", num);
+ core = &core_list[i];
+ core->dcvs_core_id = i;
+ strlcpy(core->core_name, name, CORE_NAME_MAX);
+ spin_lock_init(&core->pending_freq_lock);
+ spin_lock_init(&core->idle_state_change_lock);
+ hrtimer_init(&core->slack_timer,
+ CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
+ core->slack_timer.function = msm_dcvs_core_slack_timer;
return core;
}
-int msm_dcvs_register_core(const char *core_name, uint32_t group_id,
- struct msm_dcvs_core_info *info)
+/* Return the core if found or add to list if @add_to_list is true */
+static struct dcvs_core *msm_dcvs_get_core(int offset)
+{
+ /* if the handle is still not set bug */
+ BUG_ON(core_list[offset].dcvs_core_id == -1);
+ return &core_list[offset];
+}
+
+
+int msm_dcvs_register_core(
+ enum msm_dcvs_core_type type,
+ int type_core_num,
+ struct msm_dcvs_core_info *info,
+ int (*set_frequency)(int type_core_num, unsigned int freq),
+ unsigned int (*get_frequency)(int type_core_num),
+ int (*idle_enable)(int type_core_num,
+ enum msm_core_control_event event),
+ int sensor)
{
int ret = -EINVAL;
+ int offset;
struct dcvs_core *core = NULL;
+ uint32_t ret1;
+ uint32_t ret2;
- if (!core_name || !core_name[0])
+ offset = get_core_offset(type, type_core_num);
+ if (offset < 0)
return ret;
+ if (core_list[offset].dcvs_core_id != -1)
+ return core_list[offset].dcvs_core_id;
- core = msm_dcvs_get_core(core_name, true);
+ core = msm_dcvs_add_core(type, type_core_num);
if (!core)
return ret;
- mutex_lock(&core->lock);
- if (group_id) {
- /**
- * Create a group for cores, if this core is part of a group
- * if the group_id is 0, the core is not part of a group.
- * If the group_id already exits, it will through an error
- * which we will ignore.
- */
- ret = msm_dcvs_scm_create_group(group_id);
- if (ret == -ENOMEM)
- goto bail;
- }
- core->group_id = group_id;
+ core->type = type;
+ core->type_core_num = type_core_num;
+ core->set_frequency = set_frequency;
+ core->get_frequency = get_frequency;
+ core->idle_enable = idle_enable;
+ core->pending_freq = STOP_FREQ_CHANGE;
- core->max_time_us = info->core_param.max_time_us;
+ core->info = info;
memcpy(&core->algo_param, &info->algo_param,
sizeof(struct msm_dcvs_algo_param));
- ret = msm_dcvs_scm_register_core(core->handle, group_id,
- &info->core_param, info->freq_tbl);
- if (ret)
- goto bail;
+ memcpy(&core->coeffs, &info->energy_coeffs,
+ sizeof(struct msm_dcvs_energy_curve_coeffs));
- ret = msm_dcvs_scm_set_algo_params(core->handle, &info->algo_param);
+ /*
+ * The tz expects cpu0 to represent bit 0 in the mask, however the
+ * dcvs_core_id needs to start from 1, dcvs_core_id = 0 is used to
+ * indicate that this request is not associated with any core.
+ * mpdecision
+ */
+ info->core_param.core_bitmask_id
+ = 1 << (core->dcvs_core_id - CPU_OFFSET);
+ core->sensor = sensor;
+
+ ret = msm_dcvs_scm_register_core(core->dcvs_core_id, &info->core_param);
+ if (ret) {
+ __err("%s: scm register core fail handle = %d ret = %d\n",
+ __func__, core->dcvs_core_id, ret);
+ goto bail;
+ }
+
+ ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id,
+ &info->algo_param);
+ if (ret) {
+ __err("%s: scm algo params failed ret = %d\n", __func__, ret);
+ goto bail;
+ }
+
+ ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
+ &info->power_param,
+ &info->freq_tbl[0], &core->coeffs);
+ if (ret) {
+ __err("%s: scm power params failed ret = %d\n", __func__, ret);
+ goto bail;
+ }
+
+ ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE,
+ core->actual_freq, 0, &ret1, &ret2);
if (ret)
goto bail;
ret = msm_dcvs_setup_core_sysfs(core);
if (ret) {
__err("Unable to setup core %s sysfs\n", core->core_name);
- core_handles[core->handle - CORE_HANDLE_OFFSET] = NULL;
goto bail;
}
+ core->idle_entered = -1;
+ init_waitqueue_head(&core->wait_q);
+ core->task = kthread_run(msm_dcvs_do_freq, (void *)core,
+ "msm_dcvs/%d", core->dcvs_core_id);
+ ret = core->dcvs_core_id;
-bail:
- mutex_unlock(&core->lock);
+ INIT_DELAYED_WORK(&core->temperature_work, msm_dcvs_report_temp_work);
+ schedule_delayed_work(&core->temperature_work,
+ msecs_to_jiffies(info->thermal_poll_ms));
return ret;
+bail:
+ core->dcvs_core_id = -1;
+ return -EINVAL;
}
EXPORT_SYMBOL(msm_dcvs_register_core);
-int msm_dcvs_freq_sink_register(struct msm_dcvs_freq *drv)
+void msm_dcvs_update_limits(int dcvs_core_id)
+{
+ struct dcvs_core *core;
+
+ if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
+ __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
+ __func__, dcvs_core_id);
+ return;
+ }
+
+ core = msm_dcvs_get_core(dcvs_core_id);
+ core->actual_freq = core->get_frequency(core->type_core_num);
+}
+
+int msm_dcvs_freq_sink_start(int dcvs_core_id)
{
int ret = -EINVAL;
struct dcvs_core *core = NULL;
uint32_t ret1;
- uint32_t ret2;
+ unsigned long flags;
+ int new_freq;
+ int timer_interval_us;
- if (!drv || !drv->core_name)
- return ret;
+ if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
+ __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
+ __func__, dcvs_core_id);
+ return -EINVAL;
+ }
- core = msm_dcvs_get_core(drv->core_name, true);
+ core = msm_dcvs_get_core(dcvs_core_id);
if (!core)
return ret;
- mutex_lock(&core->lock);
- if (core->freq_driver && (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER))
- __info("Frequency notifier for %s being replaced\n",
- core->core_name);
- core->freq_driver = drv;
- core->task = kthread_create(msm_dcvs_do_freq, (void *)core,
- "msm_dcvs/%d", core->handle);
- if (IS_ERR(core->task)) {
- mutex_unlock(&core->lock);
- return -EFAULT;
+ core->actual_freq = core->get_frequency(core->type_core_num);
+
+ spin_lock_irqsave(&core->pending_freq_lock, flags);
+ /* mark that we are ready to accept new frequencies */
+ request_freq_change(core, NO_OUTSTANDING_FREQ_CHANGE);
+ spin_unlock_irqrestore(&core->pending_freq_lock, flags);
+
+ spin_lock_irqsave(&core->idle_state_change_lock, flags);
+ core->idle_entered = -1;
+ spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
+
+ /* Notify TZ to start receiving idle info for the core */
+ ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 1, &ret1);
+
+ ret = msm_dcvs_scm_event(
+ core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE, core->actual_freq,
+ 0, &new_freq, &timer_interval_us);
+ if (ret)
+ __err("Error (%d) DCVS sending online for %s\n",
+ ret, core->core_name);
+
+ if (new_freq != 0) {
+ spin_lock_irqsave(&core->pending_freq_lock, flags);
+ request_freq_change(core, new_freq);
+ spin_unlock_irqrestore(&core->pending_freq_lock, flags);
}
+ force_start_slack_timer(core, timer_interval_us);
- if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
- __info("Enabling idle pulse for %s\n", core->core_name);
- if (core->idle_driver) {
- core->actual_freq = core->freq_driver->get_frequency(drv);
- /* Notify TZ to start receiving idle info for the core */
- ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_ENABLE_CORE, 1,
- &ret1, &ret2);
- core->idle_driver->enable(core->idle_driver,
- MSM_DCVS_ENABLE_IDLE_PULSE);
- }
-
- mutex_unlock(&core->lock);
-
- return core->handle;
+ core->idle_enable(core->type_core_num, MSM_DCVS_ENABLE_IDLE_PULSE);
+ return 0;
}
-EXPORT_SYMBOL(msm_dcvs_freq_sink_register);
+EXPORT_SYMBOL(msm_dcvs_freq_sink_start);
-int msm_dcvs_freq_sink_unregister(struct msm_dcvs_freq *drv)
+int msm_dcvs_freq_sink_stop(int dcvs_core_id)
{
int ret = -EINVAL;
struct dcvs_core *core = NULL;
uint32_t ret1;
- uint32_t ret2;
+ uint32_t freq;
+ unsigned long flags;
- if (!drv || !drv->core_name)
- return ret;
-
- core = msm_dcvs_get_core(drv->core_name, false);
- if (!core)
- return ret;
-
- mutex_lock(&core->lock);
- if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
- __info("Disabling idle pulse for %s\n", core->core_name);
- if (core->idle_driver) {
- core->idle_driver->enable(core->idle_driver,
- MSM_DCVS_DISABLE_IDLE_PULSE);
- /* Notify TZ to stop receiving idle info for the core */
- ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_ENABLE_CORE, 0,
- &ret1, &ret2);
- hrtimer_cancel(&core->timer);
- core->idle_driver->enable(core->idle_driver,
- MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
- if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
- __info("Enabling LPM for %s\n", core->core_name);
+ if (dcvs_core_id < 0 || dcvs_core_id > CORES_MAX) {
+ pr_err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
+ __func__, dcvs_core_id);
+ return -EINVAL;
}
- core->freq_pending = 0;
- core->freq_driver = NULL;
- mutex_unlock(&core->lock);
- kthread_stop(core->task);
+
+ core = msm_dcvs_get_core(dcvs_core_id);
+ if (!core) {
+ __err("couldn't find core for coreid = %d\n", dcvs_core_id);
+ return ret;
+ }
+
+ core->idle_enable(core->type_core_num, MSM_DCVS_DISABLE_IDLE_PULSE);
+ /* Notify TZ to stop receiving idle info for the core */
+ ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_DCVS_ENABLE,
+ 0, core->actual_freq, &freq, &ret1);
+ core->idle_enable(core->type_core_num,
+ MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
+ spin_lock_irqsave(&core->pending_freq_lock, flags);
+ /* flush out all the pending freq changes */
+ request_freq_change(core, STOP_FREQ_CHANGE);
+ spin_unlock_irqrestore(&core->pending_freq_lock, flags);
+ force_stop_slack_timer(core);
return 0;
}
-EXPORT_SYMBOL(msm_dcvs_freq_sink_unregister);
+EXPORT_SYMBOL(msm_dcvs_freq_sink_stop);
-int msm_dcvs_idle_source_register(struct msm_dcvs_idle *drv)
-{
- int ret = -EINVAL;
- struct dcvs_core *core = NULL;
-
- if (!drv || !drv->core_name)
- return ret;
-
- core = msm_dcvs_get_core(drv->core_name, true);
- if (!core)
- return ret;
-
- mutex_lock(&core->lock);
- if (core->idle_driver && (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER))
- __info("Idle notifier for %s being replaced\n",
- core->core_name);
- core->idle_driver = drv;
- mutex_unlock(&core->lock);
-
- return core->handle;
-}
-EXPORT_SYMBOL(msm_dcvs_idle_source_register);
-
-int msm_dcvs_idle_source_unregister(struct msm_dcvs_idle *drv)
-{
- int ret = -EINVAL;
- struct dcvs_core *core = NULL;
-
- if (!drv || !drv->core_name)
- return ret;
-
- core = msm_dcvs_get_core(drv->core_name, false);
- if (!core)
- return ret;
-
- mutex_lock(&core->lock);
- core->idle_driver = NULL;
- mutex_unlock(&core->lock);
-
- return 0;
-}
-EXPORT_SYMBOL(msm_dcvs_idle_source_unregister);
-
-int msm_dcvs_idle(int handle, enum msm_core_idle_state state, uint32_t iowaited)
+int msm_dcvs_idle(int dcvs_core_id, enum msm_core_idle_state state,
+ uint32_t iowaited)
{
int ret = 0;
struct dcvs_core *core = NULL;
uint32_t timer_interval_us = 0;
uint32_t r0, r1;
- uint32_t freq_changed = 0;
- if (handle >= CORE_HANDLE_OFFSET &&
- (handle - CORE_HANDLE_OFFSET) < CORES_MAX)
- core = &core_list[handle - CORE_HANDLE_OFFSET];
+ if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
+ pr_err("invalid dcvs_core_id = %d ret -EINVAL\n", dcvs_core_id);
+ return -EINVAL;
+ }
- BUG_ON(!core);
-
- if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
- __info("Core %s idle state %d\n", core->core_name, state);
+ core = msm_dcvs_get_core(dcvs_core_id);
switch (state) {
case MSM_DCVS_IDLE_ENTER:
- hrtimer_cancel(&core->timer);
- ret = msm_dcvs_scm_event(core->handle,
+ stop_slack_timer(core);
+ ret = msm_dcvs_scm_event(core->dcvs_core_id,
MSM_DCVS_SCM_IDLE_ENTER, 0, 0, &r0, &r1);
- if (ret)
+ if (ret < 0 && ret != -13)
__err("Error (%d) sending idle enter for %s\n",
ret, core->core_name);
+ trace_msm_dcvs_idle("idle_enter_exit", core->core_name, 1);
break;
case MSM_DCVS_IDLE_EXIT:
- hrtimer_cancel(&core->timer);
ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT,
- iowaited, &timer_interval_us, &freq_changed);
+ iowaited, &timer_interval_us);
if (ret)
__err("Error (%d) sending idle exit for %s\n",
ret, core->core_name);
- /* only start slack timer if change_freq won't */
- if (freq_changed || core->change_freq_activated)
- break;
- if (timer_interval_us && !core->timer_disabled) {
- ret = hrtimer_start(&core->timer,
- ktime_set(0, timer_interval_us * 1000),
- HRTIMER_MODE_REL_PINNED);
-
- if (ret)
- __err("Failed to register timer for core %s\n",
- core->core_name);
- }
+ start_slack_timer(core, timer_interval_us);
+ trace_msm_dcvs_idle("idle_enter_exit", core->core_name, 0);
+ trace_msm_dcvs_iowait("iowait", core->core_name, iowaited);
+ trace_msm_dcvs_slack_time("slack_timer_dcvs", core->core_name,
+ timer_interval_us);
break;
}
@@ -755,13 +1025,6 @@
goto err;
}
- if (!debugfs_create_u32("debug_mask", S_IRUGO | S_IWUSR,
- debugfs_base, &msm_dcvs_debug)) {
- __err("Cannot create debugfs entry %s\n", "debug_mask");
- ret = -ENOMEM;
- goto err;
- }
-
err:
if (ret) {
kobject_del(cores_kobj);
@@ -776,16 +1039,24 @@
static int __init msm_dcvs_early_init(void)
{
int ret = 0;
+ int i;
if (!msm_dcvs_enabled) {
__info("Not enabled (%d)\n", msm_dcvs_enabled);
return 0;
}
- ret = msm_dcvs_scm_init(10 * 1024);
- if (ret)
- __err("Unable to initialize DCVS err=%d\n", ret);
+ /* Only need about 32kBytes for normal operation */
+ ret = msm_dcvs_scm_init(SZ_32K);
+ if (ret) {
+ __err("Unable to initialize DCVS err=%d\n", ret);
+ goto done;
+ }
+
+ for (i = 0; i < CORES_MAX; i++)
+ core_list[i].dcvs_core_id = -1;
+done:
return ret;
}
postcore_initcall(msm_dcvs_early_init);
diff --git a/arch/arm/mach-msm/msm_dcvs_idle.c b/arch/arm/mach-msm/msm_dcvs_idle.c
deleted file mode 100644
index 179e170..0000000
--- a/arch/arm/mach-msm/msm_dcvs_idle.c
+++ /dev/null
@@ -1,170 +0,0 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/cpu_pm.h>
-#include <linux/platform_device.h>
-#include <linux/pm_qos.h>
-#include <linux/hrtimer.h>
-#include <linux/tick.h>
-#include <mach/msm_dcvs.h>
-
-struct cpu_idle_info {
- int cpu;
- int enabled;
- int handle;
- struct msm_dcvs_idle dcvs_notifier;
-};
-
-static DEFINE_PER_CPU_SHARED_ALIGNED(struct cpu_idle_info, cpu_idle_info);
-static DEFINE_PER_CPU_SHARED_ALIGNED(u64, iowait_on_cpu);
-static char core_name[NR_CPUS][10];
-static struct pm_qos_request qos_req;
-static uint32_t latency;
-
-static int msm_dcvs_idle_notifier(struct msm_dcvs_idle *self,
- enum msm_core_control_event event)
-{
- struct cpu_idle_info *info = container_of(self,
- struct cpu_idle_info, dcvs_notifier);
-
- switch (event) {
- case MSM_DCVS_ENABLE_IDLE_PULSE:
- info->enabled = true;
- break;
-
- case MSM_DCVS_DISABLE_IDLE_PULSE:
- info->enabled = false;
- break;
-
- case MSM_DCVS_ENABLE_HIGH_LATENCY_MODES:
- pm_qos_update_request(&qos_req, PM_QOS_DEFAULT_VALUE);
- break;
-
- case MSM_DCVS_DISABLE_HIGH_LATENCY_MODES:
- pm_qos_update_request(&qos_req, latency);
- break;
- }
-
- return 0;
-}
-
-static int msm_cpuidle_notifier(struct notifier_block *self, unsigned long cmd,
- void *v)
-{
- struct cpu_idle_info *info =
- &per_cpu(cpu_idle_info, smp_processor_id());
- u64 io_wait_us = 0;
- u64 prev_io_wait_us = 0;
- u64 last_update_time = 0;
- u64 val = 0;
- uint32_t iowaited = 0;
-
- if (!info->enabled)
- return NOTIFY_OK;
-
- switch (cmd) {
- case CPU_PM_ENTER:
- val = get_cpu_iowait_time_us(smp_processor_id(),
- &last_update_time);
- /* val could be -1 when NOHZ is not enabled */
- if (val == (u64)-1)
- val = 0;
- per_cpu(iowait_on_cpu, smp_processor_id()) = val;
- msm_dcvs_idle(info->handle, MSM_DCVS_IDLE_ENTER, 0);
- break;
-
- case CPU_PM_ENTER_FAILED:
- case CPU_PM_EXIT:
- prev_io_wait_us = per_cpu(iowait_on_cpu, smp_processor_id());
- val = get_cpu_iowait_time_us(smp_processor_id(),
- &last_update_time);
- if (val == (u64)-1)
- val = 0;
- io_wait_us = val;
- iowaited = (io_wait_us - prev_io_wait_us);
- msm_dcvs_idle(info->handle, MSM_DCVS_IDLE_EXIT, iowaited);
- break;
- }
-
- return NOTIFY_OK;
-}
-
-static struct notifier_block idle_nb = {
- .notifier_call = msm_cpuidle_notifier,
-};
-
-static int msm_dcvs_idle_probe(struct platform_device *pdev)
-{
- int cpu;
- struct cpu_idle_info *info = NULL;
- struct msm_dcvs_idle *inotify = NULL;
-
- for_each_possible_cpu(cpu) {
- info = &per_cpu(cpu_idle_info, cpu);
- info->cpu = cpu;
- inotify = &info->dcvs_notifier;
- snprintf(core_name[cpu], 10, "cpu%d", cpu);
- inotify->core_name = core_name[cpu];
- inotify->enable = msm_dcvs_idle_notifier;
- info->handle = msm_dcvs_idle_source_register(inotify);
- BUG_ON(info->handle < 0);
- }
-
- latency = *((uint32_t *)pdev->dev.platform_data);
- pm_qos_add_request(&qos_req, PM_QOS_CPU_DMA_LATENCY,
- PM_QOS_DEFAULT_VALUE);
-
- return cpu_pm_register_notifier(&idle_nb);
-}
-
-static int msm_dcvs_idle_remove(struct platform_device *pdev)
-{
- int ret = 0;
- int rc = 0;
- int cpu = 0;
- struct msm_dcvs_idle *inotify = NULL;
- struct cpu_idle_info *info = NULL;
-
- rc = cpu_pm_unregister_notifier(&idle_nb);
-
- for_each_possible_cpu(cpu) {
- info = &per_cpu(cpu_idle_info, cpu);
- inotify = &info->dcvs_notifier;
- ret = msm_dcvs_idle_source_unregister(inotify);
- if (ret) {
- rc = -EFAULT;
- pr_err("Error de-registering core %d idle notifier.\n",
- cpu);
- }
- }
-
- return rc;
-}
-
-static struct platform_driver idle_pdrv = {
- .probe = msm_dcvs_idle_probe,
- .remove = __devexit_p(msm_dcvs_idle_remove),
- .driver = {
- .name = "msm_cpu_idle",
- .owner = THIS_MODULE,
- },
-};
-
-static int msm_dcvs_idle_init(void)
-{
- return platform_driver_register(&idle_pdrv);
-}
-late_initcall(msm_dcvs_idle_init);
diff --git a/arch/arm/mach-msm/msm_dcvs_scm.c b/arch/arm/mach-msm/msm_dcvs_scm.c
index 6095e0813..78d62ac 100644
--- a/arch/arm/mach-msm/msm_dcvs_scm.c
+++ b/arch/arm/mach-msm/msm_dcvs_scm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -20,18 +20,17 @@
#include <mach/memory.h>
#include <mach/scm.h>
#include <mach/msm_dcvs_scm.h>
+#include <trace/events/mpdcvs_trace.h>
-#define DCVS_CMD_CREATE_GROUP 1
#define DCVS_CMD_REGISTER_CORE 2
#define DCVS_CMD_SET_ALGO_PARAM 3
#define DCVS_CMD_EVENT 4
#define DCVS_CMD_INIT 5
+#define DCVS_CMD_SET_POWER_PARAM 6
struct scm_register_core {
uint32_t core_id;
- uint32_t group_id;
phys_addr_t core_param_phy;
- phys_addr_t freq_phy;
};
struct scm_algo {
@@ -44,6 +43,21 @@
uint32_t size;
};
+struct scm_pwr_param {
+ uint32_t core_id;
+ phys_addr_t pwr_param_phy;
+ phys_addr_t freq_phy;
+ phys_addr_t coeffs_phy;
+};
+
+struct msm_algo_param {
+ enum msm_dcvs_algo_param_type type;
+ union {
+ struct msm_dcvs_algo_param dcvs_param;
+ struct msm_mpd_algo_param mpd_param;
+ } u;
+};
+
int msm_dcvs_scm_init(size_t size)
{
int ret = 0;
@@ -69,49 +83,25 @@
}
EXPORT_SYMBOL(msm_dcvs_scm_init);
-int msm_dcvs_scm_create_group(uint32_t id)
-{
- int ret = 0;
-
- ret = scm_call(SCM_SVC_DCVS, DCVS_CMD_CREATE_GROUP,
- &id, sizeof(uint32_t), NULL, 0);
-
- return ret;
-}
-EXPORT_SYMBOL(msm_dcvs_scm_create_group);
-
-int msm_dcvs_scm_register_core(uint32_t core_id, uint32_t group_id,
- struct msm_dcvs_core_param *param,
- struct msm_dcvs_freq_entry *freq)
+int msm_dcvs_scm_register_core(uint32_t core_id,
+ struct msm_dcvs_core_param *param)
{
int ret = 0;
struct scm_register_core reg_data;
struct msm_dcvs_core_param *p = NULL;
- struct msm_dcvs_freq_entry *f = NULL;
p = kzalloc(PAGE_ALIGN(sizeof(struct msm_dcvs_core_param)), GFP_KERNEL);
if (!p)
return -ENOMEM;
- f = kzalloc(PAGE_ALIGN(sizeof(struct msm_dcvs_freq_entry) *
- param->num_freq), GFP_KERNEL);
- if (!f) {
- kfree(p);
- return -ENOMEM;
- }
-
memcpy(p, param, sizeof(struct msm_dcvs_core_param));
- memcpy(f, freq, sizeof(struct msm_dcvs_freq_entry) * param->num_freq);
reg_data.core_id = core_id;
- reg_data.group_id = group_id;
reg_data.core_param_phy = virt_to_phys(p);
- reg_data.freq_phy = virt_to_phys(f);
ret = scm_call(SCM_SVC_DCVS, DCVS_CMD_REGISTER_CORE,
®_data, sizeof(reg_data), NULL, 0);
- kfree(f);
kfree(p);
return ret;
@@ -123,13 +113,14 @@
{
int ret = 0;
struct scm_algo algo;
- struct msm_dcvs_algo_param *p = NULL;
+ struct msm_algo_param *p = NULL;
- p = kzalloc(PAGE_ALIGN(sizeof(struct msm_dcvs_algo_param)), GFP_KERNEL);
+ p = kzalloc(PAGE_ALIGN(sizeof(struct msm_algo_param)), GFP_KERNEL);
if (!p)
return -ENOMEM;
- memcpy(p, param, sizeof(struct msm_dcvs_algo_param));
+ p->type = MSM_DCVS_ALGO_DCVS_PARAM;
+ memcpy(&p->u.dcvs_param, param, sizeof(struct msm_dcvs_algo_param));
algo.core_id = core_id;
algo.algo_phy = virt_to_phys(p);
@@ -143,6 +134,85 @@
}
EXPORT_SYMBOL(msm_dcvs_scm_set_algo_params);
+int msm_mpd_scm_set_algo_params(struct msm_mpd_algo_param *param)
+{
+ int ret = 0;
+ struct scm_algo algo;
+ struct msm_algo_param *p = NULL;
+
+ p = kzalloc(PAGE_ALIGN(sizeof(struct msm_algo_param)), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ p->type = MSM_DCVS_ALGO_MPD_PARAM;
+ memcpy(&p->u.mpd_param, param, sizeof(struct msm_mpd_algo_param));
+
+ algo.core_id = 0;
+ algo.algo_phy = virt_to_phys(p);
+
+ ret = scm_call(SCM_SVC_DCVS, DCVS_CMD_SET_ALGO_PARAM,
+ &algo, sizeof(algo), NULL, 0);
+
+ kfree(p);
+
+ return ret;
+}
+EXPORT_SYMBOL(msm_mpd_scm_set_algo_params);
+
+int msm_dcvs_scm_set_power_params(uint32_t core_id,
+ struct msm_dcvs_power_params *pwr_param,
+ struct msm_dcvs_freq_entry *freq_entry,
+ struct msm_dcvs_energy_curve_coeffs *coeffs)
+{
+ int ret = 0;
+ struct scm_pwr_param pwr;
+ struct msm_dcvs_power_params *pwrt = NULL;
+ struct msm_dcvs_freq_entry *freqt = NULL;
+ struct msm_dcvs_energy_curve_coeffs *coefft = NULL;
+
+ pwrt = kzalloc(PAGE_ALIGN(sizeof(struct msm_dcvs_power_params)),
+ GFP_KERNEL);
+ if (!pwrt)
+ return -ENOMEM;
+
+ freqt = kzalloc(PAGE_ALIGN(sizeof(struct msm_dcvs_freq_entry)
+ * pwr_param->num_freq),
+ GFP_KERNEL);
+ if (!freqt) {
+ kfree(pwrt);
+ return -ENOMEM;
+ }
+
+ coefft = kzalloc(PAGE_ALIGN(
+ sizeof(struct msm_dcvs_energy_curve_coeffs)),
+ GFP_KERNEL);
+ if (!coefft) {
+ kfree(pwrt);
+ kfree(freqt);
+ return -ENOMEM;
+ }
+
+ memcpy(pwrt, pwr_param, sizeof(struct msm_dcvs_power_params));
+ memcpy(freqt, freq_entry,
+ sizeof(struct msm_dcvs_freq_entry)*pwr_param->num_freq);
+ memcpy(coefft, coeffs, sizeof(struct msm_dcvs_energy_curve_coeffs));
+
+ pwr.core_id = core_id;
+ pwr.pwr_param_phy = virt_to_phys(pwrt);
+ pwr.freq_phy = virt_to_phys(freqt);
+ pwr.coeffs_phy = virt_to_phys(coefft);
+
+ ret = scm_call(SCM_SVC_DCVS, DCVS_CMD_SET_POWER_PARAM,
+ &pwr, sizeof(pwr), NULL, 0);
+
+ kfree(pwrt);
+ kfree(freqt);
+ kfree(coefft);
+
+ return ret;
+}
+EXPORT_SYMBOL(msm_dcvs_scm_set_power_params);
+
int msm_dcvs_scm_event(uint32_t core_id,
enum msm_dcvs_scm_event event_id,
uint32_t param0, uint32_t param1,
@@ -156,6 +226,9 @@
ret = scm_call_atomic4_3(SCM_SVC_DCVS, DCVS_CMD_EVENT,
core_id, event_id, param0, param1, ret0, ret1);
+ trace_msm_dcvs_scm_event(core_id, (int)event_id, param0, param1,
+ *ret0, *ret1);
+
return ret;
}
EXPORT_SYMBOL(msm_dcvs_scm_event);
diff --git a/arch/arm/mach-msm/msm_dsps.c b/arch/arm/mach-msm/msm_dsps.c
index c39829b..b85c812 100644
--- a/arch/arm/mach-msm/msm_dsps.c
+++ b/arch/arm/mach-msm/msm_dsps.c
@@ -15,8 +15,6 @@
*
*/
-#include <asm/atomic.h>
-
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/module.h>
@@ -39,7 +37,6 @@
#include <mach/msm_smsm.h>
#include <mach/msm_dsps.h>
#include <mach/subsystem_restart.h>
-#include <mach/subsystem_notif.h>
#include "ramdump.h"
#include "timer.h"
@@ -66,8 +63,6 @@
* @smem_ramdump_segments - Ramdump segment information for smem
* @is_on - DSPS is on.
* @ref_count - open/close reference count.
- * @wdog_irq - DSPS Watchdog IRQ
- * @crash_in_progress - 1 if crash recovery is in progress
* @ppss_base - ppss registers virtual base address.
*/
struct dsps_drv {
@@ -81,17 +76,9 @@
void *pil;
- void *dspsfw_ramdump_dev;
- struct ramdump_segment dspsfw_ramdump_segments[4];
-
- void *smem_ramdump_dev;
- struct ramdump_segment smem_ramdump_segments[1];
-
int is_on;
int ref_count;
- int wdog_irq;
- atomic_t crash_in_progress;
void __iomem *ppss_base;
};
@@ -101,13 +88,6 @@
static struct dsps_drv *drv;
/**
- * self-initiated shutdown flag
- */
-static int dsps_crash_shutdown_g;
-
-static void dsps_restart_handler(void);
-
-/**
* Load DSPS Firmware.
*/
static int dsps_load(const char *name)
@@ -382,41 +362,6 @@
}
/**
- *
- * Log subsystem restart failure reason
- */
-static void dsps_log_sfr(void)
-{
- const char dflt_reason[] = "Died too early due to unknown reason";
- char *smem_reset_reason;
- unsigned smem_reset_size;
-
- smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_DSPS0,
- &smem_reset_size);
- if (smem_reset_reason != NULL && smem_reset_reason[0] != 0) {
- smem_reset_reason[smem_reset_size-1] = 0;
- pr_err("%s: DSPS failure: %s\nResetting DSPS\n",
- __func__, smem_reset_reason);
- memset(smem_reset_reason, 0, smem_reset_size);
- wmb();
- } else
- pr_err("%s: DSPS failure: %s\nResetting DSPS\n",
- __func__, dflt_reason);
-}
-
-/**
- * Watchdog interrupt handler
- *
- */
-static irqreturn_t dsps_wdog_bite_irq(int irq, void *dev_id)
-{
- pr_err("%s\n", __func__);
- dsps_log_sfr();
- dsps_restart_handler();
- return IRQ_HANDLED;
-}
-
-/**
* IO Control - handle commands from client.
*
*/
@@ -452,7 +397,7 @@
case DSPS_IOCTL_RESET:
pr_err("%s: User-initiated DSPS reset.\nResetting DSPS\n",
__func__);
- dsps_restart_handler();
+ subsystem_restart("dsps");
ret = 0;
break;
default:
@@ -471,7 +416,6 @@
{
int ret = -ENODEV;
struct resource *ppss_res;
- struct resource *ppss_wdog;
int i;
pr_debug("%s.\n", __func__);
@@ -541,58 +485,11 @@
drv->ppss_base = ioremap(ppss_res->start,
resource_size(ppss_res));
- ppss_wdog = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
- "ppss_wdog");
- if (ppss_wdog) {
- drv->wdog_irq = ppss_wdog->start;
- ret = request_irq(drv->wdog_irq, dsps_wdog_bite_irq,
- IRQF_TRIGGER_RISING, "dsps_wdog", NULL);
- if (ret) {
- pr_err("%s: request_irq fail %d\n", __func__, ret);
- goto request_irq_err;
- }
- } else {
- drv->wdog_irq = -1;
- pr_debug("%s: ppss_wdog not supported.\n", __func__);
- }
-
- drv->dspsfw_ramdump_segments[0].address = drv->pdata->tcm_code_start;
- drv->dspsfw_ramdump_segments[0].size = drv->pdata->tcm_code_size;
- drv->dspsfw_ramdump_segments[1].address = drv->pdata->tcm_buf_start;
- drv->dspsfw_ramdump_segments[1].size = drv->pdata->tcm_buf_size;
- drv->dspsfw_ramdump_segments[2].address = drv->pdata->pipe_start;
- drv->dspsfw_ramdump_segments[2].size = drv->pdata->pipe_size;
- drv->dspsfw_ramdump_segments[3].address = drv->pdata->ddr_start;
- drv->dspsfw_ramdump_segments[3].size = drv->pdata->ddr_size;
-
- drv->dspsfw_ramdump_dev = create_ramdump_device("dsps");
- if (!drv->dspsfw_ramdump_dev) {
- pr_err("%s: create_ramdump_device(\"dsps\") fail\n",
- __func__);
- goto create_ramdump_err;
- }
-
- drv->smem_ramdump_segments[0].address = drv->pdata->smem_start;
- drv->smem_ramdump_segments[0].size = drv->pdata->smem_size;
- drv->smem_ramdump_dev = create_ramdump_device("smem-dsps");
- if (!drv->smem_ramdump_dev) {
- pr_err("%s: create_ramdump_device(\"smem\") fail\n",
- __func__);
- goto create_ramdump_err;
- }
-
if (drv->pdata->init)
drv->pdata->init(drv->pdata);
return 0;
-create_ramdump_err:
- disable_irq_nosync(drv->wdog_irq);
- free_irq(drv->wdog_irq, NULL);
-
-request_irq_err:
- iounmap(drv->ppss_base);
-
reg_err:
for (i = 0; i < drv->pdata->regs_num; i++) {
if (drv->pdata->regs[i].reg) {
@@ -678,8 +575,6 @@
}
}
- free_irq(drv->wdog_irq, NULL);
-
iounmap(drv->ppss_base);
}
@@ -722,138 +617,6 @@
.unlocked_ioctl = dsps_ioctl,
};
-static struct subsys_device *dsps_dev;
-
-/**
- * Fatal error handler
- * Resets DSPS.
- */
-static void dsps_restart_handler(void)
-{
- pr_debug("%s: Restart lvl %d\n",
- __func__, get_restart_level());
-
- if (atomic_add_return(1, &drv->crash_in_progress) > 1) {
- pr_err("%s: DSPS already resetting. Count %d\n", __func__,
- atomic_read(&drv->crash_in_progress));
- } else {
- subsystem_restart_dev(dsps_dev);
- }
-}
-
-
-/**
- * SMSM state change callback
- *
- */
-static void dsps_smsm_state_cb(void *data, uint32_t old_state,
- uint32_t new_state)
-{
- pr_debug("%s\n", __func__);
- if (dsps_crash_shutdown_g == 1) {
- pr_debug("%s: SMSM_RESET state change ignored\n",
- __func__);
- dsps_crash_shutdown_g = 0;
- return;
- }
- if (new_state & SMSM_RESET) {
- dsps_log_sfr();
- dsps_restart_handler();
- }
-}
-
-/**
- * Shutdown function
- * called by the restart notifier
- *
- */
-static int dsps_shutdown(const struct subsys_desc *subsys)
-{
- pr_debug("%s\n", __func__);
- disable_irq_nosync(drv->wdog_irq);
- if (drv->pdata->ppss_wdog_unmasked_int_en_reg) {
- writel_relaxed(0, (drv->ppss_base+
- drv->pdata->ppss_wdog_unmasked_int_en_reg));
- mb(); /* Make sure wdog is disabled before shutting down */
- }
- pil_force_shutdown(drv->pdata->pil_name);
- dsps_power_off_handler();
- return 0;
-}
-
-/**
- * Powerup function
- * called by the restart notifier
- *
- */
-static int dsps_powerup(const struct subsys_desc *subsys)
-{
- pr_debug("%s\n", __func__);
- dsps_power_on_handler();
- pil_force_boot(drv->pdata->pil_name);
- atomic_set(&drv->crash_in_progress, 0);
- enable_irq(drv->wdog_irq);
- return 0;
-}
-
-/**
- * Crash shutdown function
- * called by the restart notifier
- *
- */
-static void dsps_crash_shutdown(const struct subsys_desc *subsys)
-{
- pr_debug("%s\n", __func__);
- disable_irq_nosync(drv->wdog_irq);
- dsps_crash_shutdown_g = 1;
- smsm_change_state(SMSM_DSPS_STATE, SMSM_RESET, SMSM_RESET);
-}
-
-/**
- * Ramdump function
- * called by the restart notifier
- *
- */
-static int dsps_ramdump(int enable, const struct subsys_desc *subsys)
-{
- int ret = 0;
- pr_debug("%s\n", __func__);
-
- if (enable) {
- if (drv->dspsfw_ramdump_dev != NULL) {
- ret = do_ramdump(drv->dspsfw_ramdump_dev,
- drv->dspsfw_ramdump_segments,
- ARRAY_SIZE(drv->dspsfw_ramdump_segments));
- if (ret < 0) {
- pr_err("%s: Unable to dump DSPS memory (rc = %d).\n",
- __func__, ret);
- goto dsps_ramdump_out;
- }
- }
- if (drv->smem_ramdump_dev != NULL) {
- ret = do_ramdump(drv->smem_ramdump_dev,
- drv->smem_ramdump_segments,
- ARRAY_SIZE(drv->smem_ramdump_segments));
- if (ret < 0) {
- pr_err("%s: Unable to dump smem memory (rc = %d).\n",
- __func__, ret);
- goto dsps_ramdump_out;
- }
- }
- }
-
-dsps_ramdump_out:
- return ret;
-}
-
-static struct subsys_desc dsps_ssrops = {
- .name = "dsps",
- .shutdown = dsps_shutdown,
- .powerup = dsps_powerup,
- .ramdump = dsps_ramdump,
- .crash_shutdown = dsps_crash_shutdown
-};
-
/**
* platform driver
*
@@ -874,8 +637,6 @@
pr_err("%s: kzalloc fail.\n", __func__);
goto alloc_err;
}
- atomic_set(&drv->crash_in_progress, 0);
-
drv->pdata = pdev->dev.platform_data;
drv->dev_class = class_create(THIS_MODULE, DRV_NAME);
@@ -918,31 +679,6 @@
goto cdev_add_err;
}
- ret =
- smsm_state_cb_register(SMSM_DSPS_STATE, SMSM_RESET,
- dsps_smsm_state_cb, 0);
- if (ret) {
- pr_err("%s: smsm_state_cb_register fail %d\n", __func__,
- ret);
- goto smsm_register_err;
- }
-
- dsps_dev = subsys_register(&dsps_ssrops);
- if (IS_ERR(dsps_dev)) {
- ret = PTR_ERR(dsps_dev);
- pr_err("%s: subsys_register fail %d\n", __func__,
- ret);
- goto ssr_register_err;
- }
-
- return 0;
-
-ssr_register_err:
- smsm_state_cb_deregister(SMSM_DSPS_STATE, SMSM_RESET,
- dsps_smsm_state_cb,
- 0);
-smsm_register_err:
- cdev_del(drv->cdev);
cdev_add_err:
kfree(drv->cdev);
cdev_alloc_err:
@@ -962,7 +698,6 @@
{
pr_debug("%s.\n", __func__);
- subsys_unregister(dsps_dev);
dsps_power_off_handler();
dsps_free_resources();
diff --git a/arch/arm/mach-msm/msm_mpdecision.c b/arch/arm/mach-msm/msm_mpdecision.c
new file mode 100644
index 0000000..9f6cc11
--- /dev/null
+++ b/arch/arm/mach-msm/msm_mpdecision.c
@@ -0,0 +1,709 @@
+ /* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "mpd %s: " fmt, __func__
+
+#include <linux/cpumask.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/kthread.h>
+#include <linux/kobject.h>
+#include <linux/ktime.h>
+#include <linux/hrtimer.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/cpu.h>
+#include <linux/stringify.h>
+#include <linux/sched.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/cpu_pm.h>
+#include <linux/cpu.h>
+#include <linux/cpufreq.h>
+#include <linux/sched.h>
+#include <linux/rq_stats.h>
+#include <asm/atomic.h>
+#include <asm/page.h>
+#include <mach/msm_dcvs.h>
+#include <mach/msm_dcvs_scm.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/mpdcvs_trace.h>
+
+#define DEFAULT_RQ_AVG_POLL_MS (1)
+
+struct mpd_attrib {
+ struct kobj_attribute enabled;
+ struct kobj_attribute rq_avg_poll_ms;
+ struct kobj_attribute iowait_threshold_pct;
+
+ struct kobj_attribute em_win_size_min_us;
+ struct kobj_attribute em_win_size_max_us;
+ struct kobj_attribute em_max_util_pct;
+ struct kobj_attribute mp_em_rounding_point_min;
+ struct kobj_attribute mp_em_rounding_point_max;
+ struct kobj_attribute online_util_pct_min;
+ struct kobj_attribute online_util_pct_max;
+ struct kobj_attribute slack_time_min_us;
+ struct kobj_attribute slack_time_max_us;
+ struct kobj_attribute hp_up_max_ms;
+ struct kobj_attribute hp_up_ms;
+ struct kobj_attribute hp_up_count;
+ struct kobj_attribute hp_dw_max_ms;
+ struct kobj_attribute hp_dw_ms;
+ struct kobj_attribute hp_dw_count;
+ struct attribute_group attrib_group;
+};
+
+struct msm_mpd_scm_data {
+ enum msm_dcvs_scm_event event;
+ int nr;
+};
+
+struct mpdecision {
+ uint32_t enabled;
+ atomic_t algo_cpu_mask;
+ uint32_t rq_avg_poll_ms;
+ uint32_t iowait_threshold_pct;
+ ktime_t next_update;
+ uint32_t slack_us;
+ struct msm_mpd_algo_param mp_param;
+ struct mpd_attrib attrib;
+ struct mutex lock;
+ struct task_struct *task;
+ struct task_struct *hptask;
+ struct hrtimer slack_timer;
+ struct msm_mpd_scm_data data;
+ int hpupdate;
+ wait_queue_head_t wait_q;
+ wait_queue_head_t wait_hpq;
+};
+
+struct hp_latency {
+ int hp_up_max_ms;
+ int hp_up_ms;
+ int hp_up_count;
+ int hp_dw_max_ms;
+ int hp_dw_ms;
+ int hp_dw_count;
+};
+
+static DEFINE_PER_CPU(struct hrtimer, rq_avg_poll_timer);
+static DEFINE_SPINLOCK(rq_avg_lock);
+
+enum {
+ MSM_MPD_DEBUG_NOTIFIER = BIT(0),
+ MSM_MPD_CORE_STATUS = BIT(1),
+ MSM_MPD_SLACK_TIMER = BIT(2),
+};
+
+enum {
+ HPUPDATE_WAITING = 0, /* we are waiting for cpumask update */
+ HPUPDATE_SCHEDULED = 1, /* we are in the process of hotplugging */
+ HPUPDATE_IN_PROGRESS = 2, /* we are in the process of hotplugging */
+};
+
+static int msm_mpd_enabled = 1;
+module_param_named(enabled, msm_mpd_enabled, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+static struct dentry *debugfs_base;
+static struct mpdecision msm_mpd;
+
+static struct hp_latency hp_latencies;
+
+static unsigned long last_nr;
+static int num_present_hundreds;
+
+#define RQ_AVG_INSIGNIFICANT_BITS 3
+static bool ok_to_update_tz(int nr, int last_nr)
+{
+ /*
+ * Exclude unnecessary TZ reports if run queue haven't changed much from
+ * the last reported value. The left shift by INSIGNIFICANT_BITS is to
+ * filter out small changes in the run queue average which won't cause
+ * a online cpu mask change. Also if the cpu online count does not match
+ * the count requested by TZ and we are not in the process of bringing
+ * cpus online as indicated by a HPUPDATE_IN_PROGRESS in msm_mpd.hpdata
+ */
+ return
+ (((nr >> RQ_AVG_INSIGNIFICANT_BITS)
+ != (last_nr >> RQ_AVG_INSIGNIFICANT_BITS))
+ || ((hweight32(atomic_read(&msm_mpd.algo_cpu_mask))
+ != num_online_cpus())
+ && (msm_mpd.hpupdate != HPUPDATE_IN_PROGRESS)));
+}
+
+static enum hrtimer_restart msm_mpd_rq_avg_poll_timer(struct hrtimer *timer)
+{
+ int nr, nr_iowait;
+ ktime_t curr_time = ktime_get();
+ unsigned long flags;
+ int cpu = smp_processor_id();
+ enum hrtimer_restart restart = HRTIMER_RESTART;
+
+ spin_lock_irqsave(&rq_avg_lock, flags);
+ /* If running on the wrong cpu, don't restart */
+ if (&per_cpu(rq_avg_poll_timer, cpu) != timer)
+ restart = HRTIMER_NORESTART;
+
+ if (ktime_to_ns(ktime_sub(curr_time, msm_mpd.next_update)) < 0)
+ goto out;
+
+ msm_mpd.next_update = ktime_add_ns(curr_time,
+ (msm_mpd.rq_avg_poll_ms * NSEC_PER_MSEC));
+
+ sched_get_nr_running_avg(&nr, &nr_iowait);
+
+ if ((nr_iowait >= msm_mpd.iowait_threshold_pct) && (nr < last_nr))
+ nr = last_nr;
+
+ if (nr > num_present_hundreds)
+ nr = num_present_hundreds;
+
+ trace_msm_mp_runq("nr_running", nr);
+
+ if (ok_to_update_tz(nr, last_nr)) {
+ hrtimer_try_to_cancel(&msm_mpd.slack_timer);
+ msm_mpd.data.nr = nr;
+ msm_mpd.data.event = MSM_DCVS_SCM_RUNQ_UPDATE;
+ wake_up(&msm_mpd.wait_q);
+ last_nr = nr;
+ }
+
+out:
+ hrtimer_set_expires(timer, msm_mpd.next_update);
+ spin_unlock_irqrestore(&rq_avg_lock, flags);
+ /* set next expiration */
+ return restart;
+}
+
+static void bring_up_cpu(int cpu)
+{
+ int cpu_action_time_ms;
+ int time_taken_ms;
+ int ret, ret1, ret2;
+
+ cpu_action_time_ms = ktime_to_ms(ktime_get());
+ ret = cpu_up(cpu);
+ if (ret) {
+ pr_debug("Error %d online core %d\n", ret, cpu);
+ } else {
+ time_taken_ms = ktime_to_ms(ktime_get()) - cpu_action_time_ms;
+ if (time_taken_ms > hp_latencies.hp_up_max_ms)
+ hp_latencies.hp_up_max_ms = time_taken_ms;
+ hp_latencies.hp_up_ms += time_taken_ms;
+ hp_latencies.hp_up_count++;
+ ret = msm_dcvs_scm_event(
+ CPU_OFFSET + cpu,
+ MSM_DCVS_SCM_CORE_ONLINE,
+ cpufreq_get(cpu),
+ (uint32_t) time_taken_ms * USEC_PER_MSEC,
+ &ret1, &ret2);
+ if (ret)
+ pr_err("Error sending hotplug scm event err=%d\n", ret);
+ }
+}
+
+static void bring_down_cpu(int cpu)
+{
+ int cpu_action_time_ms;
+ int time_taken_ms;
+ int ret, ret1, ret2;
+
+ BUG_ON(cpu == 0);
+ cpu_action_time_ms = ktime_to_ms(ktime_get());
+ ret = cpu_down(cpu);
+ if (ret) {
+ pr_debug("Error %d offline" "core %d\n", ret, cpu);
+ } else {
+ time_taken_ms = ktime_to_ms(ktime_get()) - cpu_action_time_ms;
+ if (time_taken_ms > hp_latencies.hp_dw_max_ms)
+ hp_latencies.hp_dw_max_ms = time_taken_ms;
+ hp_latencies.hp_dw_ms += time_taken_ms;
+ hp_latencies.hp_dw_count++;
+ ret = msm_dcvs_scm_event(
+ CPU_OFFSET + cpu,
+ MSM_DCVS_SCM_CORE_OFFLINE,
+ (uint32_t) time_taken_ms * USEC_PER_MSEC,
+ 0,
+ &ret1, &ret2);
+ if (ret)
+ pr_err("Error sending hotplug scm event err=%d\n", ret);
+ }
+}
+
+static int __ref msm_mpd_update_scm(enum msm_dcvs_scm_event event, int nr)
+{
+ int ret = 0;
+ uint32_t req_cpu_mask = 0;
+ uint32_t slack_us = 0;
+ uint32_t param0 = 0;
+
+ if (event == MSM_DCVS_SCM_RUNQ_UPDATE)
+ param0 = nr;
+
+ ret = msm_dcvs_scm_event(0, event, param0, 0,
+ &req_cpu_mask, &slack_us);
+
+ if (ret) {
+ pr_err("Error (%d) sending event %d, param %d\n", ret, event,
+ param0);
+ return ret;
+ }
+
+ trace_msm_mp_cpusonline("cpu_online_mp", req_cpu_mask);
+ trace_msm_mp_slacktime("slack_time_mp", slack_us);
+ msm_mpd.slack_us = slack_us;
+ atomic_set(&msm_mpd.algo_cpu_mask, req_cpu_mask);
+ msm_mpd.hpupdate = HPUPDATE_SCHEDULED;
+ wake_up(&msm_mpd.wait_hpq);
+
+ /* Start MP Decision slack timer */
+ if (slack_us) {
+ hrtimer_cancel(&msm_mpd.slack_timer);
+ ret = hrtimer_start(&msm_mpd.slack_timer,
+ ktime_set(0, slack_us * NSEC_PER_USEC),
+ HRTIMER_MODE_REL_PINNED);
+ if (ret)
+ pr_err("Failed to register slack timer (%d) %d\n",
+ slack_us, ret);
+ }
+
+ return ret;
+}
+
+static enum hrtimer_restart msm_mpd_slack_timer(struct hrtimer *timer)
+{
+ unsigned long flags;
+
+ trace_printk("mpd:slack_timer_fired!\n");
+
+ spin_lock_irqsave(&rq_avg_lock, flags);
+ if (msm_mpd.data.event == MSM_DCVS_SCM_RUNQ_UPDATE)
+ goto out;
+
+ msm_mpd.data.nr = 0;
+ msm_mpd.data.event = MSM_DCVS_SCM_MPD_QOS_TIMER_EXPIRED;
+ wake_up(&msm_mpd.wait_q);
+out:
+ spin_unlock_irqrestore(&rq_avg_lock, flags);
+ return HRTIMER_NORESTART;
+}
+
+static int msm_mpd_idle_notifier(struct notifier_block *self,
+ unsigned long cmd, void *v)
+{
+ int cpu = smp_processor_id();
+ unsigned long flags;
+
+ switch (cmd) {
+ case CPU_PM_EXIT:
+ spin_lock_irqsave(&rq_avg_lock, flags);
+ hrtimer_start(&per_cpu(rq_avg_poll_timer, cpu),
+ msm_mpd.next_update,
+ HRTIMER_MODE_ABS_PINNED);
+ spin_unlock_irqrestore(&rq_avg_lock, flags);
+ break;
+ case CPU_PM_ENTER:
+ hrtimer_cancel(&per_cpu(rq_avg_poll_timer, cpu));
+ break;
+ default:
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static int msm_mpd_hotplug_notifier(struct notifier_block *self,
+ unsigned long action, void *hcpu)
+{
+ int cpu = (int)hcpu;
+ unsigned long flags;
+
+ switch (action & (~CPU_TASKS_FROZEN)) {
+ case CPU_STARTING:
+ spin_lock_irqsave(&rq_avg_lock, flags);
+ hrtimer_start(&per_cpu(rq_avg_poll_timer, cpu),
+ msm_mpd.next_update,
+ HRTIMER_MODE_ABS_PINNED);
+ spin_unlock_irqrestore(&rq_avg_lock, flags);
+ break;
+ default:
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block msm_mpd_idle_nb = {
+ .notifier_call = msm_mpd_idle_notifier,
+};
+
+static struct notifier_block msm_mpd_hotplug_nb = {
+ .notifier_call = msm_mpd_hotplug_notifier,
+};
+
+static int __cpuinit msm_mpd_do_hotplug(void *data)
+{
+ int *event = (int *)data;
+ static struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
+ int cpu;
+
+ sched_setscheduler(current, SCHED_FIFO, ¶m);
+
+ while (1) {
+ wait_event(msm_mpd.wait_hpq, *event || kthread_should_stop());
+ if (kthread_should_stop())
+ break;
+
+ msm_mpd.hpupdate = HPUPDATE_IN_PROGRESS;
+ /*
+ * Bring online any offline cores, then offline any online
+ * cores. Whenever a core is off/onlined restart the procedure
+ * in case a new core is desired to be brought online in the
+ * mean time.
+ */
+restart:
+ for_each_possible_cpu(cpu) {
+ if ((atomic_read(&msm_mpd.algo_cpu_mask) & (1 << cpu))
+ && !cpu_online(cpu)) {
+ bring_up_cpu(cpu);
+ if (cpu_online(cpu))
+ goto restart;
+ }
+ }
+
+ for_each_possible_cpu(cpu) {
+ if (!(atomic_read(&msm_mpd.algo_cpu_mask) & (1 << cpu))
+ && cpu_online(cpu)) {
+ bring_down_cpu(cpu);
+ if (!cpu_online(cpu))
+ goto restart;
+ }
+ }
+ msm_mpd.hpupdate = HPUPDATE_WAITING;
+ }
+
+ return 0;
+}
+
+static int msm_mpd_do_update_scm(void *data)
+{
+ struct msm_mpd_scm_data *scm_data = (struct msm_mpd_scm_data *)data;
+ static struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
+ unsigned long flags;
+ enum msm_dcvs_scm_event event;
+ int nr;
+
+ sched_setscheduler(current, SCHED_FIFO, ¶m);
+
+ while (1) {
+ wait_event(msm_mpd.wait_q,
+ msm_mpd.data.event == MSM_DCVS_SCM_MPD_QOS_TIMER_EXPIRED
+ || msm_mpd.data.event == MSM_DCVS_SCM_RUNQ_UPDATE
+ || kthread_should_stop());
+
+ if (kthread_should_stop())
+ break;
+
+ spin_lock_irqsave(&rq_avg_lock, flags);
+ event = scm_data->event;
+ nr = scm_data->nr;
+ scm_data->event = 0;
+ scm_data->nr = 0;
+ spin_unlock_irqrestore(&rq_avg_lock, flags);
+
+ msm_mpd_update_scm(event, nr);
+ }
+ return 0;
+}
+
+static int __ref msm_mpd_set_enabled(uint32_t enable)
+{
+ int ret = 0;
+ int ret0 = 0;
+ int ret1 = 0;
+ int cpu;
+ static uint32_t last_enable;
+
+ enable = (enable > 0) ? 1 : 0;
+ if (last_enable == enable)
+ return ret;
+
+ if (enable) {
+ ret = msm_mpd_scm_set_algo_params(&msm_mpd.mp_param);
+ if (ret) {
+ pr_err("Error(%d): msm_mpd_scm_set_algo_params failed\n",
+ ret);
+ return ret;
+ }
+ }
+
+ ret = msm_dcvs_scm_event(0, MSM_DCVS_SCM_MPD_ENABLE, enable, 0,
+ &ret0, &ret1);
+ if (ret) {
+ pr_err("Error(%d) %s MP Decision\n",
+ ret, (enable ? "enabling" : "disabling"));
+ } else {
+ last_enable = enable;
+ last_nr = 0;
+ }
+ if (enable) {
+ msm_mpd.next_update = ktime_add_ns(ktime_get(),
+ (msm_mpd.rq_avg_poll_ms * NSEC_PER_MSEC));
+ msm_mpd.task = kthread_run(msm_mpd_do_update_scm,
+ &msm_mpd.data, "msm_mpdecision");
+ if (IS_ERR(msm_mpd.task))
+ return -EFAULT;
+
+ msm_mpd.hptask = kthread_run(msm_mpd_do_hotplug,
+ &msm_mpd.hpupdate, "msm_hp");
+ if (IS_ERR(msm_mpd.hptask))
+ return -EFAULT;
+
+ for_each_online_cpu(cpu)
+ hrtimer_start(&per_cpu(rq_avg_poll_timer, cpu),
+ msm_mpd.next_update,
+ HRTIMER_MODE_ABS_PINNED);
+ cpu_pm_register_notifier(&msm_mpd_idle_nb);
+ register_cpu_notifier(&msm_mpd_hotplug_nb);
+ msm_mpd.enabled = 1;
+ } else {
+ for_each_online_cpu(cpu)
+ hrtimer_cancel(&per_cpu(rq_avg_poll_timer, cpu));
+ kthread_stop(msm_mpd.hptask);
+ kthread_stop(msm_mpd.task);
+ cpu_pm_unregister_notifier(&msm_mpd_idle_nb);
+ unregister_cpu_notifier(&msm_mpd_hotplug_nb);
+ msm_mpd.enabled = 0;
+ }
+
+ return ret;
+}
+
+static int msm_mpd_set_rq_avg_poll_ms(uint32_t val)
+{
+ /*
+ * No need to do anything. Just let the timer set its own next poll
+ * interval when it next fires.
+ */
+ msm_mpd.rq_avg_poll_ms = val;
+ return 0;
+}
+
+static int msm_mpd_set_iowait_threshold_pct(uint32_t val)
+{
+ /*
+ * No need to do anything. Just let the timer set its own next poll
+ * interval when it next fires.
+ */
+ msm_mpd.iowait_threshold_pct = val;
+ return 0;
+}
+
+#define MPD_ALGO_PARAM(_name, _param) \
+static ssize_t msm_mpd_attr_##_name##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, char *buf) \
+{ \
+ return snprintf(buf, PAGE_SIZE, "%d\n", _param); \
+} \
+static ssize_t msm_mpd_attr_##_name##_store(struct kobject *kobj, \
+ struct kobj_attribute *attr, const char *buf, size_t count) \
+{ \
+ int ret = 0; \
+ uint32_t val; \
+ uint32_t old_val; \
+ mutex_lock(&msm_mpd.lock); \
+ ret = kstrtouint(buf, 10, &val); \
+ if (ret) { \
+ pr_err("Invalid input %s for %s %d\n", \
+ buf, __stringify(_name), ret);\
+ return 0; \
+ } \
+ old_val = _param; \
+ _param = val; \
+ ret = msm_mpd_scm_set_algo_params(&msm_mpd.mp_param); \
+ if (ret) { \
+ pr_err("Error %d returned when setting algo param %s to %d\n",\
+ ret, __stringify(_name), val); \
+ _param = old_val; \
+ } \
+ mutex_unlock(&msm_mpd.lock); \
+ return count; \
+}
+
+#define MPD_PARAM(_name, _param) \
+static ssize_t msm_mpd_attr_##_name##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, char *buf) \
+{ \
+ return snprintf(buf, PAGE_SIZE, "%d\n", _param); \
+} \
+static ssize_t msm_mpd_attr_##_name##_store(struct kobject *kobj, \
+ struct kobj_attribute *attr, const char *buf, size_t count) \
+{ \
+ int ret = 0; \
+ uint32_t val; \
+ uint32_t old_val; \
+ mutex_lock(&msm_mpd.lock); \
+ ret = kstrtouint(buf, 10, &val); \
+ if (ret) { \
+ pr_err("Invalid input %s for %s %d\n", \
+ buf, __stringify(_name), ret);\
+ return 0; \
+ } \
+ old_val = _param; \
+ ret = msm_mpd_set_##_name(val); \
+ if (ret) { \
+ pr_err("Error %d returned when setting algo param %s to %d\n",\
+ ret, __stringify(_name), val); \
+ _param = old_val; \
+ } \
+ mutex_unlock(&msm_mpd.lock); \
+ return count; \
+}
+
+#define MPD_RW_ATTRIB(i, _name) \
+ msm_mpd.attrib._name.attr.name = __stringify(_name); \
+ msm_mpd.attrib._name.attr.mode = S_IRUGO | S_IWUSR; \
+ msm_mpd.attrib._name.show = msm_mpd_attr_##_name##_show; \
+ msm_mpd.attrib._name.store = msm_mpd_attr_##_name##_store; \
+ msm_mpd.attrib.attrib_group.attrs[i] = &msm_mpd.attrib._name.attr;
+
+MPD_PARAM(enabled, msm_mpd.enabled);
+MPD_PARAM(rq_avg_poll_ms, msm_mpd.rq_avg_poll_ms);
+MPD_PARAM(iowait_threshold_pct, msm_mpd.iowait_threshold_pct);
+MPD_ALGO_PARAM(em_win_size_min_us, msm_mpd.mp_param.em_win_size_min_us);
+MPD_ALGO_PARAM(em_win_size_max_us, msm_mpd.mp_param.em_win_size_max_us);
+MPD_ALGO_PARAM(em_max_util_pct, msm_mpd.mp_param.em_max_util_pct);
+MPD_ALGO_PARAM(mp_em_rounding_point_min,
+ msm_mpd.mp_param.mp_em_rounding_point_min);
+MPD_ALGO_PARAM(mp_em_rounding_point_max,
+ msm_mpd.mp_param.mp_em_rounding_point_max);
+MPD_ALGO_PARAM(online_util_pct_min, msm_mpd.mp_param.online_util_pct_min);
+MPD_ALGO_PARAM(online_util_pct_max, msm_mpd.mp_param.online_util_pct_max);
+MPD_ALGO_PARAM(slack_time_min_us, msm_mpd.mp_param.slack_time_min_us);
+MPD_ALGO_PARAM(slack_time_max_us, msm_mpd.mp_param.slack_time_max_us);
+MPD_ALGO_PARAM(hp_up_max_ms, hp_latencies.hp_up_max_ms);
+MPD_ALGO_PARAM(hp_up_ms, hp_latencies.hp_up_ms);
+MPD_ALGO_PARAM(hp_up_count, hp_latencies.hp_up_count);
+MPD_ALGO_PARAM(hp_dw_max_ms, hp_latencies.hp_dw_max_ms);
+MPD_ALGO_PARAM(hp_dw_ms, hp_latencies.hp_dw_ms);
+MPD_ALGO_PARAM(hp_dw_count, hp_latencies.hp_dw_count);
+
+static int __devinit msm_mpd_probe(struct platform_device *pdev)
+{
+ struct kobject *module_kobj = NULL;
+ int ret = 0;
+ const int attr_count = 19;
+ struct msm_mpd_algo_param *param = NULL;
+
+ param = pdev->dev.platform_data;
+
+ module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
+ if (!module_kobj) {
+ pr_err("Cannot find kobject for module %s\n", KBUILD_MODNAME);
+ ret = -ENOENT;
+ goto done;
+ }
+
+ msm_mpd.attrib.attrib_group.attrs =
+ kzalloc(attr_count * sizeof(struct attribute *), GFP_KERNEL);
+ if (!msm_mpd.attrib.attrib_group.attrs) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ MPD_RW_ATTRIB(0, enabled);
+ MPD_RW_ATTRIB(1, rq_avg_poll_ms);
+ MPD_RW_ATTRIB(2, iowait_threshold_pct);
+ MPD_RW_ATTRIB(3, em_win_size_min_us);
+ MPD_RW_ATTRIB(4, em_win_size_max_us);
+ MPD_RW_ATTRIB(5, em_max_util_pct);
+ MPD_RW_ATTRIB(6, mp_em_rounding_point_min);
+ MPD_RW_ATTRIB(7, mp_em_rounding_point_max);
+ MPD_RW_ATTRIB(8, online_util_pct_min);
+ MPD_RW_ATTRIB(9, online_util_pct_max);
+ MPD_RW_ATTRIB(10, slack_time_min_us);
+ MPD_RW_ATTRIB(11, slack_time_max_us);
+ MPD_RW_ATTRIB(12, hp_up_max_ms);
+ MPD_RW_ATTRIB(13, hp_up_ms);
+ MPD_RW_ATTRIB(14, hp_up_count);
+ MPD_RW_ATTRIB(15, hp_dw_max_ms);
+ MPD_RW_ATTRIB(16, hp_dw_ms);
+ MPD_RW_ATTRIB(17, hp_dw_count);
+
+ msm_mpd.attrib.attrib_group.attrs[18] = NULL;
+ ret = sysfs_create_group(module_kobj, &msm_mpd.attrib.attrib_group);
+ if (ret)
+ pr_err("Unable to create sysfs objects :%d\n", ret);
+
+ msm_mpd.rq_avg_poll_ms = DEFAULT_RQ_AVG_POLL_MS;
+
+ memcpy(&msm_mpd.mp_param, param, sizeof(struct msm_mpd_algo_param));
+
+ debugfs_base = debugfs_create_dir("msm_mpdecision", NULL);
+ if (!debugfs_base) {
+ pr_err("Cannot create debugfs base msm_mpdecision\n");
+ ret = -ENOENT;
+ goto done;
+ }
+
+done:
+ if (ret && debugfs_base)
+ debugfs_remove(debugfs_base);
+
+ return ret;
+}
+
+static int __devexit msm_mpd_remove(struct platform_device *pdev)
+{
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver msm_mpd_driver = {
+ .probe = msm_mpd_probe,
+ .remove = __devexit_p(msm_mpd_remove),
+ .driver = {
+ .name = "msm_mpdecision",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_mpdecision_init(void)
+{
+ int cpu;
+ if (!msm_mpd_enabled) {
+ pr_info("Not enabled\n");
+ return 0;
+ }
+
+ num_present_hundreds = 100 * num_present_cpus();
+
+ hrtimer_init(&msm_mpd.slack_timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL_PINNED);
+ msm_mpd.slack_timer.function = msm_mpd_slack_timer;
+
+ for_each_possible_cpu(cpu) {
+ hrtimer_init(&per_cpu(rq_avg_poll_timer, cpu),
+ CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED);
+ per_cpu(rq_avg_poll_timer, cpu).function
+ = msm_mpd_rq_avg_poll_timer;
+ }
+ mutex_init(&msm_mpd.lock);
+ init_waitqueue_head(&msm_mpd.wait_q);
+ init_waitqueue_head(&msm_mpd.wait_hpq);
+ return platform_driver_register(&msm_mpd_driver);
+}
+late_initcall(msm_mpdecision_init);
diff --git a/arch/arm/mach-msm/msm_smem_iface.c b/arch/arm/mach-msm/msm_smem_iface.c
index b09fda5..b35467b 100644
--- a/arch/arm/mach-msm/msm_smem_iface.c
+++ b/arch/arm/mach-msm/msm_smem_iface.c
@@ -41,4 +41,5 @@
cpr_info->ring_osc = temp_cpr_info->ring_osc;
cpr_info->turbo_quot = temp_cpr_info->turbo_quot;
cpr_info->pvs_fuse = temp_cpr_info->pvs_fuse;
+ cpr_info->floor_fuse = temp_cpr_info->floor_fuse;
}
diff --git a/arch/arm/mach-msm/msm_smem_iface.h b/arch/arm/mach-msm/msm_smem_iface.h
index 2da0232..a6d6714 100644
--- a/arch/arm/mach-msm/msm_smem_iface.h
+++ b/arch/arm/mach-msm/msm_smem_iface.h
@@ -33,10 +33,12 @@
uint8_t iv[MAX_SEC_KEY_PAYLOAD]; /* Initialization Vector */
};
+/* floor_fuse to re-use the fuse bit earlier used by ring_osc */
struct cpr_info_type {
- uint8_t ring_osc; /* CPR FUSE [0]: TURBO RO SEL BIT */
- uint8_t turbo_quot; /* CPRFUSE[1:7] : TURBO QUOT*/
- uint8_t pvs_fuse; /* TURBO PVS FUSE */
+ uint8_t ring_osc; /* CPR FUSE [0]: TURBO RO SEL BIT */
+ uint8_t turbo_quot; /* CPRFUSE[1:7] : TURBO QUOT*/
+ uint8_t pvs_fuse; /* TURBO PVS FUSE */
+ uint8_t floor_fuse; /* Vmin Selection. b1: FAB_ID(2), b0: CPR_fuse[0] */
};
struct boot_info_for_apps {
@@ -47,7 +49,7 @@
uint16_t boot_keys_pressed[MAX_KEY_EVENTS]; /* Log of key presses */
uint32_t timetick; /* Modem tick timer value before apps out of reset */
struct cpr_info_type cpr_info;
- uint8_t PAD[25];
+ uint8_t PAD[24];
};
void msm_smem_get_cpr_info(struct cpr_info_type *cpr_info);
diff --git a/arch/arm/mach-msm/msm_watchdog.h b/arch/arm/mach-msm/msm_watchdog.h
index 5fb82ee..7bf97d9 100644
--- a/arch/arm/mach-msm/msm_watchdog.h
+++ b/arch/arm/mach-msm/msm_watchdog.h
@@ -72,6 +72,7 @@
void msm_wdog_fiq_setup(void *stack);
extern unsigned int msm_wdog_fiq_length, msm_wdog_fiq_start;
+extern unsigned int msm7k_fiq_start, msm7k_fiq_length;
#ifdef CONFIG_MSM_WATCHDOG
void pet_watchdog(void);
diff --git a/arch/arm/mach-msm/msm_watchdog_v2.c b/arch/arm/mach-msm/msm_watchdog_v2.c
index 48a57cd..f88c611 100644
--- a/arch/arm/mach-msm/msm_watchdog_v2.c
+++ b/arch/arm/mach-msm/msm_watchdog_v2.c
@@ -228,13 +228,19 @@
static void pet_watchdog(struct msm_watchdog_data *wdog_dd)
{
- int slack;
+ int slack, i, count, prev_count = 0;
unsigned long long time_ns;
unsigned long long slack_ns;
unsigned long long bark_time_ns = wdog_dd->bark_time * 1000000ULL;
- slack = __raw_readl(wdog_dd->base + WDT0_STS) >> 3;
- slack = ((wdog_dd->bark_time*WDT_HZ)/1000) - slack;
+ for (i = 0; i < 2; i++) {
+ count = (__raw_readl(wdog_dd->base + WDT0_STS) >> 1) & 0xFFFFF;
+ if (count != prev_count) {
+ prev_count = count;
+ i = 0;
+ }
+ }
+ slack = ((wdog_dd->bark_time * WDT_HZ) / 1000) - count;
if (slack < wdog_dd->min_slack_ticks)
wdog_dd->min_slack_ticks = slack;
__raw_writel(1, wdog_dd->base + WDT0_RST);
diff --git a/arch/arm/mach-msm/msm_xo.c b/arch/arm/mach-msm/msm_xo.c
index 404b350..46d4a12 100644
--- a/arch/arm/mach-msm/msm_xo.c
+++ b/arch/arm/mach-msm/msm_xo.c
@@ -233,10 +233,9 @@
int ret;
struct msm_xo *xo = xo_voter->xo;
int is_d0 = xo == &msm_xo_sources[MSM_XO_TCXO_D0];
- int needs_workaround = cpu_is_msm8960() || cpu_is_apq8064() ||
- cpu_is_msm8930() || cpu_is_msm8930aa() ||
- cpu_is_msm9615() || cpu_is_msm8627() ||
- cpu_is_msm8960ab() || cpu_is_apq8064ab();
+ int needs_workaround = soc_class_is_msm8960() ||
+ soc_class_is_apq8064() ||
+ soc_class_is_msm8930() || cpu_is_msm9615();
if (xo_voter->mode == mode)
return 0;
diff --git a/arch/arm/mach-msm/peripheral-loader.h b/arch/arm/mach-msm/peripheral-loader.h
index e3b250b..405b73f 100644
--- a/arch/arm/mach-msm/peripheral-loader.h
+++ b/arch/arm/mach-msm/peripheral-loader.h
@@ -55,7 +55,15 @@
struct pil_device;
+#ifdef CONFIG_MSM_PIL
extern struct pil_device *msm_pil_register(struct pil_desc *desc);
extern void msm_pil_unregister(struct pil_device *pil);
+#else
+static inline struct pil_device *msm_pil_register(struct pil_desc *desc)
+{
+ return NULL;
+}
+static inline void msm_pil_unregister(struct pil_device *pil) { }
+#endif
#endif
diff --git a/arch/arm/mach-msm/pil-dsps.c b/arch/arm/mach-msm/pil-dsps.c
index 81f5330..dc80a3a 100644
--- a/arch/arm/mach-msm/pil-dsps.c
+++ b/arch/arm/mach-msm/pil-dsps.c
@@ -17,11 +17,17 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/delay.h>
+#include <linux/atomic.h>
+#include <linux/interrupt.h>
#include <mach/msm_iomap.h>
+#include <mach/subsystem_restart.h>
+#include <mach/msm_smsm.h>
+#include <mach/peripheral-loader.h>
#include "peripheral-loader.h"
#include "scm-pas.h"
+#include "ramdump.h"
#define PPSS_RESET (MSM_CLK_CTL_BASE + 0x2594)
#define PPSS_RESET_PROC_RESET 0x2
@@ -31,6 +37,28 @@
#define PPSS_HCLK_CTL (MSM_CLK_CTL_BASE + 0x2580)
#define CLK_HALT_DFAB_STATE (MSM_CLK_CTL_BASE + 0x2FC8)
+#define PPSS_WDOG_UNMASKED_INT_EN 0x1808
+
+struct dsps_data {
+ struct pil_device *pil;
+ struct pil_desc desc;
+ struct subsys_device *subsys;
+ struct subsys_desc subsys_desc;
+ int crash;
+ int wdog_irq;
+ atomic_t wd_crash;
+ atomic_t crash_in_progress;
+ void __iomem *ppss_base;
+
+ void *ramdump_dev;
+ struct ramdump_segment fw_ramdump_segments[4];
+
+ void *smem_ramdump_dev;
+ struct ramdump_segment smem_ramdump_segments[1];
+};
+
+#define desc_to_drv(d) container_of(d, struct dsps_data, subsys_desc)
+
static int init_image_dsps(struct pil_desc *pil, const u8 *metadata,
size_t size)
{
@@ -88,15 +116,143 @@
.shutdown = shutdown_dsps_trusted,
};
+static void dsps_log_sfr(void)
+{
+ const char dflt_reason[] = "Died too early due to unknown reason";
+ char *smem_reset_reason;
+ unsigned smem_reset_size;
+
+ smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_DSPS0,
+ &smem_reset_size);
+ if (smem_reset_reason != NULL && smem_reset_reason[0] != 0) {
+ smem_reset_reason[smem_reset_size-1] = 0;
+ pr_err("%s: DSPS failure: %s\nResetting DSPS\n",
+ __func__, smem_reset_reason);
+ memset(smem_reset_reason, 0, smem_reset_size);
+ wmb();
+ } else
+ pr_err("%s: DSPS failure: %s\nResetting DSPS\n",
+ __func__, dflt_reason);
+}
+
+
+static void dsps_restart_handler(struct dsps_data *drv)
+{
+ pr_debug("%s: Restart lvl %d\n",
+ __func__, get_restart_level());
+
+ if (atomic_add_return(1, &drv->crash_in_progress) > 1) {
+ pr_err("%s: DSPS already resetting. Count %d\n", __func__,
+ atomic_read(&drv->crash_in_progress));
+ } else {
+ subsystem_restart_dev(drv->subsys);
+ }
+}
+
+static void dsps_smsm_state_cb(void *data, uint32_t old_state,
+ uint32_t new_state)
+{
+ struct dsps_data *drv = data;
+
+ if (drv->crash == 1) {
+ pr_debug("SMSM_RESET state change ignored\n");
+ drv->crash = 0;
+ } else if (new_state & SMSM_RESET) {
+ dsps_log_sfr();
+ dsps_restart_handler(drv);
+ }
+}
+
+static int dsps_shutdown(const struct subsys_desc *desc)
+{
+ struct dsps_data *drv = desc_to_drv(desc);
+ disable_irq_nosync(drv->wdog_irq);
+ if (drv->ppss_base) {
+ writel_relaxed(0, drv->ppss_base + PPSS_WDOG_UNMASKED_INT_EN);
+ mb(); /* Make sure wdog is disabled before shutting down */
+ }
+ pil_force_shutdown(drv->desc.name);
+ return 0;
+}
+
+static int dsps_powerup(const struct subsys_desc *desc)
+{
+ struct dsps_data *drv = desc_to_drv(desc);
+
+ pil_force_boot(drv->desc.name);
+ atomic_set(&drv->crash_in_progress, 0);
+ enable_irq(drv->wdog_irq);
+
+ return 0;
+}
+
+static int dsps_ramdump(int enable, const struct subsys_desc *desc)
+{
+ int ret;
+ struct dsps_data *drv = desc_to_drv(desc);
+
+ if (!enable)
+ return 0;
+
+ ret = do_ramdump(drv->ramdump_dev,
+ drv->fw_ramdump_segments,
+ ARRAY_SIZE(drv->fw_ramdump_segments));
+ if (ret < 0) {
+ pr_err("%s: Unable to dump DSPS memory (rc = %d).\n",
+ __func__, ret);
+ return ret;
+ }
+ ret = do_ramdump(drv->smem_ramdump_dev,
+ drv->smem_ramdump_segments,
+ ARRAY_SIZE(drv->smem_ramdump_segments));
+ if (ret < 0) {
+ pr_err("%s: Unable to dump smem memory (rc = %d).\n",
+ __func__, ret);
+ return ret;
+ }
+ return 0;
+}
+
+static void dsps_crash_shutdown(const struct subsys_desc *desc)
+{
+ struct dsps_data *drv = desc_to_drv(desc);
+
+ disable_irq_nosync(drv->wdog_irq);
+ drv->crash = 1;
+ smsm_change_state(SMSM_DSPS_STATE, SMSM_RESET, SMSM_RESET);
+}
+
+static irqreturn_t dsps_wdog_bite_irq(int irq, void *dev_id)
+{
+ struct dsps_data *drv = dev_id;
+
+ atomic_set(&drv->wd_crash, 1);
+ dsps_log_sfr();
+ dsps_restart_handler(drv);
+ return IRQ_HANDLED;
+}
+
static int __devinit pil_dsps_driver_probe(struct platform_device *pdev)
{
+ struct dsps_data *drv;
struct pil_desc *desc;
- struct pil_device *pil;
+ int ret;
+ struct resource *res;
- desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
- if (!desc)
+ drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv)
return -ENOMEM;
+ platform_set_drvdata(pdev, drv);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res) {
+ drv->ppss_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!drv->ppss_base)
+ return -ENOMEM;
+ }
+
+ desc = &drv->desc;
desc->name = pdev->dev.platform_data;
desc->dev = &pdev->dev;
desc->owner = THIS_MODULE;
@@ -107,17 +263,85 @@
desc->ops = &pil_dsps_ops;
dev_info(&pdev->dev, "using non-secure boot\n");
}
- pil = msm_pil_register(desc);
- if (IS_ERR(pil))
- return PTR_ERR(pil);
- platform_set_drvdata(pdev, pil);
+ drv->pil = msm_pil_register(desc);
+ if (IS_ERR(drv->pil))
+ return PTR_ERR(drv->pil);
+
+ drv->fw_ramdump_segments[0].address = 0x12000000;
+ drv->fw_ramdump_segments[0].size = 0x28000;
+ drv->fw_ramdump_segments[1].address = 0x12040000;
+ drv->fw_ramdump_segments[1].size = 0x4000;
+ drv->fw_ramdump_segments[2].address = 0x12800000;
+ drv->fw_ramdump_segments[2].size = 0x4000;
+ drv->fw_ramdump_segments[3].address = 0x8fe00000;
+ drv->fw_ramdump_segments[3].size = 0x100000;
+ drv->ramdump_dev = create_ramdump_device("dsps");
+ if (!drv->ramdump_dev) {
+ ret = -ENOMEM;
+ goto err_ramdump;
+ }
+
+ drv->smem_ramdump_segments[0].address = PHYS_OFFSET - SZ_2M;
+ drv->smem_ramdump_segments[0].size = SZ_2M;
+ drv->smem_ramdump_dev = create_ramdump_device("smem-dsps");
+ if (!drv->smem_ramdump_dev) {
+ ret = -ENOMEM;
+ goto err_smem_ramdump;
+ }
+
+ drv->subsys_desc.name = "dsps";
+ drv->subsys_desc.shutdown = dsps_shutdown;
+ drv->subsys_desc.powerup = dsps_powerup;
+ drv->subsys_desc.ramdump = dsps_ramdump,
+ drv->subsys_desc.crash_shutdown = dsps_crash_shutdown;
+
+ drv->subsys = subsys_register(&drv->subsys_desc);
+ if (IS_ERR(drv->subsys)) {
+ ret = PTR_ERR(drv->subsys);
+ goto err_subsys;
+ }
+
+ ret = smsm_state_cb_register(SMSM_DSPS_STATE, SMSM_RESET,
+ dsps_smsm_state_cb, drv);
+ if (ret)
+ goto err_smsm;
+
+ drv->wdog_irq = platform_get_irq(pdev, 0);
+ if (drv->wdog_irq >= 0) {
+ ret = devm_request_irq(&pdev->dev, drv->wdog_irq,
+ dsps_wdog_bite_irq, IRQF_TRIGGER_RISING,
+ "dsps_wdog", drv);
+ if (ret) {
+ dev_err(&pdev->dev, "request_irq failed\n");
+ goto err_smsm;
+ }
+ } else {
+ drv->wdog_irq = -1;
+ dev_dbg(&pdev->dev, "ppss_wdog not supported\n");
+ }
+
return 0;
+
+err_smsm:
+ subsys_unregister(drv->subsys);
+err_subsys:
+ destroy_ramdump_device(drv->smem_ramdump_dev);
+err_smem_ramdump:
+ destroy_ramdump_device(drv->ramdump_dev);
+err_ramdump:
+ msm_pil_unregister(drv->pil);
+ return ret;
}
static int __devexit pil_dsps_driver_exit(struct platform_device *pdev)
{
- struct pil_device *pil = platform_get_drvdata(pdev);
- msm_pil_unregister(pil);
+ struct dsps_data *drv = platform_get_drvdata(pdev);
+ smsm_state_cb_deregister(SMSM_DSPS_STATE, SMSM_RESET,
+ dsps_smsm_state_cb, drv);
+ subsys_unregister(drv->subsys);
+ destroy_ramdump_device(drv->smem_ramdump_dev);
+ destroy_ramdump_device(drv->ramdump_dev);
+ msm_pil_unregister(drv->pil);
return 0;
}
diff --git a/arch/arm/mach-msm/pil-gss.c b/arch/arm/mach-msm/pil-gss.c
index 73248db..bccbce2 100644
--- a/arch/arm/mach-msm/pil-gss.c
+++ b/arch/arm/mach-msm/pil-gss.c
@@ -21,15 +21,22 @@
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/smp.h>
+#include <linux/miscdevice.h>
+#include <linux/reboot.h>
+#include <linux/interrupt.h>
#include <mach/msm_iomap.h>
#include <mach/msm_xo.h>
#include <mach/socinfo.h>
#include <mach/msm_bus_board.h>
#include <mach/msm_bus.h>
+#include <mach/subsystem_restart.h>
+#include <mach/peripheral-loader.h>
#include "peripheral-loader.h"
#include "scm-pas.h"
+#include "smd_private.h"
+#include "ramdump.h"
#define GSS_CSR_AHB_CLK_SEL 0x0
#define GSS_CSR_RESET 0x4
@@ -63,6 +70,14 @@
unsigned long start_addr;
struct clk *xo;
struct pil_device *pil;
+ struct miscdevice misc_dev;
+ struct subsys_device *subsys;
+ struct subsys_desc subsys_desc;
+ int crash_shutdown;
+ int irq;
+ void *pil_handle;
+ struct ramdump_device *ramdump_dev;
+ struct ramdump_device *smem_ramdump_dev;
};
static int pil_gss_init_image(struct pil_desc *pil, const u8 *metadata,
@@ -308,11 +323,160 @@
.proxy_unvote = remove_gss_proxy_votes,
};
+#define MAX_SSR_REASON_LEN 81U
+
+static void log_gss_sfr(void)
+{
+ u32 size;
+ char *smem_reason, reason[MAX_SSR_REASON_LEN];
+
+ smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size);
+ if (!smem_reason || !size) {
+ pr_err("GSS subsystem failure reason: (unknown, smem_get_entry failed).\n");
+ return;
+ }
+ if (!smem_reason[0]) {
+ pr_err("GSS subsystem failure reason: (unknown, init string found).\n");
+ return;
+ }
+
+ size = min(size, MAX_SSR_REASON_LEN-1);
+ memcpy(reason, smem_reason, size);
+ reason[size] = '\0';
+ pr_err("GSS subsystem failure reason: %s.\n", reason);
+
+ smem_reason[0] = '\0';
+ wmb();
+}
+
+static void restart_gss(struct gss_data *drv)
+{
+ log_gss_sfr();
+ subsystem_restart_dev(drv->subsys);
+}
+
+static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
+{
+ struct gss_data *drv = data;
+
+ /* Ignore if we're the one that set SMSM_RESET */
+ if (drv->crash_shutdown)
+ return;
+
+ if (new_state & SMSM_RESET) {
+ pr_err("GSS SMSM state changed to SMSM_RESET.\n"
+ "Probable err_fatal on the GSS. "
+ "Calling subsystem restart...\n");
+ restart_gss(drv);
+ }
+}
+
+static int gss_shutdown(const struct subsys_desc *desc)
+{
+ struct gss_data *drv = container_of(desc, struct gss_data, subsys_desc);
+
+ pil_force_shutdown("gss");
+ disable_irq_nosync(drv->irq);
+
+ return 0;
+}
+
+static int gss_powerup(const struct subsys_desc *desc)
+{
+ struct gss_data *drv = container_of(desc, struct gss_data, subsys_desc);
+
+ pil_force_boot("gss");
+ enable_irq(drv->irq);
+ return 0;
+}
+
+void gss_crash_shutdown(const struct subsys_desc *desc)
+{
+ struct gss_data *drv = container_of(desc, struct gss_data, subsys_desc);
+
+ drv->crash_shutdown = 1;
+ smsm_reset_modem(SMSM_RESET);
+}
+
+/* FIXME: Get address, size from PIL */
+static struct ramdump_segment gss_segments[] = {
+ {0x89000000, 0x00D00000}
+};
+
+static struct ramdump_segment smem_segments[] = {
+ {0x80000000, 0x00200000},
+};
+
+static int gss_ramdump(int enable, const struct subsys_desc *desc)
+{
+ int ret;
+ struct gss_data *drv = container_of(desc, struct gss_data, subsys_desc);
+
+ if (enable) {
+ ret = do_ramdump(drv->ramdump_dev, gss_segments,
+ ARRAY_SIZE(gss_segments));
+ if (ret < 0) {
+ pr_err("Unable to dump gss memory\n");
+ return ret;
+ }
+
+ ret = do_ramdump(drv->smem_ramdump_dev, smem_segments,
+ ARRAY_SIZE(smem_segments));
+ if (ret < 0) {
+ pr_err("Unable to dump smem memory (rc = %d).\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static irqreturn_t gss_wdog_bite_irq(int irq, void *dev_id)
+{
+ struct gss_data *drv = dev_id;
+
+ pr_err("Watchdog bite received from GSS!\n");
+ restart_gss(drv);
+
+ return IRQ_HANDLED;
+}
+
+static int gss_open(struct inode *inode, struct file *filp)
+{
+ void *ret;
+ struct miscdevice *c = filp->private_data;
+ struct gss_data *drv = container_of(c, struct gss_data, misc_dev);
+
+ drv->pil_handle = ret = pil_get("gss");
+ if (!ret)
+ pr_debug("%s - pil_get returned NULL\n", __func__);
+
+ return 0;
+}
+
+static int gss_release(struct inode *inode, struct file *filp)
+{
+ struct miscdevice *c = filp->private_data;
+ struct gss_data *drv = container_of(c, struct gss_data, misc_dev);
+
+ pil_put(drv->pil_handle);
+ pr_debug("%s pil_put called on GSS\n", __func__);
+
+ return 0;
+}
+
+const struct file_operations gss_file_ops = {
+ .open = gss_open,
+ .release = gss_release,
+ .owner = THIS_MODULE,
+};
+
static int __devinit pil_gss_probe(struct platform_device *pdev)
{
struct gss_data *drv;
struct resource *res;
struct pil_desc *desc;
+ int ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
@@ -344,6 +508,10 @@
if (IS_ERR(drv->xo))
return PTR_ERR(drv->xo);
+ drv->irq = platform_get_irq(pdev, 0);
+ if (drv->irq < 0)
+ return drv->irq;
+
desc->name = "gss";
desc->dev = &pdev->dev;
desc->owner = THIS_MODULE;
@@ -363,13 +531,71 @@
if (IS_ERR(drv->pil)) {
return PTR_ERR(drv->pil);
}
+
+ ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
+ smsm_state_cb, drv);
+ if (ret < 0)
+ dev_warn(&pdev->dev, "Unable to register SMSM callback\n");
+
+ drv->subsys_desc.name = "gss";
+ drv->subsys_desc.shutdown = gss_shutdown;
+ drv->subsys_desc.powerup = gss_powerup;
+ drv->subsys_desc.ramdump = gss_ramdump;
+ drv->subsys_desc.crash_shutdown = gss_crash_shutdown;
+
+ drv->subsys = subsys_register(&drv->subsys_desc);
+ if (IS_ERR(drv->subsys)) {
+ ret = PTR_ERR(drv->subsys);
+ goto err_subsys;
+ }
+
+ drv->misc_dev.minor = MISC_DYNAMIC_MINOR;
+ drv->misc_dev.name = "gss";
+ drv->misc_dev.fops = &gss_file_ops;
+ ret = misc_register(&drv->misc_dev);
+ if (ret)
+ goto err_misc;
+
+ drv->ramdump_dev = create_ramdump_device("gss");
+ if (!drv->ramdump_dev) {
+ ret = -ENOMEM;
+ goto err_ramdump;
+ }
+
+ drv->smem_ramdump_dev = create_ramdump_device("smem-gss");
+ if (!drv->smem_ramdump_dev) {
+ ret = -ENOMEM;
+ goto err_smem;
+ }
+
+ ret = devm_request_irq(&pdev->dev, drv->irq, gss_wdog_bite_irq,
+ IRQF_TRIGGER_RISING, "gss_a5_wdog", drv);
+ if (ret < 0)
+ goto err;
return 0;
+err:
+ destroy_ramdump_device(drv->smem_ramdump_dev);
+err_smem:
+ destroy_ramdump_device(drv->ramdump_dev);
+err_ramdump:
+ misc_deregister(&drv->misc_dev);
+err_misc:
+ subsys_unregister(drv->subsys);
+err_subsys:
+ msm_pil_unregister(drv->pil);
+ return ret;
}
static int __devexit pil_gss_remove(struct platform_device *pdev)
{
struct gss_data *drv = platform_get_drvdata(pdev);
+
+ destroy_ramdump_device(drv->smem_ramdump_dev);
+ destroy_ramdump_device(drv->ramdump_dev);
+ misc_deregister(&drv->misc_dev);
+ subsys_unregister(drv->subsys);
msm_pil_unregister(drv->pil);
+
return 0;
}
diff --git a/arch/arm/mach-msm/pil-mba.c b/arch/arm/mach-msm/pil-mba.c
index 0207f0b..8432328 100644
--- a/arch/arm/mach-msm/pil-mba.c
+++ b/arch/arm/mach-msm/pil-mba.c
@@ -23,8 +23,14 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/of.h>
+#include <linux/interrupt.h>
+
+#include <mach/subsystem_restart.h>
+#include <mach/msm_smsm.h>
+#include <mach/peripheral-loader.h>
#include "peripheral-loader.h"
+#include "ramdump.h"
#define RMB_MBA_COMMAND 0x08
#define RMB_MBA_STATUS 0x0C
@@ -41,6 +47,8 @@
#define PROXY_TIMEOUT_MS 10000
#define POLL_INTERVAL_US 50
+#define MAX_SSR_REASON_LEN 81U
+
static int modem_auth_timeout_ms = 10000;
module_param(modem_auth_timeout_ms, int, S_IRUGO | S_IWUSR);
@@ -49,7 +57,13 @@
void __iomem *metadata_base;
unsigned long metadata_phys;
struct pil_device *pil;
+ struct subsys_device *subsys;
+ struct subsys_desc subsys_desc;
struct clk *xo;
+ void *ramdump_dev;
+ void *smem_ramdump_dev;
+ bool crash_shutdown;
+ bool ignore_errors;
u32 img_length;
};
@@ -160,18 +174,142 @@
.shutdown = pil_mba_shutdown,
};
+#define subsys_to_drv(d) container_of(d, struct mba_data, subsys_desc)
+
+static void log_modem_sfr(void)
+{
+ u32 size;
+ char *smem_reason, reason[MAX_SSR_REASON_LEN];
+
+ smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size);
+ if (!smem_reason || !size) {
+ pr_err("modem subsystem failure reason: (unknown, smem_get_entry failed).\n");
+ return;
+ }
+ if (!smem_reason[0]) {
+ pr_err("modem subsystem failure reason: (unknown, empty string found).\n");
+ return;
+ }
+
+ strlcpy(reason, smem_reason, min(size, sizeof(reason)));
+ pr_err("modem subsystem failure reason: %s.\n", reason);
+
+ smem_reason[0] = '\0';
+ wmb();
+}
+
+static void restart_modem(struct mba_data *drv)
+{
+ log_modem_sfr();
+ drv->ignore_errors = true;
+ subsystem_restart_dev(drv->subsys);
+}
+
+static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
+{
+ struct mba_data *drv = data;
+
+ /* Ignore if we're the one that set SMSM_RESET */
+ if (drv->crash_shutdown)
+ return;
+
+ if (new_state & SMSM_RESET) {
+ pr_err("Probable fatal error on the modem.\n");
+ restart_modem(drv);
+ }
+}
+
+static int modem_shutdown(const struct subsys_desc *subsys)
+{
+ pil_force_shutdown("modem");
+ pil_force_shutdown("mba");
+ return 0;
+}
+
+static int modem_powerup(const struct subsys_desc *subsys)
+{
+ struct mba_data *drv = subsys_to_drv(subsys);
+ /*
+ * At this time, the modem is shutdown. Therefore this function cannot
+ * run concurrently with either the watchdog bite error handler or the
+ * SMSM callback, making it safe to unset the flag below.
+ */
+ drv->ignore_errors = 0;
+ pil_force_boot("mba");
+ pil_force_boot("modem");
+ return 0;
+}
+
+static void modem_crash_shutdown(const struct subsys_desc *subsys)
+{
+ struct mba_data *drv = subsys_to_drv(subsys);
+ drv->crash_shutdown = true;
+ smsm_reset_modem(SMSM_RESET);
+}
+
+static struct ramdump_segment modem_segments[] = {
+ {0x08400000, 0x0D100000 - 0x08400000},
+};
+
+static struct ramdump_segment smem_segments[] = {
+ {0x0FA00000, 0x0FC00000 - 0x0FA00000},
+};
+
+static int modem_ramdump(int enable, const struct subsys_desc *subsys)
+{
+ struct mba_data *drv = subsys_to_drv(subsys);
+ int ret;
+
+ if (!enable)
+ return 0;
+
+ pil_force_boot("mba");
+
+ ret = do_ramdump(drv->ramdump_dev, modem_segments,
+ ARRAY_SIZE(modem_segments));
+ if (ret < 0) {
+ pr_err("Unable to dump modem fw memory (rc = %d).\n", ret);
+ goto out;
+ }
+
+ ret = do_ramdump(drv->smem_ramdump_dev, smem_segments,
+ ARRAY_SIZE(smem_segments));
+ if (ret < 0) {
+ pr_err("Unable to dump smem memory (rc = %d).\n", ret);
+ goto out;
+ }
+
+out:
+ pil_force_shutdown("mba");
+ return ret;
+}
+
+static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
+{
+ struct mba_data *drv = dev_id;
+ if (drv->ignore_errors)
+ return IRQ_HANDLED;
+ pr_err("Watchdog bite received from modem software!\n");
+ restart_modem(drv);
+ return IRQ_HANDLED;
+}
+
static int __devinit pil_mba_driver_probe(struct platform_device *pdev)
{
struct mba_data *drv;
struct resource *res;
struct pil_desc *desc;
- int ret;
+ int ret, irq;
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
if (!drv)
return -ENOMEM;
platform_set_drvdata(pdev, drv);
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rmb_base");
if (!res)
return -EINVAL;
@@ -215,12 +353,71 @@
if (IS_ERR(drv->pil))
return PTR_ERR(drv->pil);
+ drv->subsys_desc.name = desc->name;
+ drv->subsys_desc.dev = &pdev->dev;
+ drv->subsys_desc.owner = THIS_MODULE;
+ drv->subsys_desc.shutdown = modem_shutdown;
+ drv->subsys_desc.powerup = modem_powerup;
+ drv->subsys_desc.ramdump = modem_ramdump;
+ drv->subsys_desc.crash_shutdown = modem_crash_shutdown;
+
+ drv->ramdump_dev = create_ramdump_device("modem");
+ if (!drv->ramdump_dev) {
+ pr_err("%s: Unable to create a modem ramdump device.\n",
+ __func__);
+ ret = -ENOMEM;
+ goto err_ramdump;
+ }
+
+ drv->smem_ramdump_dev = create_ramdump_device("smem-modem");
+ if (!drv->smem_ramdump_dev) {
+ pr_err("%s: Unable to create an smem ramdump device.\n",
+ __func__);
+ ret = -ENOMEM;
+ goto err_ramdump_smem;
+ }
+
+ drv->subsys = subsys_register(&drv->subsys_desc);
+ if (IS_ERR(drv->subsys)) {
+ goto err_subsys;
+ ret = PTR_ERR(drv->subsys);
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, modem_wdog_bite_irq,
+ IRQF_TRIGGER_RISING, "modem_wdog", drv);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Unable to request watchdog IRQ.\n");
+ goto err_irq;
+ }
+
+ ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
+ smsm_state_cb, drv);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Unable to register SMSM callback!\n");
+ goto err_irq;
+ }
+
return 0;
+
+err_irq:
+ subsys_unregister(drv->subsys);
+err_subsys:
+ destroy_ramdump_device(drv->smem_ramdump_dev);
+err_ramdump_smem:
+ destroy_ramdump_device(drv->ramdump_dev);
+err_ramdump:
+ msm_pil_unregister(drv->pil);
+ return ret;
}
static int __devexit pil_mba_driver_exit(struct platform_device *pdev)
{
struct mba_data *drv = platform_get_drvdata(pdev);
+ smsm_state_cb_deregister(SMSM_MODEM_STATE, SMSM_RESET,
+ smsm_state_cb, drv);
+ subsys_unregister(drv->subsys);
+ destroy_ramdump_device(drv->smem_ramdump_dev);
+ destroy_ramdump_device(drv->ramdump_dev);
msm_pil_unregister(drv->pil);
return 0;
}
diff --git a/arch/arm/mach-msm/pil-pronto.c b/arch/arm/mach-msm/pil-pronto.c
index 1e39043..5685787 100644
--- a/arch/arm/mach-msm/pil-pronto.c
+++ b/arch/arm/mach-msm/pil-pronto.c
@@ -22,6 +22,14 @@
#include <linux/iopoll.h>
#include <linux/of.h>
#include <linux/regulator/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+#include <linux/wcnss_wlan.h>
+
+#include <mach/subsystem_restart.h>
+#include <mach/peripheral-loader.h>
+#include <mach/msm_smsm.h>
#include "peripheral-loader.h"
#include "scm-pas.h"
@@ -67,8 +75,14 @@
void __iomem *axi_halt_base;
unsigned long start_addr;
struct pil_device *pil;
+ struct subsys_device *subsys;
+ struct subsys_desc subsys_desc;
struct clk *cxo;
struct regulator *vreg;
+ bool restart_inprogress;
+ bool crash;
+ struct delayed_work cancel_vote_work;
+ int irq;
};
static int pil_pronto_make_proxy_vote(struct pil_desc *pil)
@@ -225,6 +239,129 @@
.proxy_unvote = pil_pronto_remove_proxy_vote,
};
+#define subsys_to_drv(d) container_of(d, struct pronto_data, subsys_desc)
+
+static void log_wcnss_sfr(void)
+{
+ char *smem_reset_reason;
+ unsigned smem_reset_size;
+
+ smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_WCNSS0,
+ &smem_reset_size);
+
+ if (!smem_reset_reason || !smem_reset_size) {
+ pr_err("wcnss subsystem failure reason:\n"
+ "(unknown, smem_get_entry failed)");
+ } else if (!smem_reset_reason[0]) {
+ pr_err("wcnss subsystem failure reason:\n"
+ "(unknown, init string found)");
+ } else {
+ pr_err("wcnss subsystem failure reason: %.81s\n",
+ smem_reset_reason);
+ memset(smem_reset_reason, 0, smem_reset_size);
+ wmb();
+ }
+}
+
+static void restart_wcnss(struct pronto_data *drv)
+{
+ log_wcnss_sfr();
+ subsystem_restart_dev(drv->subsys);
+}
+
+static void smsm_state_cb_hdlr(void *data, uint32_t old_state,
+ uint32_t new_state)
+{
+ struct pronto_data *drv = data;
+
+ drv->crash = true;
+
+ pr_err("wcnss smsm state changed\n");
+
+ if (!(new_state & SMSM_RESET))
+ return;
+
+ if (drv->restart_inprogress) {
+ pr_err("wcnss: Ignoring smsm reset req, restart in progress\n");
+ return;
+ }
+
+ drv->restart_inprogress = true;
+ restart_wcnss(drv);
+}
+
+static irqreturn_t wcnss_wdog_bite_irq_hdlr(int irq, void *dev_id)
+{
+ struct pronto_data *drv = dev_id;
+
+ drv->crash = true;
+
+ if (drv->restart_inprogress) {
+ pr_err("Ignoring wcnss bite irq, restart in progress\n");
+ return IRQ_HANDLED;
+ }
+
+ drv->restart_inprogress = true;
+ restart_wcnss(drv);
+
+ return IRQ_HANDLED;
+}
+
+static void wcnss_post_bootup(struct work_struct *work)
+{
+ struct platform_device *pdev = wcnss_get_platform_device();
+ struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();
+
+ wcnss_wlan_power(&pdev->dev, pwlanconfig, WCNSS_WLAN_SWITCH_OFF);
+}
+
+static int wcnss_shutdown(const struct subsys_desc *subsys)
+{
+ struct pronto_data *drv = subsys_to_drv(subsys);
+
+ pil_force_shutdown("wcnss");
+ flush_delayed_work(&drv->cancel_vote_work);
+ wcnss_flush_delayed_boot_votes();
+ disable_irq_nosync(drv->irq);
+
+ return 0;
+}
+
+static int wcnss_powerup(const struct subsys_desc *subsys)
+{
+ struct pronto_data *drv = subsys_to_drv(subsys);
+ struct platform_device *pdev = wcnss_get_platform_device();
+ struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();
+ int ret = -1;
+
+ if (pdev && pwlanconfig)
+ ret = wcnss_wlan_power(&pdev->dev, pwlanconfig,
+ WCNSS_WLAN_SWITCH_ON);
+ if (!ret) {
+ msleep(1000);
+ pil_force_boot("wcnss");
+ }
+ drv->restart_inprogress = false;
+ enable_irq(drv->irq);
+ schedule_delayed_work(&drv->cancel_vote_work, msecs_to_jiffies(5000));
+
+ return 0;
+}
+
+static void crash_shutdown(const struct subsys_desc *subsys)
+{
+ struct pronto_data *drv = subsys_to_drv(subsys);
+
+ pr_err("wcnss crash shutdown %d\n", drv->crash);
+ if (!drv->crash)
+ smsm_change_state(SMSM_APPS_STATE, SMSM_RESET, SMSM_RESET);
+}
+
+static int wcnss_ramdump(int enable, const struct subsys_desc *crashed_subsys)
+{
+ return 0;
+}
+
static int __devinit pil_pronto_probe(struct platform_device *pdev)
{
struct pronto_data *drv;
@@ -242,6 +379,10 @@
return -ENOMEM;
platform_set_drvdata(pdev, drv);
+ drv->irq = platform_get_irq(pdev, 0);
+ if (drv->irq < 0)
+ return drv->irq;
+
drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!drv->base)
return -ENOMEM;
@@ -303,6 +444,32 @@
if (IS_ERR(drv->pil))
return PTR_ERR(drv->pil);
+ ret = smsm_state_cb_register(SMSM_WCNSS_STATE, SMSM_RESET,
+ smsm_state_cb_hdlr, drv);
+ if (ret < 0)
+ goto err_smsm;
+
+ drv->subsys_desc.name = desc->name;
+ drv->subsys_desc.dev = &pdev->dev;
+ drv->subsys_desc.owner = THIS_MODULE;
+ drv->subsys_desc.shutdown = wcnss_shutdown;
+ drv->subsys_desc.powerup = wcnss_powerup;
+ drv->subsys_desc.ramdump = wcnss_ramdump;
+ drv->subsys_desc.crash_shutdown = crash_shutdown;
+
+ INIT_DELAYED_WORK(&drv->cancel_vote_work, wcnss_post_bootup);
+
+ drv->subsys = subsys_register(&drv->subsys_desc);
+ if (IS_ERR(drv->subsys)) {
+ ret = PTR_ERR(drv->subsys);
+ goto err_subsys;
+ }
+
+ ret = devm_request_irq(&pdev->dev, drv->irq, wcnss_wdog_bite_irq_hdlr,
+ IRQF_TRIGGER_HIGH, "wcnss_wdog", drv);
+ if (ret < 0)
+ goto err_irq;
+
/* Initialize common_ss GDSCR to wait 4 cycles between states */
regval = readl_relaxed(drv->base + PRONTO_PMU_COMMON_GDSCR)
& PRONTO_PMU_COMMON_GDSCR_SW_COLLAPSE;
@@ -311,11 +478,22 @@
writel_relaxed(regval, drv->base + PRONTO_PMU_COMMON_GDSCR);
return 0;
+err_irq:
+ subsys_unregister(drv->subsys);
+err_subsys:
+ smsm_state_cb_deregister(SMSM_WCNSS_STATE, SMSM_RESET,
+ smsm_state_cb_hdlr, drv);
+err_smsm:
+ msm_pil_unregister(drv->pil);
+ return ret;
}
static int __devexit pil_pronto_remove(struct platform_device *pdev)
{
struct pronto_data *drv = platform_get_drvdata(pdev);
+ subsys_unregister(drv->subsys);
+ smsm_state_cb_deregister(SMSM_WCNSS_STATE, SMSM_RESET,
+ smsm_state_cb_hdlr, drv);
msm_pil_unregister(drv->pil);
return 0;
}
diff --git a/arch/arm/mach-msm/pil-q6v3.c b/arch/arm/mach-msm/pil-q6v3.c
index 28b9dee..1a226de 100644
--- a/arch/arm/mach-msm/pil-q6v3.c
+++ b/arch/arm/mach-msm/pil-q6v3.c
@@ -19,9 +19,15 @@
#include <linux/elf.h>
#include <linux/err.h>
#include <linux/clk.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
#include <mach/msm_iomap.h>
+#include <mach/subsystem_restart.h>
+#include <mach/scm.h>
+#include <mach/peripheral-loader.h>
+#include "ramdump.h"
#include "peripheral-loader.h"
#include "scm-pas.h"
@@ -60,11 +66,34 @@
#define Q6_STRAP_TCM_BASE (0x28C << 15)
#define Q6_STRAP_TCM_CONFIG 0x28B
+#define SCM_Q6_NMI_CMD 0x1
+
+/**
+ * struct q6v3_data - LPASS driver data
+ * @base: register base
+ * @wk_base: wakeup register base
+ * @wd_base: watchdog register base
+ * @start_addr: address that processor starts running at
+ * @irq: watchdog irq
+ * @pil: peripheral handle
+ * @subsys: subsystem restart handle
+ * @subsys_desc: subsystem restart descriptor
+ * @fatal_wrk: fatal error workqueue
+ * @pll: pll clock handle
+ * @ramdump_dev: ramdump device
+ */
struct q6v3_data {
void __iomem *base;
+ void __iomem *wk_base;
+ void __iomem *wd_base;
unsigned long start_addr;
+ int irq;
struct pil_device *pil;
+ struct subsys_device *subsys;
+ struct subsys_desc subsys_desc;
+ struct work_struct fatal_wrk;
struct clk *pll;
+ struct ramdump_device *ramdump_dev;
};
static int pil_q6v3_init_image(struct pil_desc *pil, const u8 *metadata,
@@ -198,11 +227,96 @@
.proxy_unvote = pil_q6v3_remove_proxy_votes,
};
+static void q6_fatal_fn(struct work_struct *work)
+{
+ struct q6v3_data *drv = container_of(work, struct q6v3_data, fatal_wrk);
+
+ pr_err("Watchdog bite received from Q6!\n");
+ subsystem_restart_dev(drv->subsys);
+ enable_irq(drv->irq);
+}
+
+static void send_q6_nmi(struct q6v3_data *drv)
+{
+ /* Send NMI to QDSP6 via an SCM call. */
+ scm_call_atomic1(SCM_SVC_UTIL, SCM_Q6_NMI_CMD, 0x1);
+
+ /* Wakeup the Q6 */
+ writel_relaxed(0x2000, drv->wk_base + 0x1c);
+ /* Q6 requires atleast 100ms to dump caches etc.*/
+ mdelay(100);
+ pr_info("Q6 NMI was sent.\n");
+}
+
+static int lpass_q6_shutdown(const struct subsys_desc *subsys)
+{
+ struct q6v3_data *drv;
+
+ drv = container_of(subsys, struct q6v3_data, subsys_desc);
+ send_q6_nmi(drv);
+ writel_relaxed(0x0, drv->wd_base + 0x24);
+ mb();
+
+ pil_force_shutdown("q6");
+ disable_irq_nosync(drv->irq);
+
+ return 0;
+}
+
+static int lpass_q6_powerup(const struct subsys_desc *subsys)
+{
+ struct q6v3_data *drv;
+ int ret;
+
+ drv = container_of(subsys, struct q6v3_data, subsys_desc);
+ ret = pil_force_boot("q6");
+ enable_irq(drv->irq);
+ return ret;
+}
+
+/* FIXME: Get address, size from PIL */
+static struct ramdump_segment q6_segments[] = {
+ { 0x46700000, 0x47f00000 - 0x46700000 },
+ { 0x28400000, 0x12800 }
+};
+
+static int lpass_q6_ramdump(int enable, const struct subsys_desc *subsys)
+{
+ struct q6v3_data *drv;
+
+ drv = container_of(subsys, struct q6v3_data, subsys_desc);
+ if (enable)
+ return do_ramdump(drv->ramdump_dev, q6_segments,
+ ARRAY_SIZE(q6_segments));
+ else
+ return 0;
+}
+
+static void lpass_q6_crash_shutdown(const struct subsys_desc *subsys)
+{
+ struct q6v3_data *drv;
+
+ drv = container_of(subsys, struct q6v3_data, subsys_desc);
+ send_q6_nmi(drv);
+}
+
+static irqreturn_t lpass_wdog_bite_irq(int irq, void *dev_id)
+{
+ int ret;
+ struct q6v3_data *drv = dev_id;
+
+ ret = schedule_work(&drv->fatal_wrk);
+ disable_irq_nosync(drv->irq);
+
+ return IRQ_HANDLED;
+}
+
static int __devinit pil_q6v3_driver_probe(struct platform_device *pdev)
{
struct q6v3_data *drv;
struct resource *res;
struct pil_desc *desc;
+ int ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
@@ -217,14 +331,34 @@
if (!drv->base)
return -ENOMEM;
- desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
- if (!drv)
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res)
+ return -EINVAL;
+
+ drv->wk_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!drv->wk_base)
return -ENOMEM;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ if (!res)
+ return -EINVAL;
+
+ drv->wd_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!drv->wd_base)
+ return -ENOMEM;
+
+ drv->irq = platform_get_irq(pdev, 0);
+ if (drv->irq < 0)
+ return drv->irq;
+
drv->pll = devm_clk_get(&pdev->dev, "pll4");
if (IS_ERR(drv->pll))
return PTR_ERR(drv->pll);
+ desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+
desc->name = "q6";
desc->dev = &pdev->dev;
desc->owner = THIS_MODULE;
@@ -239,15 +373,51 @@
}
drv->pil = msm_pil_register(desc);
- if (IS_ERR(drv->pil)) {
+ if (IS_ERR(drv->pil))
return PTR_ERR(drv->pil);
+
+ drv->subsys_desc.name = "lpass";
+ drv->subsys_desc.shutdown = lpass_q6_shutdown;
+ drv->subsys_desc.powerup = lpass_q6_powerup;
+ drv->subsys_desc.ramdump = lpass_q6_ramdump;
+ drv->subsys_desc.crash_shutdown = lpass_q6_crash_shutdown;
+
+ INIT_WORK(&drv->fatal_wrk, q6_fatal_fn);
+
+ drv->ramdump_dev = create_ramdump_device("lpass");
+ if (!drv->ramdump_dev) {
+ ret = -ENOMEM;
+ goto err_ramdump;
}
+
+ drv->subsys = subsys_register(&drv->subsys_desc);
+ if (IS_ERR(drv->subsys)) {
+ ret = PTR_ERR(drv->subsys);
+ goto err_subsys;
+ }
+
+ ret = devm_request_irq(&pdev->dev, drv->irq, lpass_wdog_bite_irq,
+ IRQF_TRIGGER_RISING, "lpass_wdog", drv);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to request wdog irq.\n");
+ goto err_irq;
+ }
+
return 0;
+err_irq:
+ subsys_unregister(drv->subsys);
+err_subsys:
+ destroy_ramdump_device(drv->ramdump_dev);
+err_ramdump:
+ msm_pil_unregister(drv->pil);
+ return ret;
}
static int __devexit pil_q6v3_driver_exit(struct platform_device *pdev)
{
struct q6v3_data *drv = platform_get_drvdata(pdev);
+ subsys_unregister(drv->subsys);
+ destroy_ramdump_device(drv->ramdump_dev);
msm_pil_unregister(drv->pil);
return 0;
}
diff --git a/arch/arm/mach-msm/pil-q6v4-lpass.c b/arch/arm/mach-msm/pil-q6v4-lpass.c
new file mode 100644
index 0000000..a0432550
--- /dev/null
+++ b/arch/arm/mach-msm/pil-q6v4-lpass.c
@@ -0,0 +1,402 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+
+#include <mach/scm.h>
+#include <mach/peripheral-loader.h>
+#include <mach/subsystem_restart.h>
+#include <mach/subsystem_notif.h>
+
+#include "smd_private.h"
+#include "ramdump.h"
+#include "sysmon.h"
+#include "peripheral-loader.h"
+#include "pil-q6v4.h"
+#include "scm-pas.h"
+
+struct lpass_q6v4 {
+ struct q6v4_data q6;
+ void *riva_notif_hdle;
+ void *modem_notif_hdle;
+ struct subsys_device *subsys;
+ struct subsys_desc subsys_desc;
+ int crash_shutdown;
+ void *ramdump_dev;
+ struct work_struct work;
+ int loadable;
+};
+
+static int pil_q6v4_lpass_boot(struct pil_desc *pil)
+{
+ struct q6v4_data *drv = pil_to_q6v4_data(pil);
+ int err;
+
+ err = pil_q6v4_power_up(drv);
+ if (err)
+ return err;
+
+ return pil_q6v4_boot(pil);
+}
+
+static int pil_q6v4_lpass_shutdown(struct pil_desc *pil)
+{
+ struct q6v4_data *drv = pil_to_q6v4_data(pil);
+ int ret;
+
+ ret = pil_q6v4_shutdown(pil);
+ if (ret)
+ return ret;
+ pil_q6v4_power_down(drv);
+ return 0;
+}
+
+static struct pil_reset_ops pil_q6v4_lpass_ops = {
+ .init_image = pil_q6v4_init_image,
+ .auth_and_reset = pil_q6v4_lpass_boot,
+ .shutdown = pil_q6v4_lpass_shutdown,
+ .proxy_vote = pil_q6v4_make_proxy_votes,
+ .proxy_unvote = pil_q6v4_remove_proxy_votes,
+};
+
+static struct pil_reset_ops pil_q6v4_lpass_ops_trusted = {
+ .init_image = pil_q6v4_init_image_trusted,
+ .auth_and_reset = pil_q6v4_boot_trusted,
+ .shutdown = pil_q6v4_shutdown_trusted,
+ .proxy_vote = pil_q6v4_make_proxy_votes,
+ .proxy_unvote = pil_q6v4_remove_proxy_votes,
+};
+
+static int riva_notifier_cb(struct notifier_block *this, unsigned long code,
+ void *ss_handle)
+{
+ int ret;
+ switch (code) {
+ case SUBSYS_BEFORE_SHUTDOWN:
+ pr_debug("%s: R-Notify: Shutdown started\n", __func__);
+ ret = sysmon_send_event(SYSMON_SS_LPASS, "wcnss",
+ SUBSYS_BEFORE_SHUTDOWN);
+ if (ret < 0)
+ pr_err("%s: sysmon_send_event error %d", __func__, ret);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block rnb = {
+ .notifier_call = riva_notifier_cb,
+};
+
+static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
+ void *ss_handle)
+{
+ int ret;
+ switch (code) {
+ case SUBSYS_BEFORE_SHUTDOWN:
+ pr_debug("%s: M-Notify: Shutdown started\n", __func__);
+ ret = sysmon_send_event(SYSMON_SS_LPASS, "modem",
+ SUBSYS_BEFORE_SHUTDOWN);
+ if (ret < 0)
+ pr_err("%s: sysmon_send_event error %d", __func__, ret);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block mnb = {
+ .notifier_call = modem_notifier_cb,
+};
+
+static void lpass_log_failure_reason(void)
+{
+ char *reason;
+ char buffer[81];
+ unsigned size;
+
+ reason = smem_get_entry(SMEM_SSR_REASON_LPASS0, &size);
+
+ if (!reason) {
+ pr_err("LPASS subsystem failure reason: (unknown, smem_get_entry failed).");
+ return;
+ }
+
+ if (reason[0] == '\0') {
+ pr_err("LPASS subsystem failure reason: (unknown, init value found)");
+ return;
+ }
+
+ size = min(size, sizeof(buffer) - 1);
+ memcpy(buffer, reason, size);
+ buffer[size] = '\0';
+ pr_err("LPASS subsystem failure reason: %s", buffer);
+ memset((void *)reason, 0x0, size);
+ wmb();
+}
+
+static void lpass_fatal_fn(struct work_struct *work)
+{
+ pr_err("Watchdog bite received from lpass Q6!\n");
+ lpass_log_failure_reason();
+ panic("Q6 Resetting the SoC");
+}
+
+static void lpass_smsm_state_cb(void *data, uint32_t old_state,
+ uint32_t new_state)
+{
+ struct lpass_q6v4 *drv = data;
+
+ /* Ignore if we're the one that set SMSM_RESET */
+ if (drv->crash_shutdown)
+ return;
+
+ if (new_state & SMSM_RESET) {
+ pr_err("%s: LPASS SMSM state changed to SMSM_RESET, new_state = %#x, old_state = %#x\n",
+ __func__, new_state, old_state);
+ lpass_log_failure_reason();
+ panic("Q6 Resetting the SoC");
+ }
+}
+
+#define SCM_Q6_NMI_CMD 0x1
+
+static void send_q6_nmi(void)
+{
+ /* Send NMI to QDSP6 via an SCM call. */
+ uint32_t cmd = 0x1;
+
+ scm_call(SCM_SVC_UTIL, SCM_Q6_NMI_CMD,
+ &cmd, sizeof(cmd), NULL, 0);
+
+ /* Q6 requires worstcase 100ms to dump caches etc.*/
+ mdelay(100);
+ pr_debug("%s: Q6 NMI was sent.\n", __func__);
+}
+
+#define subsys_to_lpass(d) container_of(d, struct lpass_q6v4, subsys_desc)
+
+static int lpass_shutdown(const struct subsys_desc *subsys)
+{
+ struct lpass_q6v4 *drv = subsys_to_lpass(subsys);
+
+ send_q6_nmi();
+ if (drv->loadable)
+ pil_force_shutdown("q6");
+ disable_irq_nosync(drv->q6.wdog_irq);
+
+ return 0;
+}
+
+static int lpass_powerup(const struct subsys_desc *subsys)
+{
+ struct lpass_q6v4 *drv = subsys_to_lpass(subsys);
+ int ret = 0;
+
+ if (drv->loadable)
+ ret = pil_force_boot("q6");
+ enable_irq(drv->q6.wdog_irq);
+
+ return ret;
+}
+
+static struct ramdump_segment segments[] = {
+ {0x8da00000, 0x8f200000 - 0x8da00000},
+ {0x28400000, 0x20000}
+};
+
+static int lpass_ramdump(int enable, const struct subsys_desc *subsys)
+{
+ struct lpass_q6v4 *drv = subsys_to_lpass(subsys);
+
+ if (!enable)
+ return 0;
+ return do_ramdump(drv->ramdump_dev, segments, ARRAY_SIZE(segments));
+}
+
+static void lpass_crash_shutdown(const struct subsys_desc *subsys)
+{
+ struct lpass_q6v4 *drv = subsys_to_lpass(subsys);
+
+ drv->crash_shutdown = 1;
+ send_q6_nmi();
+}
+
+static irqreturn_t lpass_wdog_bite_irq(int irq, void *dev_id)
+{
+ struct lpass_q6v4 *drv = dev_id;
+
+ disable_irq_nosync(drv->q6.wdog_irq);
+ schedule_work(&drv->work);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit pil_q6v4_lpass_driver_probe(struct platform_device *pdev)
+{
+ const struct pil_q6v4_pdata *pdata = pdev->dev.platform_data;
+ struct lpass_q6v4 *drv;
+ struct q6v4_data *q6;
+ struct pil_desc *desc;
+ struct resource *res;
+ int ret;
+
+ drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, drv);
+ q6 = &drv->q6;
+ desc = &q6->desc;
+
+ q6->wdog_irq = platform_get_irq(pdev, 0);
+ if (q6->wdog_irq < 0)
+ return q6->wdog_irq;
+
+ drv->loadable = !!pdata; /* No pdata = don't use PIL */
+ if (drv->loadable) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+ q6->base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!q6->base)
+ return -ENOMEM;
+
+ q6->vreg = devm_regulator_get(&pdev->dev, "core_vdd");
+ if (IS_ERR(q6->vreg))
+ return PTR_ERR(q6->vreg);
+
+ q6->xo = devm_clk_get(&pdev->dev, "xo");
+ if (IS_ERR(q6->xo))
+ return PTR_ERR(q6->xo);
+
+ desc->name = pdata->name;
+ desc->dev = &pdev->dev;
+ desc->owner = THIS_MODULE;
+ desc->proxy_timeout = 10000;
+ pil_q6v4_init(q6, pdata);
+
+ if (pas_supported(pdata->pas_id) > 0) {
+ desc->ops = &pil_q6v4_lpass_ops_trusted;
+ dev_info(&pdev->dev, "using secure boot\n");
+ } else {
+ desc->ops = &pil_q6v4_lpass_ops;
+ dev_info(&pdev->dev, "using non-secure boot\n");
+ }
+
+ q6->pil = msm_pil_register(desc);
+ if (IS_ERR(q6->pil))
+ return PTR_ERR(q6->pil);
+ }
+
+ drv->subsys_desc.name = "lpass";
+ drv->subsys_desc.shutdown = lpass_shutdown;
+ drv->subsys_desc.powerup = lpass_powerup;
+ drv->subsys_desc.ramdump = lpass_ramdump;
+ drv->subsys_desc.crash_shutdown = lpass_crash_shutdown;
+
+ INIT_WORK(&drv->work, lpass_fatal_fn);
+
+ drv->ramdump_dev = create_ramdump_device("lpass");
+ if (!drv->ramdump_dev) {
+ ret = -ENOMEM;
+ goto err_ramdump;
+ }
+
+ drv->subsys = subsys_register(&drv->subsys_desc);
+ if (IS_ERR(drv->subsys)) {
+ ret = PTR_ERR(drv->subsys);
+ goto err_subsys;
+ }
+
+ ret = devm_request_irq(&pdev->dev, q6->wdog_irq, lpass_wdog_bite_irq,
+ IRQF_TRIGGER_RISING, dev_name(&pdev->dev), drv);
+ if (ret)
+ goto err_irq;
+
+ ret = smsm_state_cb_register(SMSM_Q6_STATE, SMSM_RESET,
+ lpass_smsm_state_cb, drv);
+ if (ret < 0)
+ goto err_smsm;
+
+ drv->riva_notif_hdle = subsys_notif_register_notifier("riva", &rnb);
+ if (IS_ERR(drv->riva_notif_hdle)) {
+ ret = PTR_ERR(drv->riva_notif_hdle);
+ goto err_notif_riva;
+ }
+
+ drv->modem_notif_hdle = subsys_notif_register_notifier("modem", &mnb);
+ if (IS_ERR(drv->modem_notif_hdle)) {
+ ret = PTR_ERR(drv->modem_notif_hdle);
+ goto err_notif_modem;
+ }
+ return 0;
+err_notif_modem:
+ subsys_notif_unregister_notifier(drv->riva_notif_hdle, &rnb);
+err_notif_riva:
+ smsm_state_cb_deregister(SMSM_Q6_STATE, SMSM_RESET,
+ lpass_smsm_state_cb, drv);
+err_smsm:
+err_irq:
+ subsys_unregister(drv->subsys);
+err_subsys:
+ destroy_ramdump_device(drv->ramdump_dev);
+err_ramdump:
+ if (drv->loadable)
+ msm_pil_unregister(q6->pil);
+ return ret;
+}
+
+static int __devexit pil_q6v4_lpass_driver_exit(struct platform_device *pdev)
+{
+ struct lpass_q6v4 *drv = platform_get_drvdata(pdev);
+ subsys_notif_unregister_notifier(drv->riva_notif_hdle, &rnb);
+ subsys_notif_unregister_notifier(drv->modem_notif_hdle, &mnb);
+ smsm_state_cb_deregister(SMSM_Q6_STATE, SMSM_RESET,
+ lpass_smsm_state_cb, drv);
+ subsys_unregister(drv->subsys);
+ destroy_ramdump_device(drv->ramdump_dev);
+ if (drv->loadable)
+ msm_pil_unregister(drv->q6.pil);
+ return 0;
+}
+
+static struct platform_driver pil_q6v4_lpass_driver = {
+ .probe = pil_q6v4_lpass_driver_probe,
+ .remove = __devexit_p(pil_q6v4_lpass_driver_exit),
+ .driver = {
+ .name = "pil-q6v4-lpass",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pil_q6v4_lpass_init(void)
+{
+ return platform_driver_register(&pil_q6v4_lpass_driver);
+}
+module_init(pil_q6v4_lpass_init);
+
+static void __exit pil_q6v4_lpass_exit(void)
+{
+ platform_driver_unregister(&pil_q6v4_lpass_driver);
+}
+module_exit(pil_q6v4_lpass_exit);
+
+MODULE_DESCRIPTION("Support for booting QDSP6v4 (Hexagon) processors");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/pil-q6v4-mss.c b/arch/arm/mach-msm/pil-q6v4-mss.c
new file mode 100644
index 0000000..61b5536
--- /dev/null
+++ b/arch/arm/mach-msm/pil-q6v4-mss.c
@@ -0,0 +1,501 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+
+#include <mach/msm_iomap.h>
+#include <mach/subsystem_restart.h>
+#include <mach/msm_smsm.h>
+#include <mach/peripheral-loader.h>
+
+#include "smd_private.h"
+#include "ramdump.h"
+#include "peripheral-loader.h"
+#include "pil-q6v4.h"
+#include "scm-pas.h"
+
+#define MSS_S_HCLK_CTL (MSM_CLK_CTL_BASE + 0x2C70)
+#define MSS_SLP_CLK_CTL (MSM_CLK_CTL_BASE + 0x2C60)
+#define SFAB_MSS_M_ACLK_CTL (MSM_CLK_CTL_BASE + 0x2340)
+#define SFAB_MSS_S_HCLK_CTL (MSM_CLK_CTL_BASE + 0x2C00)
+#define MSS_RESET (MSM_CLK_CTL_BASE + 0x2C64)
+
+struct q6v4_modem {
+ struct q6v4_data q6_fw;
+ struct q6v4_data q6_sw;
+ void __iomem *modem_base;
+ void *fw_ramdump_dev;
+ void *sw_ramdump_dev;
+ void *smem_ramdump_dev;
+ struct subsys_device *subsys;
+ struct subsys_desc subsys_desc;
+ int crash_shutdown;
+ int loadable;
+};
+
+static DEFINE_MUTEX(pil_q6v4_modem_lock);
+static unsigned pil_q6v4_modem_count;
+
+/* Bring modem subsystem out of reset */
+static void pil_q6v4_init_modem(void __iomem *base, void __iomem *jtag_clk)
+{
+ mutex_lock(&pil_q6v4_modem_lock);
+ if (!pil_q6v4_modem_count) {
+ /* Enable MSS clocks */
+ writel_relaxed(0x10, SFAB_MSS_M_ACLK_CTL);
+ writel_relaxed(0x10, SFAB_MSS_S_HCLK_CTL);
+ writel_relaxed(0x10, MSS_S_HCLK_CTL);
+ writel_relaxed(0x10, MSS_SLP_CLK_CTL);
+ /* Wait for clocks to enable */
+ mb();
+ udelay(10);
+
+ /* De-assert MSS reset */
+ writel_relaxed(0x0, MSS_RESET);
+ mb();
+ udelay(10);
+ /* Enable MSS */
+ writel_relaxed(0x7, base);
+ }
+
+ /* Enable JTAG clocks */
+ /* TODO: Remove if/when Q6 software enables them? */
+ writel_relaxed(0x10, jtag_clk);
+
+ pil_q6v4_modem_count++;
+ mutex_unlock(&pil_q6v4_modem_lock);
+}
+
+/* Put modem subsystem back into reset */
+static void pil_q6v4_shutdown_modem(void)
+{
+ mutex_lock(&pil_q6v4_modem_lock);
+ if (pil_q6v4_modem_count)
+ pil_q6v4_modem_count--;
+ if (pil_q6v4_modem_count == 0)
+ writel_relaxed(0x1, MSS_RESET);
+ mutex_unlock(&pil_q6v4_modem_lock);
+}
+
+static int pil_q6v4_modem_boot(struct pil_desc *pil)
+{
+ struct q6v4_data *drv = pil_to_q6v4_data(pil);
+ struct q6v4_modem *mdm = dev_get_drvdata(pil->dev);
+ int err;
+
+ err = pil_q6v4_power_up(drv);
+ if (err)
+ return err;
+
+ pil_q6v4_init_modem(mdm->modem_base, drv->jtag_clk_reg);
+ return pil_q6v4_boot(pil);
+}
+
+static int pil_q6v4_modem_shutdown(struct pil_desc *pil)
+{
+ struct q6v4_data *drv = pil_to_q6v4_data(pil);
+ int ret;
+
+ ret = pil_q6v4_shutdown(pil);
+ if (ret)
+ return ret;
+ pil_q6v4_shutdown_modem();
+ pil_q6v4_power_down(drv);
+ return 0;
+}
+
+static struct pil_reset_ops pil_q6v4_modem_ops = {
+ .init_image = pil_q6v4_init_image,
+ .auth_and_reset = pil_q6v4_modem_boot,
+ .shutdown = pil_q6v4_modem_shutdown,
+ .proxy_vote = pil_q6v4_make_proxy_votes,
+ .proxy_unvote = pil_q6v4_remove_proxy_votes,
+};
+
+static struct pil_reset_ops pil_q6v4_modem_ops_trusted = {
+ .init_image = pil_q6v4_init_image_trusted,
+ .auth_and_reset = pil_q6v4_boot_trusted,
+ .shutdown = pil_q6v4_shutdown_trusted,
+ .proxy_vote = pil_q6v4_make_proxy_votes,
+ .proxy_unvote = pil_q6v4_remove_proxy_votes,
+};
+
+static void log_modem_sfr(void)
+{
+ u32 size;
+ char *smem_reason, reason[81];
+
+ smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size);
+ if (!smem_reason || !size) {
+ pr_err("modem subsystem failure reason: (unknown, smem_get_entry failed).\n");
+ return;
+ }
+ if (!smem_reason[0]) {
+ pr_err("modem subsystem failure reason: (unknown, init string found).\n");
+ return;
+ }
+
+ size = min(size, sizeof(reason)-1);
+ memcpy(reason, smem_reason, size);
+ reason[size] = '\0';
+ pr_err("modem subsystem failure reason: %s.\n", reason);
+
+ smem_reason[0] = '\0';
+ wmb();
+}
+
+static void restart_modem(struct q6v4_modem *drv)
+{
+ log_modem_sfr();
+ subsystem_restart_dev(drv->subsys);
+}
+
+#define desc_to_modem(d) container_of(d, struct q6v4_modem, subsys_desc)
+
+static int modem_shutdown(const struct subsys_desc *subsys)
+{
+ struct q6v4_modem *drv = desc_to_modem(subsys);
+
+ /* The watchdogs keep running even after the modem is shutdown */
+ writel_relaxed(0x0, drv->q6_fw.wdog_base + 0x24);
+ writel_relaxed(0x0, drv->q6_sw.wdog_base + 0x24);
+ mb();
+
+ if (drv->loadable) {
+ pil_force_shutdown("modem");
+ pil_force_shutdown("modem_fw");
+ }
+
+ disable_irq_nosync(drv->q6_fw.wdog_irq);
+ disable_irq_nosync(drv->q6_sw.wdog_irq);
+
+ return 0;
+}
+
+static int modem_powerup(const struct subsys_desc *subsys)
+{
+ struct q6v4_modem *drv = desc_to_modem(subsys);
+
+ if (drv->loadable) {
+ pil_force_boot("modem_fw");
+ pil_force_boot("modem");
+ }
+ enable_irq(drv->q6_fw.wdog_irq);
+ enable_irq(drv->q6_sw.wdog_irq);
+ return 0;
+}
+
+void modem_crash_shutdown(const struct subsys_desc *subsys)
+{
+ struct q6v4_modem *drv = desc_to_modem(subsys);
+
+ drv->crash_shutdown = 1;
+ smsm_reset_modem(SMSM_RESET);
+}
+
+static struct ramdump_segment sw_segments[] = {
+ {0x89000000, 0x8D400000 - 0x89000000},
+};
+
+static struct ramdump_segment fw_segments[] = {
+ {0x8D400000, 0x8DA00000 - 0x8D400000},
+};
+
+static struct ramdump_segment smem_segments[] = {
+ {0x80000000, 0x00200000},
+};
+
+static int modem_ramdump(int enable, const struct subsys_desc *subsys)
+{
+ struct q6v4_modem *drv = desc_to_modem(subsys);
+ int ret;
+
+ if (!enable)
+ return 0;
+
+ ret = do_ramdump(drv->sw_ramdump_dev, sw_segments,
+ ARRAY_SIZE(sw_segments));
+ if (ret < 0)
+ return ret;
+
+ ret = do_ramdump(drv->fw_ramdump_dev, fw_segments,
+ ARRAY_SIZE(fw_segments));
+ if (ret < 0)
+ return ret;
+
+ ret = do_ramdump(drv->smem_ramdump_dev, smem_segments,
+ ARRAY_SIZE(smem_segments));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
+{
+ struct q6v4_modem *drv = data;
+
+ /* Ignore if we're the one that set SMSM_RESET */
+ if (drv->crash_shutdown)
+ return;
+
+ if (new_state & SMSM_RESET) {
+ pr_err("Probable fatal error on the modem.\n");
+ restart_modem(drv);
+ }
+}
+
+static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
+{
+ struct q6v4_modem *drv = dev_id;
+ restart_modem(drv);
+ return IRQ_HANDLED;
+}
+
+static int __devinit
+pil_q6v4_proc_init(struct q6v4_data *drv, struct platform_device *pdev, int i)
+{
+ static const char *name[2] = { "fw", "sw" };
+ const struct pil_q6v4_pdata *pdata_p = pdev->dev.platform_data;
+ const struct pil_q6v4_pdata *pdata = pdata_p + i;
+ char reg_name[12];
+ struct pil_desc *desc;
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1 + (i * 2));
+ if (!res)
+ return -EINVAL;
+
+ drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!drv->base)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2 + (i * 2));
+ if (!res)
+ return -EINVAL;
+
+ drv->wdog_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!drv->wdog_base)
+ return -ENOMEM;
+
+ snprintf(reg_name, sizeof(reg_name), "%s_core_vdd", name[i]);
+ drv->vreg = devm_regulator_get(&pdev->dev, reg_name);
+ if (IS_ERR(drv->vreg))
+ return PTR_ERR(drv->vreg);
+
+ drv->xo = devm_clk_get(&pdev->dev, "xo");
+ if (IS_ERR(drv->xo))
+ return PTR_ERR(drv->xo);
+
+ desc = &drv->desc;
+ desc->name = pdata->name;
+ desc->depends_on = pdata->depends;
+ desc->dev = &pdev->dev;
+ desc->owner = THIS_MODULE;
+ desc->proxy_timeout = 10000;
+ pil_q6v4_init(drv, pdata);
+
+ if (pas_supported(pdata->pas_id) > 0) {
+ desc->ops = &pil_q6v4_modem_ops_trusted;
+ dev_info(&pdev->dev, "using secure boot for %s\n", name[i]);
+ } else {
+ desc->ops = &pil_q6v4_modem_ops;
+ dev_info(&pdev->dev, "using non-secure boot for %s\n", name[i]);
+ }
+ return 0;
+}
+
+static int __devinit pil_q6v4_modem_driver_probe(struct platform_device *pdev)
+{
+ struct q6v4_data *drv_fw, *drv_sw;
+ struct q6v4_modem *drv;
+ struct resource *res;
+ struct regulator *pll_supply;
+ int ret;
+ const struct pil_q6v4_pdata *pdata = pdev->dev.platform_data;
+
+ drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, drv);
+
+ drv_fw = &drv->q6_fw;
+ drv_sw = &drv->q6_sw;
+
+ drv_fw->wdog_irq = platform_get_irq(pdev, 0);
+ if (drv_fw->wdog_irq < 0)
+ return drv_fw->wdog_irq;
+
+ drv_sw->wdog_irq = platform_get_irq(pdev, 1);
+ if (drv_sw->wdog_irq < 0)
+ return drv_sw->wdog_irq;
+
+ drv->loadable = !!pdata; /* No pdata = don't use PIL */
+ if (drv->loadable) {
+ ret = pil_q6v4_proc_init(drv_fw, pdev, 0);
+ if (ret)
+ return ret;
+
+ ret = pil_q6v4_proc_init(drv_sw, pdev, 1);
+ if (ret)
+ return ret;
+
+ pll_supply = devm_regulator_get(&pdev->dev, "pll_vdd");
+ drv_fw->pll_supply = drv_sw->pll_supply = pll_supply;
+ if (IS_ERR(pll_supply))
+ return PTR_ERR(pll_supply);
+
+ ret = regulator_set_voltage(pll_supply, 1800000, 1800000);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to set pll voltage\n");
+ return ret;
+ }
+
+ ret = regulator_set_optimum_mode(pll_supply, 100000);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to set pll optimum mode\n");
+ return ret;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ drv->modem_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!drv->modem_base)
+ return -ENOMEM;
+
+ drv_fw->pil = msm_pil_register(&drv_fw->desc);
+ if (IS_ERR(drv_fw->pil))
+ return PTR_ERR(drv_fw->pil);
+
+ drv_sw->pil = msm_pil_register(&drv_sw->desc);
+ if (IS_ERR(drv_sw->pil)) {
+ ret = PTR_ERR(drv_sw->pil);
+ goto err_pil_sw;
+ }
+ }
+
+ drv->subsys_desc.name = "modem";
+ drv->subsys_desc.shutdown = modem_shutdown;
+ drv->subsys_desc.powerup = modem_powerup;
+ drv->subsys_desc.ramdump = modem_ramdump;
+ drv->subsys_desc.crash_shutdown = modem_crash_shutdown;
+
+ drv->fw_ramdump_dev = create_ramdump_device("modem_fw");
+ if (!drv->fw_ramdump_dev) {
+ ret = -ENOMEM;
+ goto err_fw_ramdump;
+ }
+
+ drv->sw_ramdump_dev = create_ramdump_device("modem_sw");
+ if (!drv->sw_ramdump_dev) {
+ ret = -ENOMEM;
+ goto err_sw_ramdump;
+ }
+
+ drv->smem_ramdump_dev = create_ramdump_device("smem-modem");
+ if (!drv->smem_ramdump_dev) {
+ ret = -ENOMEM;
+ goto err_smem_ramdump;
+ }
+
+ drv->subsys = subsys_register(&drv->subsys_desc);
+ if (IS_ERR(drv->subsys)) {
+ ret = PTR_ERR(drv->subsys);
+ goto err_subsys;
+ }
+
+ ret = devm_request_irq(&pdev->dev, drv_fw->wdog_irq,
+ modem_wdog_bite_irq, IRQF_TRIGGER_RISING,
+ dev_name(&pdev->dev), drv);
+ if (ret)
+ goto err_irq;
+
+ ret = devm_request_irq(&pdev->dev, drv_sw->wdog_irq,
+ modem_wdog_bite_irq, IRQF_TRIGGER_RISING,
+ dev_name(&pdev->dev), drv);
+ if (ret)
+ goto err_irq;
+
+ ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
+ smsm_state_cb, drv);
+ if (ret)
+ goto err_irq;
+ return 0;
+
+err_irq:
+ subsys_unregister(drv->subsys);
+err_subsys:
+ destroy_ramdump_device(drv->smem_ramdump_dev);
+err_smem_ramdump:
+ destroy_ramdump_device(drv->sw_ramdump_dev);
+err_sw_ramdump:
+ destroy_ramdump_device(drv->fw_ramdump_dev);
+err_fw_ramdump:
+ if (drv->loadable)
+ msm_pil_unregister(drv_sw->pil);
+err_pil_sw:
+ msm_pil_unregister(drv_fw->pil);
+ return ret;
+}
+
+static int __devexit pil_q6v4_modem_driver_exit(struct platform_device *pdev)
+{
+ struct q6v4_modem *drv = platform_get_drvdata(pdev);
+
+ smsm_state_cb_deregister(SMSM_MODEM_STATE, SMSM_RESET,
+ smsm_state_cb, drv);
+ subsys_unregister(drv->subsys);
+ destroy_ramdump_device(drv->smem_ramdump_dev);
+ destroy_ramdump_device(drv->sw_ramdump_dev);
+ destroy_ramdump_device(drv->fw_ramdump_dev);
+ if (drv->loadable) {
+ msm_pil_unregister(drv->q6_sw.pil);
+ msm_pil_unregister(drv->q6_fw.pil);
+ }
+ return 0;
+}
+
+static struct platform_driver pil_q6v4_modem_driver = {
+ .probe = pil_q6v4_modem_driver_probe,
+ .remove = __devexit_p(pil_q6v4_modem_driver_exit),
+ .driver = {
+ .name = "pil-q6v4-modem",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pil_q6v4_modem_init(void)
+{
+ return platform_driver_register(&pil_q6v4_modem_driver);
+}
+module_init(pil_q6v4_modem_init);
+
+static void __exit pil_q6v4_modem_exit(void)
+{
+ platform_driver_unregister(&pil_q6v4_modem_driver);
+}
+module_exit(pil_q6v4_modem_exit);
+
+MODULE_DESCRIPTION("Support for booting QDSP6v4 (Hexagon) processors");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/pil-q6v4.c b/arch/arm/mach-msm/pil-q6v4.c
index 32cce1d..47033fc 100644
--- a/arch/arm/mach-msm/pil-q6v4.c
+++ b/arch/arm/mach-msm/pil-q6v4.c
@@ -22,7 +22,6 @@
#include <linux/clk.h>
#include <mach/msm_bus.h>
-#include <mach/msm_iomap.h>
#include "peripheral-loader.h"
#include "pil-q6v4.h"
@@ -35,12 +34,6 @@
#define QDSP6SS_GFMUX_CTL 0x30
#define QDSP6SS_PWR_CTL 0x38
-#define MSS_S_HCLK_CTL (MSM_CLK_CTL_BASE + 0x2C70)
-#define MSS_SLP_CLK_CTL (MSM_CLK_CTL_BASE + 0x2C60)
-#define SFAB_MSS_M_ACLK_CTL (MSM_CLK_CTL_BASE + 0x2340)
-#define SFAB_MSS_S_HCLK_CTL (MSM_CLK_CTL_BASE + 0x2C00)
-#define MSS_RESET (MSM_CLK_CTL_BASE + 0x2C64)
-
#define Q6SS_SS_ARES BIT(0)
#define Q6SS_CORE_ARES BIT(1)
#define Q6SS_ISDB_ARES BIT(2)
@@ -59,29 +52,19 @@
#define Q6SS_CLK_ENA BIT(1)
#define Q6SS_SRC_SWITCH_CLK_OVR BIT(8)
-struct q6v4_data {
- void __iomem *base;
- void __iomem *modem_base;
- unsigned long start_addr;
- struct regulator *vreg;
- struct regulator *pll_supply;
- bool vreg_enabled;
- struct clk *xo;
- struct pil_device *pil;
-};
-
-static int pil_q6v4_init_image(struct pil_desc *pil, const u8 *metadata,
+int pil_q6v4_init_image(struct pil_desc *pil, const u8 *metadata,
size_t size)
{
const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
- struct q6v4_data *drv = dev_get_drvdata(pil->dev);
+ struct q6v4_data *drv = pil_to_q6v4_data(pil);
drv->start_addr = ehdr->e_entry;
return 0;
}
+EXPORT_SYMBOL(pil_q6v4_init_image);
-static int pil_q6v4_make_proxy_votes(struct pil_desc *pil)
+int pil_q6v4_make_proxy_votes(struct pil_desc *pil)
{
- const struct q6v4_data *drv = dev_get_drvdata(pil->dev);
+ const struct q6v4_data *drv = pil_to_q6v4_data(pil);
int ret;
ret = clk_prepare_enable(drv->xo);
@@ -102,19 +85,21 @@
err:
return ret;
}
+EXPORT_SYMBOL(pil_q6v4_make_proxy_votes);
-static void pil_q6v4_remove_proxy_votes(struct pil_desc *pil)
+void pil_q6v4_remove_proxy_votes(struct pil_desc *pil)
{
- const struct q6v4_data *drv = dev_get_drvdata(pil->dev);
+ const struct q6v4_data *drv = pil_to_q6v4_data(pil);
if (drv->pll_supply)
regulator_disable(drv->pll_supply);
clk_disable_unprepare(drv->xo);
}
+EXPORT_SYMBOL(pil_q6v4_remove_proxy_votes);
-static int pil_q6v4_power_up(struct device *dev)
+int pil_q6v4_power_up(struct q6v4_data *drv)
{
int err;
- struct q6v4_data *drv = dev_get_drvdata(dev);
+ struct device *dev = drv->desc.dev;
err = regulator_set_voltage(drv->vreg, 743750, 743750);
if (err) {
@@ -141,68 +126,27 @@
drv->vreg_enabled = true;
return 0;
}
+EXPORT_SYMBOL(pil_q6v4_power_up);
-static DEFINE_MUTEX(pil_q6v4_modem_lock);
-static unsigned pil_q6v4_modem_count;
-
-/* Bring modem subsystem out of reset */
-static void pil_q6v4_init_modem(void __iomem *base, void __iomem *jtag_clk)
+void pil_q6v4_power_down(struct q6v4_data *drv)
{
- mutex_lock(&pil_q6v4_modem_lock);
- if (!pil_q6v4_modem_count) {
- /* Enable MSS clocks */
- writel_relaxed(0x10, SFAB_MSS_M_ACLK_CTL);
- writel_relaxed(0x10, SFAB_MSS_S_HCLK_CTL);
- writel_relaxed(0x10, MSS_S_HCLK_CTL);
- writel_relaxed(0x10, MSS_SLP_CLK_CTL);
- /* Wait for clocks to enable */
- mb();
- udelay(10);
-
- /* De-assert MSS reset */
- writel_relaxed(0x0, MSS_RESET);
- mb();
- udelay(10);
- /* Enable MSS */
- writel_relaxed(0x7, base);
+ if (drv->vreg_enabled) {
+ regulator_disable(drv->vreg);
+ drv->vreg_enabled = false;
}
-
- /* Enable JTAG clocks */
- /* TODO: Remove if/when Q6 software enables them? */
- writel_relaxed(0x10, jtag_clk);
-
- pil_q6v4_modem_count++;
- mutex_unlock(&pil_q6v4_modem_lock);
}
+EXPORT_SYMBOL(pil_q6v4_power_down);
-/* Put modem subsystem back into reset */
-static void pil_q6v4_shutdown_modem(void)
-{
- mutex_lock(&pil_q6v4_modem_lock);
- if (pil_q6v4_modem_count)
- pil_q6v4_modem_count--;
- if (pil_q6v4_modem_count == 0)
- writel_relaxed(0x1, MSS_RESET);
- mutex_unlock(&pil_q6v4_modem_lock);
-}
-
-static int pil_q6v4_reset(struct pil_desc *pil)
+int pil_q6v4_boot(struct pil_desc *pil)
{
u32 reg, err;
- const struct q6v4_data *drv = dev_get_drvdata(pil->dev);
- const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
+ const struct q6v4_data *drv = pil_to_q6v4_data(pil);
- err = pil_q6v4_power_up(pil->dev);
- if (err)
- return err;
/* Enable Q6 ACLK */
- writel_relaxed(0x10, pdata->aclk_reg);
-
- if (drv->modem_base)
- pil_q6v4_init_modem(drv->modem_base, pdata->jtag_clk_reg);
+ writel_relaxed(0x10, drv->aclk_reg);
/* Unhalt bus port */
- err = msm_bus_axi_portunhalt(pdata->bus_port);
+ err = msm_bus_axi_portunhalt(drv->bus_port);
if (err)
dev_err(pil->dev, "Failed to unhalt bus port\n");
@@ -216,8 +160,8 @@
drv->base + QDSP6SS_RST_EVB);
/* Program TCM and AHB address ranges */
- writel_relaxed(pdata->strap_tcm_base, drv->base + QDSP6SS_STRAP_TCM);
- writel_relaxed(pdata->strap_ahb_upper | pdata->strap_ahb_lower,
+ writel_relaxed(drv->strap_tcm_base, drv->base + QDSP6SS_STRAP_TCM);
+ writel_relaxed(drv->strap_ahb_upper | drv->strap_ahb_lower,
drv->base + QDSP6SS_STRAP_AHB);
/* Turn off Q6 core clock */
@@ -257,15 +201,15 @@
return 0;
}
+EXPORT_SYMBOL(pil_q6v4_boot);
-static int pil_q6v4_shutdown(struct pil_desc *pil)
+int pil_q6v4_shutdown(struct pil_desc *pil)
{
u32 reg;
- struct q6v4_data *drv = dev_get_drvdata(pil->dev);
- const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
+ struct q6v4_data *drv = pil_to_q6v4_data(pil);
/* Make sure bus port is halted */
- msm_bus_axi_porthalt(pdata->bus_port);
+ msm_bus_axi_porthalt(drv->bus_port);
/* Turn off Q6 core clock */
writel_relaxed(Q6SS_SRC_SWITCH_CLK_OVR,
@@ -279,188 +223,67 @@
/* Turn off Q6 memories */
writel_relaxed(Q6SS_CLAMP_IO, drv->base + QDSP6SS_PWR_CTL);
- if (drv->modem_base)
- pil_q6v4_shutdown_modem();
-
- if (drv->vreg_enabled) {
- regulator_disable(drv->vreg);
- drv->vreg_enabled = false;
- }
-
return 0;
}
+EXPORT_SYMBOL(pil_q6v4_shutdown);
-static struct pil_reset_ops pil_q6v4_ops = {
- .init_image = pil_q6v4_init_image,
- .auth_and_reset = pil_q6v4_reset,
- .shutdown = pil_q6v4_shutdown,
- .proxy_vote = pil_q6v4_make_proxy_votes,
- .proxy_unvote = pil_q6v4_remove_proxy_votes,
-};
-
-static int pil_q6v4_init_image_trusted(struct pil_desc *pil,
+int pil_q6v4_init_image_trusted(struct pil_desc *pil,
const u8 *metadata, size_t size)
{
- const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
- return pas_init_image(pdata->pas_id, metadata, size);
+ struct q6v4_data *drv = pil_to_q6v4_data(pil);
+ return pas_init_image(drv->pas_id, metadata, size);
}
+EXPORT_SYMBOL(pil_q6v4_init_image_trusted);
-static int pil_q6v4_reset_trusted(struct pil_desc *pil)
+int pil_q6v4_boot_trusted(struct pil_desc *pil)
{
- const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
+ struct q6v4_data *drv = pil_to_q6v4_data(pil);
int err;
- err = pil_q6v4_power_up(pil->dev);
+ err = pil_q6v4_power_up(drv);
if (err)
return err;
/* Unhalt bus port */
- err = msm_bus_axi_portunhalt(pdata->bus_port);
+ err = msm_bus_axi_portunhalt(drv->bus_port);
if (err)
dev_err(pil->dev, "Failed to unhalt bus port\n");
- return pas_auth_and_reset(pdata->pas_id);
+ return pas_auth_and_reset(drv->pas_id);
}
+EXPORT_SYMBOL(pil_q6v4_boot_trusted);
-static int pil_q6v4_shutdown_trusted(struct pil_desc *pil)
+int pil_q6v4_shutdown_trusted(struct pil_desc *pil)
{
int ret;
- struct q6v4_data *drv = dev_get_drvdata(pil->dev);
- struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
+ struct q6v4_data *drv = pil_to_q6v4_data(pil);
/* Make sure bus port is halted */
- msm_bus_axi_porthalt(pdata->bus_port);
+ msm_bus_axi_porthalt(drv->bus_port);
- ret = pas_shutdown(pdata->pas_id);
+ ret = pas_shutdown(drv->pas_id);
if (ret)
return ret;
- if (drv->vreg_enabled) {
- regulator_disable(drv->vreg);
- drv->vreg_enabled = false;
- }
+ pil_q6v4_power_down(drv);
return ret;
}
+EXPORT_SYMBOL(pil_q6v4_shutdown_trusted);
-static struct pil_reset_ops pil_q6v4_ops_trusted = {
- .init_image = pil_q6v4_init_image_trusted,
- .auth_and_reset = pil_q6v4_reset_trusted,
- .shutdown = pil_q6v4_shutdown_trusted,
- .proxy_vote = pil_q6v4_make_proxy_votes,
- .proxy_unvote = pil_q6v4_remove_proxy_votes,
-};
-
-static int __devinit pil_q6v4_driver_probe(struct platform_device *pdev)
+void __devinit
+pil_q6v4_init(struct q6v4_data *drv, const struct pil_q6v4_pdata *pdata)
{
- const struct pil_q6v4_pdata *pdata = pdev->dev.platform_data;
- struct q6v4_data *drv;
- struct resource *res;
- struct pil_desc *desc;
- int ret;
+ drv->strap_tcm_base = pdata->strap_tcm_base;
+ drv->strap_ahb_upper = pdata->strap_ahb_upper;
+ drv->strap_ahb_lower = pdata->strap_ahb_lower;
+ drv->aclk_reg = pdata->aclk_reg;
+ drv->jtag_clk_reg = pdata->jtag_clk_reg;
+ drv->pas_id = pdata->pas_id;
+ drv->bus_port = pdata->bus_port;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -EINVAL;
-
- drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
- if (!drv)
- return -ENOMEM;
- platform_set_drvdata(pdev, drv);
-
- drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
- if (!drv->base)
- return -ENOMEM;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (res) {
- drv->modem_base = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- if (!drv->modem_base)
- return -ENOMEM;
- }
-
- desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
- if (!desc)
- return -ENOMEM;
-
- drv->pll_supply = devm_regulator_get(&pdev->dev, "pll_vdd");
- if (IS_ERR(drv->pll_supply)) {
- drv->pll_supply = NULL;
- } else {
- ret = regulator_set_voltage(drv->pll_supply, 1800000, 1800000);
- if (ret) {
- dev_err(&pdev->dev, "failed to set pll voltage\n");
- return ret;
- }
-
- ret = regulator_set_optimum_mode(drv->pll_supply, 100000);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to set pll optimum mode\n");
- return ret;
- }
- }
-
- desc->name = pdata->name;
- desc->depends_on = pdata->depends;
- desc->dev = &pdev->dev;
- desc->owner = THIS_MODULE;
- desc->proxy_timeout = 10000;
-
- if (pas_supported(pdata->pas_id) > 0) {
- desc->ops = &pil_q6v4_ops_trusted;
- dev_info(&pdev->dev, "using secure boot\n");
- } else {
- desc->ops = &pil_q6v4_ops;
- dev_info(&pdev->dev, "using non-secure boot\n");
- }
-
- drv->vreg = devm_regulator_get(&pdev->dev, "core_vdd");
- if (IS_ERR(drv->vreg))
- return PTR_ERR(drv->vreg);
-
- ret = regulator_set_optimum_mode(drv->vreg, 100000);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed to set regulator's mode.\n");
- return ret;
- }
-
- drv->xo = devm_clk_get(&pdev->dev, "xo");
- if (IS_ERR(drv->xo))
- return PTR_ERR(drv->xo);
-
- drv->pil = msm_pil_register(desc);
- if (IS_ERR(drv->pil))
- return PTR_ERR(drv->pil);
- return 0;
+ regulator_set_optimum_mode(drv->vreg, 100000);
}
-
-static int __devexit pil_q6v4_driver_exit(struct platform_device *pdev)
-{
- struct q6v4_data *drv = platform_get_drvdata(pdev);
- msm_pil_unregister(drv->pil);
- return 0;
-}
-
-static struct platform_driver pil_q6v4_driver = {
- .probe = pil_q6v4_driver_probe,
- .remove = __devexit_p(pil_q6v4_driver_exit),
- .driver = {
- .name = "pil_qdsp6v4",
- .owner = THIS_MODULE,
- },
-};
-
-static int __init pil_q6v4_init(void)
-{
- return platform_driver_register(&pil_q6v4_driver);
-}
-module_init(pil_q6v4_init);
-
-static void __exit pil_q6v4_exit(void)
-{
- platform_driver_unregister(&pil_q6v4_driver);
-}
-module_exit(pil_q6v4_exit);
+EXPORT_SYMBOL(pil_q6v4_init);
MODULE_DESCRIPTION("Support for booting QDSP6v4 (Hexagon) processors");
MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/pil-q6v4.h b/arch/arm/mach-msm/pil-q6v4.h
index b0b97d0..0395bed 100644
--- a/arch/arm/mach-msm/pil-q6v4.h
+++ b/arch/arm/mach-msm/pil-q6v4.h
@@ -12,6 +12,8 @@
#ifndef __MSM_PIL_Q6V4_H
#define __MSM_PIL_Q6V4_H
+#include "peripheral-loader.h"
+
struct pil_q6v4_pdata {
const unsigned long strap_tcm_base;
const unsigned long strap_ahb_upper;
@@ -23,4 +25,52 @@
const unsigned pas_id;
int bus_port;
};
+
+struct clk;
+struct pil_device;
+struct regulator;
+
+/**
+ * struct q6v4_data - Q6 processor
+ */
+struct q6v4_data {
+ void __iomem *base;
+ void __iomem *wdog_base;
+ unsigned long start_addr;
+ unsigned long strap_tcm_base;
+ unsigned long strap_ahb_upper;
+ unsigned long strap_ahb_lower;
+ void __iomem *aclk_reg;
+ void __iomem *jtag_clk_reg;
+ unsigned pas_id;
+ int bus_port;
+ int wdog_irq;
+
+ struct regulator *vreg;
+ struct regulator *pll_supply;
+ bool vreg_enabled;
+ struct clk *xo;
+
+ struct pil_device *pil;
+ struct pil_desc desc;
+};
+
+#define pil_to_q6v4_data(p) container_of(p, struct q6v4_data, desc)
+
+extern int pil_q6v4_init_image(struct pil_desc *pil, const u8 *metadata,
+ size_t size);
+extern int pil_q6v4_make_proxy_votes(struct pil_desc *pil);
+extern void pil_q6v4_remove_proxy_votes(struct pil_desc *pil);
+extern int pil_q6v4_power_up(struct q6v4_data *drv);
+extern void pil_q6v4_power_down(struct q6v4_data *drv);
+extern int pil_q6v4_boot(struct pil_desc *pil);
+extern int pil_q6v4_shutdown(struct pil_desc *pil);
+
+extern int pil_q6v4_init_image_trusted(struct pil_desc *pil,
+ const u8 *metadata, size_t size);
+extern int pil_q6v4_boot_trusted(struct pil_desc *pil);
+extern int pil_q6v4_shutdown_trusted(struct pil_desc *pil);
+extern void __devinit
+pil_q6v4_init(struct q6v4_data *drv, const struct pil_q6v4_pdata *p);
+
#endif
diff --git a/arch/arm/mach-msm/pil-q6v5-lpass.c b/arch/arm/mach-msm/pil-q6v5-lpass.c
index ed072ea..c48ea02 100644
--- a/arch/arm/mach-msm/pil-q6v5-lpass.c
+++ b/arch/arm/mach-msm/pil-q6v5-lpass.c
@@ -18,14 +18,39 @@
#include <linux/err.h>
#include <linux/of.h>
#include <linux/clk.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
#include <mach/clk.h>
+#include <mach/subsystem_restart.h>
+#include <mach/subsystem_notif.h>
+#include <mach/scm.h>
+#include <mach/peripheral-loader.h>
+
#include "peripheral-loader.h"
#include "pil-q6v5.h"
#include "scm-pas.h"
+#include "ramdump.h"
+#include "sysmon.h"
#define QDSP6SS_RST_EVB 0x010
#define PROXY_TIMEOUT_MS 10000
+struct lpass_data {
+ struct q6v5_data *q6;
+ struct subsys_device *subsys;
+ struct subsys_desc subsys_desc;
+ void *ramdump_dev;
+ int wdog_irq;
+ struct work_struct work;
+ void *riva_notif_hdle;
+ void *modem_notif_hdle;
+ int crash_shutdown;
+};
+
+#define subsys_to_drv(d) container_of(d, struct lpass_data, subsys_desc)
+
static int pil_lpass_enable_clks(struct q6v5_data *drv)
{
int ret;
@@ -71,7 +96,7 @@
static int pil_lpass_shutdown(struct pil_desc *pil)
{
- struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+ struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
pil_q6v5_halt_axi_port(pil, drv->axi_halt_base);
@@ -93,7 +118,7 @@
static int pil_lpass_reset(struct pil_desc *pil)
{
- struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+ struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
int ret;
ret = pil_lpass_enable_clks(drv);
@@ -147,38 +172,211 @@
.shutdown = pil_lpass_shutdown_trusted,
};
+static int riva_notifier_cb(struct notifier_block *this, unsigned long code,
+ void *ss_handle)
+{
+ int ret;
+ switch (code) {
+ case SUBSYS_BEFORE_SHUTDOWN:
+ pr_debug("%s: R-Notify: Shutdown started\n", __func__);
+ ret = sysmon_send_event(SYSMON_SS_LPASS, "wcnss",
+ SUBSYS_BEFORE_SHUTDOWN);
+ if (ret < 0)
+ pr_err("%s: sysmon_send_event error %d", __func__, ret);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block rnb = {
+ .notifier_call = riva_notifier_cb,
+};
+
+static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
+ void *ss_handle)
+{
+ int ret;
+ switch (code) {
+ case SUBSYS_BEFORE_SHUTDOWN:
+ pr_debug("%s: M-Notify: Shutdown started\n", __func__);
+ ret = sysmon_send_event(SYSMON_SS_LPASS, "modem",
+ SUBSYS_BEFORE_SHUTDOWN);
+ if (ret < 0)
+ pr_err("%s: sysmon_send_event error %d", __func__, ret);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block mnb = {
+ .notifier_call = modem_notifier_cb,
+};
+
+static void adsp_log_failure_reason(void)
+{
+ char *reason;
+ char buffer[81];
+ unsigned size;
+
+ reason = smem_get_entry(SMEM_SSR_REASON_LPASS0, &size);
+
+ if (!reason) {
+ pr_err("ADSP subsystem failure reason: (unknown, smem_get_entry failed).");
+ return;
+ }
+
+ if (reason[0] == '\0') {
+ pr_err("ADSP subsystem failure reason: (unknown, init value found)");
+ return;
+ }
+
+ size = min(size, sizeof(buffer) - 1);
+ memcpy(buffer, reason, size);
+ buffer[size] = '\0';
+ pr_err("ADSP subsystem failure reason: %s", buffer);
+ memset((void *)reason, 0x0, size);
+ wmb();
+}
+
+static void restart_adsp(struct lpass_data *drv)
+{
+ adsp_log_failure_reason();
+ subsystem_restart_dev(drv->subsys);
+}
+
+static void adsp_fatal_fn(struct work_struct *work)
+{
+ struct lpass_data *drv = container_of(work, struct lpass_data, work);
+
+ pr_err("Watchdog bite received from ADSP!\n");
+ restart_adsp(drv);
+}
+
+static void adsp_smsm_state_cb(void *data, uint32_t old_state,
+ uint32_t new_state)
+{
+ struct lpass_data *drv = data;
+
+ /* Ignore if we're the one that set SMSM_RESET */
+ if (drv->crash_shutdown)
+ return;
+
+ if (new_state & SMSM_RESET) {
+ pr_err("%s: ADSP SMSM state changed to SMSM_RESET, new_state = %#x, old_state = %#x\n",
+ __func__, new_state, old_state);
+ restart_adsp(drv);
+ }
+}
+
+#define SCM_Q6_NMI_CMD 0x1
+
+static void send_q6_nmi(void)
+{
+ /* Send NMI to QDSP6 via an SCM call. */
+ scm_call_atomic1(SCM_SVC_UTIL, SCM_Q6_NMI_CMD, 0x1);
+ pr_debug("%s: Q6 NMI was sent.\n", __func__);
+}
+
+#define subsys_to_lpass(d) container_of(d, struct lpass_data, subsys_desc)
+
+static int adsp_shutdown(const struct subsys_desc *subsys)
+{
+ struct lpass_data *drv = subsys_to_lpass(subsys);
+
+ send_q6_nmi();
+ /* The write needs to go through before the q6 is shutdown. */
+ mb();
+ pil_force_shutdown("adsp");
+ disable_irq_nosync(drv->wdog_irq);
+
+ return 0;
+}
+
+static int adsp_powerup(const struct subsys_desc *subsys)
+{
+ struct lpass_data *drv = subsys_to_lpass(subsys);
+ int ret = 0;
+
+ if (get_restart_level() == RESET_SUBSYS_INDEPENDENT) {
+ pr_debug("%s: Wait for ADSP power up!", __func__);
+ msleep(10000);
+ }
+
+ ret = pil_force_boot("adsp");
+ enable_irq(drv->wdog_irq);
+
+ return ret;
+}
+
+static struct ramdump_segment segments = { 0xdc00000, 0x1800000 };
+
+static int adsp_ramdump(int enable, const struct subsys_desc *subsys)
+{
+ struct lpass_data *drv = subsys_to_lpass(subsys);
+
+ if (!enable)
+ return 0;
+ return do_ramdump(drv->ramdump_dev, &segments, 1);
+}
+
+static void adsp_crash_shutdown(const struct subsys_desc *subsys)
+{
+ struct lpass_data *drv = subsys_to_lpass(subsys);
+
+ drv->crash_shutdown = 1;
+ send_q6_nmi();
+}
+
+static irqreturn_t adsp_wdog_bite_irq(int irq, void *dev_id)
+{
+ struct lpass_data *drv = dev_id;
+
+ disable_irq_nosync(drv->wdog_irq);
+ schedule_work(&drv->work);
+
+ return IRQ_HANDLED;
+}
+
static int __devinit pil_lpass_driver_probe(struct platform_device *pdev)
{
- struct q6v5_data *drv;
+ struct lpass_data *drv;
+ struct q6v5_data *q6;
struct pil_desc *desc;
+ int ret;
- desc = pil_q6v5_init(pdev);
- if (IS_ERR(desc))
- return PTR_ERR(desc);
+ drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, drv);
- drv = platform_get_drvdata(pdev);
- if (drv == NULL)
- return -ENODEV;
+ drv->wdog_irq = platform_get_irq(pdev, 0);
+ if (drv->wdog_irq < 0)
+ return drv->wdog_irq;
- desc->ops = &pil_lpass_ops;
+ q6 = pil_q6v5_init(pdev);
+ if (IS_ERR(q6))
+ return PTR_ERR(q6);
+ drv->q6 = q6;
+
+ desc = &q6->desc;
desc->owner = THIS_MODULE;
desc->proxy_timeout = PROXY_TIMEOUT_MS;
- drv->core_clk = devm_clk_get(&pdev->dev, "core_clk");
- if (IS_ERR(drv->core_clk))
- return PTR_ERR(drv->core_clk);
+ q6->core_clk = devm_clk_get(&pdev->dev, "core_clk");
+ if (IS_ERR(q6->core_clk))
+ return PTR_ERR(q6->core_clk);
- drv->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk");
- if (IS_ERR(drv->ahb_clk))
- return PTR_ERR(drv->ahb_clk);
+ q6->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk");
+ if (IS_ERR(q6->ahb_clk))
+ return PTR_ERR(q6->ahb_clk);
- drv->axi_clk = devm_clk_get(&pdev->dev, "bus_clk");
- if (IS_ERR(drv->axi_clk))
- return PTR_ERR(drv->axi_clk);
+ q6->axi_clk = devm_clk_get(&pdev->dev, "bus_clk");
+ if (IS_ERR(q6->axi_clk))
+ return PTR_ERR(q6->axi_clk);
- drv->reg_clk = devm_clk_get(&pdev->dev, "reg_clk");
- if (IS_ERR(drv->reg_clk))
- return PTR_ERR(drv->reg_clk);
+ q6->reg_clk = devm_clk_get(&pdev->dev, "reg_clk");
+ if (IS_ERR(q6->reg_clk))
+ return PTR_ERR(q6->reg_clk);
if (pas_supported(PAS_Q6) > 0) {
desc->ops = &pil_lpass_ops_trusted;
@@ -188,17 +386,79 @@
dev_info(&pdev->dev, "using non-secure boot\n");
}
- drv->pil = msm_pil_register(desc);
- if (IS_ERR(drv->pil))
- return PTR_ERR(drv->pil);
+ drv->q6->pil = msm_pil_register(desc);
+ if (IS_ERR(drv->q6->pil))
+ return PTR_ERR(drv->q6->pil);
+ drv->subsys_desc.name = desc->name;
+ drv->subsys_desc.owner = THIS_MODULE;
+ drv->subsys_desc.dev = &pdev->dev;
+ drv->subsys_desc.shutdown = adsp_shutdown;
+ drv->subsys_desc.powerup = adsp_powerup;
+ drv->subsys_desc.ramdump = adsp_ramdump;
+ drv->subsys_desc.crash_shutdown = adsp_crash_shutdown;
+
+ INIT_WORK(&drv->work, adsp_fatal_fn);
+
+ drv->ramdump_dev = create_ramdump_device("adsp");
+ if (!drv->ramdump_dev) {
+ ret = -ENOMEM;
+ goto err_ramdump;
+ }
+
+ drv->subsys = subsys_register(&drv->subsys_desc);
+ if (IS_ERR(drv->subsys)) {
+ ret = PTR_ERR(drv->subsys);
+ goto err_subsys;
+ }
+
+ ret = devm_request_irq(&pdev->dev, drv->wdog_irq, adsp_wdog_bite_irq,
+ IRQF_TRIGGER_RISING, dev_name(&pdev->dev), drv);
+ if (ret)
+ goto err_irq;
+
+ ret = smsm_state_cb_register(SMSM_Q6_STATE, SMSM_RESET,
+ adsp_smsm_state_cb, drv);
+ if (ret < 0)
+ goto err_smsm;
+
+ drv->riva_notif_hdle = subsys_notif_register_notifier("riva", &rnb);
+ if (IS_ERR(drv->riva_notif_hdle)) {
+ ret = PTR_ERR(drv->riva_notif_hdle);
+ goto err_notif_riva;
+ }
+
+ drv->modem_notif_hdle = subsys_notif_register_notifier("modem", &mnb);
+ if (IS_ERR(drv->modem_notif_hdle)) {
+ ret = PTR_ERR(drv->modem_notif_hdle);
+ goto err_notif_modem;
+ }
+ return 0;
+err_notif_modem:
+ subsys_notif_unregister_notifier(drv->riva_notif_hdle, &rnb);
+err_notif_riva:
+ smsm_state_cb_deregister(SMSM_Q6_STATE, SMSM_RESET,
+ adsp_smsm_state_cb, drv);
+err_smsm:
+err_irq:
+ subsys_unregister(drv->subsys);
+err_subsys:
+ destroy_ramdump_device(drv->ramdump_dev);
+err_ramdump:
+ msm_pil_unregister(drv->q6->pil);
return 0;
}
static int __devexit pil_lpass_driver_exit(struct platform_device *pdev)
{
- struct q6v5_data *drv = platform_get_drvdata(pdev);
- msm_pil_unregister(drv->pil);
+ struct lpass_data *drv = platform_get_drvdata(pdev);
+ subsys_notif_unregister_notifier(drv->riva_notif_hdle, &rnb);
+ subsys_notif_unregister_notifier(drv->modem_notif_hdle, &mnb);
+ smsm_state_cb_deregister(SMSM_Q6_STATE, SMSM_RESET,
+ adsp_smsm_state_cb, drv);
+ subsys_unregister(drv->subsys);
+ destroy_ramdump_device(drv->ramdump_dev);
+ msm_pil_unregister(drv->q6->pil);
return 0;
}
diff --git a/arch/arm/mach-msm/pil-q6v5-mss.c b/arch/arm/mach-msm/pil-q6v5-mss.c
index 7b45a0e..f3c731f 100644
--- a/arch/arm/mach-msm/pil-q6v5-mss.c
+++ b/arch/arm/mach-msm/pil-q6v5-mss.c
@@ -57,10 +57,10 @@
static int pbl_mba_boot_timeout_ms = 100;
module_param(pbl_mba_boot_timeout_ms, int, S_IRUGO | S_IWUSR);
-static int pil_mss_power_up(struct device *dev)
+static int pil_mss_power_up(struct q6v5_data *drv)
{
int ret;
- struct q6v5_data *drv = dev_get_drvdata(dev);
+ struct device *dev = drv->desc.dev;
ret = regulator_enable(drv->vreg);
if (ret)
@@ -69,17 +69,14 @@
return ret;
}
-static int pil_mss_power_down(struct device *dev)
+static int pil_mss_power_down(struct q6v5_data *drv)
{
- struct q6v5_data *drv = dev_get_drvdata(dev);
-
return regulator_disable(drv->vreg);
}
static int pil_mss_enable_clks(struct q6v5_data *drv)
{
int ret;
- void __iomem *mpll1_config_ctl;
ret = clk_prepare_enable(drv->ahb_clk);
if (ret)
@@ -91,12 +88,6 @@
if (ret)
goto err_rom_clk;
- /* TODO: Remove when support for 8974v1.0 HW is dropped. */
- mpll1_config_ctl = ioremap(0xFC981034, 0x4);
- writel_relaxed(0x0300403D, mpll1_config_ctl);
- mb();
- iounmap(mpll1_config_ctl);
-
return 0;
err_rom_clk:
@@ -114,9 +105,9 @@
clk_disable_unprepare(drv->ahb_clk);
}
-static int wait_for_mba_ready(struct device *dev)
+static int wait_for_mba_ready(struct q6v5_data *drv)
{
- struct q6v5_data *drv = dev_get_drvdata(dev);
+ struct device *dev = drv->desc.dev;
int ret;
u32 status;
@@ -150,7 +141,7 @@
static int pil_mss_shutdown(struct pil_desc *pil)
{
- struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+ struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_Q6_HALT_BASE);
pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_MODEM_HALT_BASE);
@@ -162,13 +153,13 @@
* writes performed during the shutdown succeed.
*/
if (drv->is_booted == false) {
- pil_mss_power_up(pil->dev);
+ pil_mss_power_up(drv);
pil_mss_enable_clks(drv);
}
pil_q6v5_shutdown(pil);
pil_mss_disable_clks(drv);
- pil_mss_power_down(pil->dev);
+ pil_mss_power_down(drv);
writel_relaxed(1, drv->restart_reg);
@@ -179,7 +170,7 @@
static int pil_mss_reset(struct pil_desc *pil)
{
- struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+ struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
int ret;
/* Deassert reset to subsystem and wait for propagation */
@@ -191,7 +182,7 @@
* Bring subsystem out of reset and enable required
* regulators and clocks.
*/
- ret = pil_mss_power_up(pil->dev);
+ ret = pil_mss_power_up(drv);
if (ret)
goto err_power;
@@ -218,7 +209,7 @@
/* Wait for MBA to start. Check for PBL and MBA errors while waiting. */
if (drv->self_auth) {
- ret = wait_for_mba_ready(pil->dev);
+ ret = wait_for_mba_ready(drv);
if (ret)
goto err_auth;
}
@@ -232,7 +223,7 @@
err_q6v5_reset:
pil_mss_disable_clks(drv);
err_clks:
- pil_mss_power_down(pil->dev);
+ pil_mss_power_down(drv);
err_power:
return ret;
}
@@ -252,13 +243,12 @@
struct resource *res;
int ret;
- desc = pil_q6v5_init(pdev);
- if (IS_ERR(desc))
- return PTR_ERR(desc);
- drv = platform_get_drvdata(pdev);
- if (drv == NULL)
- return -ENODEV;
+ drv = pil_q6v5_init(pdev);
+ if (IS_ERR(drv))
+ return PTR_ERR(drv);
+ platform_set_drvdata(pdev, drv);
+ desc = &drv->desc;
desc->ops = &pil_mss_ops;
desc->owner = THIS_MODULE;
desc->proxy_timeout = PROXY_TIMEOUT_MS;
diff --git a/arch/arm/mach-msm/pil-q6v5.c b/arch/arm/mach-msm/pil-q6v5.c
index f4e8844..70a12de 100644
--- a/arch/arm/mach-msm/pil-q6v5.c
+++ b/arch/arm/mach-msm/pil-q6v5.c
@@ -61,7 +61,7 @@
int pil_q6v5_make_proxy_votes(struct pil_desc *pil)
{
int ret;
- struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+ struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
ret = clk_prepare_enable(drv->xo);
if (ret) {
@@ -74,7 +74,7 @@
void pil_q6v5_remove_proxy_votes(struct pil_desc *pil)
{
- struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+ struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
clk_disable_unprepare(drv->xo);
}
EXPORT_SYMBOL(pil_q6v5_remove_proxy_votes);
@@ -104,7 +104,7 @@
size_t size)
{
const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
- struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+ struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
drv->start_addr = ehdr->e_entry;
return 0;
}
@@ -113,7 +113,7 @@
void pil_q6v5_shutdown(struct pil_desc *pil)
{
u32 val;
- struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+ struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
/* Turn off core clock */
val = readl_relaxed(drv->reg_base + QDSP6SS_GFMUX_CTL);
@@ -145,7 +145,7 @@
int pil_q6v5_reset(struct pil_desc *pil)
{
- struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+ struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
u32 val;
/* Assert resets, stop core */
@@ -193,7 +193,7 @@
}
EXPORT_SYMBOL(pil_q6v5_reset);
-struct pil_desc __devinit *pil_q6v5_init(struct platform_device *pdev)
+struct q6v5_data __devinit *pil_q6v5_init(struct platform_device *pdev)
{
struct q6v5_data *drv;
struct resource *res;
@@ -203,7 +203,6 @@
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
if (!drv)
return ERR_PTR(-ENOMEM);
- platform_set_drvdata(pdev, drv);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6_base");
if (!res)
@@ -218,10 +217,7 @@
if (!drv->axi_halt_base)
return ERR_PTR(-ENOMEM);
- desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
- if (!desc)
- return ERR_PTR(-ENOMEM);
-
+ desc = &drv->desc;
ret = of_property_read_string(pdev->dev.of_node, "qcom,firmware-name",
&desc->name);
if (ret)
@@ -233,6 +229,6 @@
desc->dev = &pdev->dev;
- return desc;
+ return drv;
}
EXPORT_SYMBOL(pil_q6v5_init);
diff --git a/arch/arm/mach-msm/pil-q6v5.h b/arch/arm/mach-msm/pil-q6v5.h
index 03f93fa..f176d2d 100644
--- a/arch/arm/mach-msm/pil-q6v5.h
+++ b/arch/arm/mach-msm/pil-q6v5.h
@@ -13,10 +13,11 @@
#ifndef __MSM_PIL_Q6V5_H
#define __MSM_PIL_Q6V5_H
+#include "peripheral-loader.h"
+
struct regulator;
struct clk;
struct pil_device;
-struct pil_desc;
struct platform_device;
struct q6v5_data {
@@ -36,6 +37,7 @@
bool is_booted;
int self_auth;
struct pil_device *pil;
+ struct pil_desc desc;
};
int pil_q6v5_make_proxy_votes(struct pil_desc *pil);
@@ -45,6 +47,6 @@
size_t size);
void pil_q6v5_shutdown(struct pil_desc *pil);
int pil_q6v5_reset(struct pil_desc *pil);
-struct pil_desc *pil_q6v5_init(struct platform_device *pdev);
+struct q6v5_data *pil_q6v5_init(struct platform_device *pdev);
#endif
diff --git a/arch/arm/mach-msm/pil-riva.c b/arch/arm/mach-msm/pil-riva.c
index 3040a31..dbb4408 100644
--- a/arch/arm/mach-msm/pil-riva.c
+++ b/arch/arm/mach-msm/pil-riva.c
@@ -20,11 +20,17 @@
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/wcnss_wlan.h>
#include <mach/msm_iomap.h>
+#include <mach/subsystem_restart.h>
+#include <mach/peripheral-loader.h>
#include "peripheral-loader.h"
#include "scm-pas.h"
+#include "ramdump.h"
+#include "smd_private.h"
#define RIVA_PMU_A2XB_CFG 0xB8
#define RIVA_PMU_A2XB_CFG_EN BIT(0)
@@ -82,6 +88,13 @@
struct clk *xo;
struct regulator *pll_supply;
struct pil_device *pil;
+ int irq;
+ int crash;
+ int rst_in_progress;
+ struct subsys_device *subsys;
+ struct subsys_desc subsys_desc;
+ struct delayed_work cancel_work;
+ struct ramdump_device *ramdump_dev;
};
static bool cxo_is_needed(struct riva_data *drv)
@@ -272,6 +285,160 @@
.proxy_unvote = pil_riva_remove_proxy_vote,
};
+static int enable_riva_ssr;
+
+static int enable_riva_ssr_set(const char *val, struct kernel_param *kp)
+{
+ int ret;
+
+ ret = param_set_int(val, kp);
+ if (ret)
+ return ret;
+
+ if (enable_riva_ssr)
+ pr_info("Subsystem restart activated for riva.\n");
+
+ return 0;
+}
+module_param_call(enable_riva_ssr, enable_riva_ssr_set, param_get_int,
+ &enable_riva_ssr, S_IRUGO | S_IWUSR);
+
+static void smsm_state_cb_hdlr(void *data, uint32_t old_state,
+ uint32_t new_state)
+{
+ struct riva_data *drv = data;
+ char *smem_reset_reason;
+ char buffer[81];
+ unsigned smem_reset_size;
+ unsigned size;
+
+ drv->crash = true;
+ if (!(new_state & SMSM_RESET))
+ return;
+
+ if (drv->rst_in_progress) {
+ pr_err("riva: Ignoring smsm reset req, restart in progress\n");
+ return;
+ }
+
+ pr_err("riva: smsm state changed to smsm reset\n");
+
+ smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_WCNSS0,
+ &smem_reset_size);
+
+ if (!smem_reset_reason || !smem_reset_size) {
+ pr_err("wcnss subsystem failure reason:\n"
+ "(unknown, smem_get_entry failed)");
+ } else if (!smem_reset_reason[0]) {
+ pr_err("wcnss subsystem failure reason:\n"
+ "(unknown, init string found)");
+ } else {
+ size = smem_reset_size < sizeof(buffer) ? smem_reset_size :
+ (sizeof(buffer) - 1);
+ memcpy(buffer, smem_reset_reason, size);
+ buffer[size] = '\0';
+ pr_err("wcnss subsystem failure reason: %s\n", buffer);
+ memset(smem_reset_reason, 0, smem_reset_size);
+ wmb();
+ }
+
+ drv->rst_in_progress = 1;
+ subsystem_restart_dev(drv->subsys);
+}
+
+static irqreturn_t riva_wdog_bite_irq_hdlr(int irq, void *dev_id)
+{
+ struct riva_data *drv = dev_id;
+
+ drv->crash = true;
+ if (drv->rst_in_progress) {
+ pr_err("Ignoring riva bite irq, restart in progress\n");
+ return IRQ_HANDLED;
+ }
+ if (!enable_riva_ssr)
+ panic("Watchdog bite received from Riva");
+
+ drv->rst_in_progress = 1;
+ subsystem_restart_dev(drv->subsys);
+
+ return IRQ_HANDLED;
+}
+
+static void riva_post_bootup(struct work_struct *work)
+{
+ struct platform_device *pdev = wcnss_get_platform_device();
+ struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();
+
+ wcnss_wlan_power(&pdev->dev, pwlanconfig, WCNSS_WLAN_SWITCH_OFF);
+}
+
+static int riva_shutdown(const struct subsys_desc *desc)
+{
+ struct riva_data *drv;
+
+ drv = container_of(desc, struct riva_data, subsys_desc);
+ pil_force_shutdown("wcnss");
+ flush_delayed_work(&drv->cancel_work);
+ wcnss_flush_delayed_boot_votes();
+ disable_irq_nosync(drv->irq);
+
+ return 0;
+}
+
+static int riva_powerup(const struct subsys_desc *desc)
+{
+ struct riva_data *drv;
+ struct platform_device *pdev = wcnss_get_platform_device();
+ struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();
+ int ret = 0;
+
+ drv = container_of(desc, struct riva_data, subsys_desc);
+ if (pdev && pwlanconfig) {
+ ret = wcnss_wlan_power(&pdev->dev, pwlanconfig,
+ WCNSS_WLAN_SWITCH_ON);
+ if (!ret)
+ pil_force_boot("wcnss");
+ }
+ drv->rst_in_progress = 0;
+ enable_irq(drv->irq);
+ schedule_delayed_work(&drv->cancel_work, msecs_to_jiffies(5000));
+
+ return ret;
+}
+
+/*
+ * 7MB RAM segments for Riva SS;
+ * Riva 1.1 0x8f000000 - 0x8f700000
+ * Riva 1.0 0x8f200000 - 0x8f700000
+ */
+static struct ramdump_segment riva_segments[] = {
+ {0x8f000000, 0x8f700000 - 0x8f000000}
+};
+
+static int riva_ramdump(int enable, const struct subsys_desc *desc)
+{
+ struct riva_data *drv;
+
+ drv = container_of(desc, struct riva_data, subsys_desc);
+
+ if (enable)
+ return do_ramdump(drv->ramdump_dev, riva_segments,
+ ARRAY_SIZE(riva_segments));
+ else
+ return 0;
+}
+
+/* Riva crash handler */
+static void riva_crash_shutdown(const struct subsys_desc *desc)
+{
+ struct riva_data *drv;
+
+ drv = container_of(desc, struct riva_data, subsys_desc);
+ pr_err("riva crash shutdown %d\n", drv->crash);
+ if (drv->crash != true)
+ smsm_change_state(SMSM_APPS_STATE, SMSM_RESET, SMSM_RESET);
+}
+
static int __devinit pil_riva_probe(struct platform_device *pdev)
{
struct riva_data *drv;
@@ -317,6 +484,10 @@
}
}
+ drv->irq = platform_get_irq(pdev, 0);
+ if (drv->irq < 0)
+ return drv->irq;
+
desc->name = "wcnss";
desc->dev = &pdev->dev;
desc->owner = THIS_MODULE;
@@ -337,13 +508,60 @@
drv->pil = msm_pil_register(desc);
if (IS_ERR(drv->pil))
return PTR_ERR(drv->pil);
+
+ ret = smsm_state_cb_register(SMSM_WCNSS_STATE, SMSM_RESET,
+ smsm_state_cb_hdlr, drv);
+ if (ret < 0)
+ goto err_smsm;
+
+ drv->subsys_desc.name = "wcnss";
+ drv->subsys_desc.shutdown = riva_shutdown;
+ drv->subsys_desc.powerup = riva_powerup;
+ drv->subsys_desc.ramdump = riva_ramdump;
+ drv->subsys_desc.crash_shutdown = riva_crash_shutdown;
+
+ INIT_DELAYED_WORK(&drv->cancel_work, riva_post_bootup);
+
+ drv->ramdump_dev = create_ramdump_device("riva");
+ if (!drv->ramdump_dev) {
+ ret = -ENOMEM;
+ goto err_ramdump;
+ }
+
+ drv->subsys = subsys_register(&drv->subsys_desc);
+ if (IS_ERR(drv->subsys)) {
+ ret = PTR_ERR(drv->subsys);
+ goto err_subsys;
+ }
+
+ ret = devm_request_irq(&pdev->dev, drv->irq, riva_wdog_bite_irq_hdlr,
+ IRQF_TRIGGER_HIGH, "riva_wdog", drv);
+ if (ret < 0)
+ goto err;
+
return 0;
+err:
+ subsys_unregister(drv->subsys);
+err_subsys:
+ destroy_ramdump_device(drv->ramdump_dev);
+err_ramdump:
+ smsm_state_cb_deregister(SMSM_WCNSS_STATE, SMSM_RESET,
+ smsm_state_cb_hdlr, drv);
+err_smsm:
+ msm_pil_unregister(drv->pil);
+ return ret;
}
static int __devexit pil_riva_remove(struct platform_device *pdev)
{
struct riva_data *drv = platform_get_drvdata(pdev);
+
+ subsys_unregister(drv->subsys);
+ destroy_ramdump_device(drv->ramdump_dev);
+ smsm_state_cb_deregister(SMSM_WCNSS_STATE, SMSM_RESET,
+ smsm_state_cb_hdlr, drv);
msm_pil_unregister(drv->pil);
+
return 0;
}
diff --git a/arch/arm/mach-msm/platsmp-8625.c b/arch/arm/mach-msm/platsmp-8625.c
index 700f966..3b31b9f 100644
--- a/arch/arm/mach-msm/platsmp-8625.c
+++ b/arch/arm/mach-msm/platsmp-8625.c
@@ -28,19 +28,28 @@
#include <mach/msm_iomap.h>
#include "pm.h"
-#define MSM_CORE1_RESET 0xA8600590
-#define MSM_CORE1_STATUS_MSK 0x02800000
+#define CORE_RESET_BASE 0xA8600590
+#define MSM_CORE_STATUS_MSK 0x02800000
/*
* control for which core is the next to come out of the secondary
* boot "holding pen"
*/
-int pen_release = -1;
+volatile int pen_release = -1;
-static bool cold_boot_done;
+static DEFINE_PER_CPU(bool, cold_boot_done);
+
+struct per_cpu_data {
+ unsigned int reset_off;
+ unsigned int offset;
+ unsigned int ipc_irq;
+ void __iomem *reset_core_base;
+};
static uint32_t *msm8625_boot_vector;
-static void __iomem *reset_core1_base;
+
+
+static struct per_cpu_data cpu_data[CONFIG_NR_CPUS];
/*
* Write pen_release in a way that is guaranteed to be visible to all
@@ -65,7 +74,8 @@
/*
* MP_CORE_IPC will be used to generate interrupt and can be used by either
* of core.
- * To bring core1 out of GDFS we need to raise the SPI using the MP_CORE_IPC.
+ * To bring secondary cores out of GDFS we need to raise the SPI using the
+ * MP_CORE_IPC.
*/
static void raise_clear_spi(unsigned int cpu, bool set)
{
@@ -110,10 +120,10 @@
*/
write_pen_release(-1);
- /* clear the IPC1(SPI-8) pending SPI */
+ /* clear the IPC pending SPI */
if (power_collapsed) {
- raise_clear_spi(1, false);
- clear_pending_spi(MSM8625_INT_ACSR_MP_CORE_IPC1);
+ raise_clear_spi(cpu, false);
+ clear_pending_spi(cpu_data[cpu].ipc_irq);
power_collapsed = 0;
}
@@ -124,7 +134,7 @@
spin_unlock(&boot_lock);
}
-static int __cpuinit msm8625_release_secondary(void)
+static int __cpuinit msm8625_release_secondary(unsigned int cpu)
{
void __iomem *base_ptr;
int value = 0;
@@ -137,33 +147,35 @@
*/
timeout = jiffies + usecs_to_jiffies(20);
while (time_before(jiffies, timeout)) {
- value = __raw_readl(MSM_CFG_CTL_BASE + 0x3c);
- if ((value & MSM_CORE1_STATUS_MSK) ==
- MSM_CORE1_STATUS_MSK)
+ value = __raw_readl(MSM_CFG_CTL_BASE + cpu_data[cpu].offset);
+ if ((value & MSM_CORE_STATUS_MSK) ==
+ MSM_CORE_STATUS_MSK)
break;
udelay(1);
}
if (!value) {
- pr_err("Core 1 cannot be brought out of Reset!!!\n");
+ pr_err("Core %u cannot be brought out of Reset!!!\n", cpu);
return -ENODEV;
}
- base_ptr = ioremap_nocache(MSM_CORE1_RESET, SZ_4);
+ base_ptr = ioremap_nocache(CORE_RESET_BASE +
+ cpu_data[cpu].reset_off, SZ_4);
if (!base_ptr)
return -ENODEV;
- /* Reset core 1 out of reset */
+
+ /* Reset core out of reset */
__raw_writel(0x0, base_ptr);
mb();
- reset_core1_base = base_ptr;
+ cpu_data[cpu].reset_core_base = base_ptr;
return 0;
}
-void __iomem *core1_reset_base(void)
+void __iomem *core_reset_base(unsigned int cpu)
{
- return reset_core1_base;
+ return cpu_data[cpu].reset_core_base;
}
int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
@@ -172,12 +184,12 @@
preset_lpj = loops_per_jiffy;
- if (cold_boot_done == false) {
- if (msm8625_release_secondary()) {
- pr_err("Failed to release secondary core\n");
+ if (per_cpu(cold_boot_done, cpu) == false) {
+ if (msm8625_release_secondary(cpu)) {
+ pr_err("Failed to release core %u\n", cpu);
return -ENODEV;
}
- cold_boot_done = true;
+ per_cpu(cold_boot_done, cpu) = true;
}
/*
@@ -200,13 +212,13 @@
* and branch to the address found there.
*
* power_collapsed is the flag which will be updated for Powercollapse.
- * Once we are out of PC, as Core1 will be in the state of GDFS which
- * needs to be brought out by raising an SPI.
+ * Once we are out of PC, as secondary cores will be in the state of
+ * GDFS which needs to be brought out by raising an SPI.
*/
if (power_collapsed) {
- core1_gic_configure_and_raise();
- raise_clear_spi(1, true);
+ gic_configure_and_raise(cpu_data[cpu].ipc_irq, cpu);
+ raise_clear_spi(cpu, true);
} else {
gic_raise_softirq(cpumask_of(cpu), 1);
}
@@ -247,6 +259,34 @@
set_smp_cross_call(gic_raise_softirq);
}
+static void per_cpu_data(unsigned int cpu, unsigned int off,
+ unsigned int off1, unsigned int irq)
+{
+ cpu_data[cpu].reset_off = off;
+ cpu_data[cpu].offset = off1;
+ cpu_data[cpu].ipc_irq = irq;
+}
+
+static void enable_boot_remapper(unsigned long bit, unsigned int off)
+{
+ int value;
+
+ /* Enable boot remapper address */
+ value = __raw_readl(MSM_CFG_CTL_BASE + off);
+ __raw_writel(value | bit, MSM_CFG_CTL_BASE + off) ;
+ mb();
+}
+
+static void remapper_address(unsigned long phys, unsigned int off)
+{
+ /*
+ * Write the address of secondary startup into the
+ * boot remapper register. The secondary CPU branches to this address.
+ */
+ __raw_writel(phys, (MSM_CFG_CTL_BASE + off));
+ mb();
+}
+
static void __init msm8625_boot_vector_init(uint32_t *boot_vector,
unsigned long entry)
{
@@ -260,8 +300,8 @@
void __init platform_smp_prepare_cpus(unsigned int max_cpus)
{
- int i, value;
- void __iomem *second_ptr;
+ int i, cpu, value;
+ void __iomem *cpu_ptr;
/*
* Initialise the present map, which describes the set of CPUs
@@ -272,25 +312,41 @@
scu_enable(scu_base_addr());
- /*
- * Write the address of secondary startup into the
- * boot remapper register. The secondary CPU branches to this address.
- */
- __raw_writel(MSM8625_SECONDARY_PHYS, (MSM_CFG_CTL_BASE + 0x34));
- mb();
-
- second_ptr = ioremap_nocache(MSM8625_SECONDARY_PHYS, SZ_8);
- if (!second_ptr) {
- pr_err("failed to ioremap for secondary core\n");
+ cpu_ptr = ioremap_nocache(MSM8625_CPU_PHYS, SZ_8);
+ if (!cpu_ptr) {
+ pr_err("failed to ioremap for secondary cores\n");
return;
}
- msm8625_boot_vector_init(second_ptr,
+ msm8625_boot_vector_init(cpu_ptr,
virt_to_phys(msm_secondary_startup));
- iounmap(second_ptr);
- /* Enable boot remapper address: bit 26 for core1 */
- value = __raw_readl(MSM_CFG_CTL_BASE + 0x30);
- __raw_writel(value | (0x4 << 24), MSM_CFG_CTL_BASE + 0x30) ;
- mb();
+ iounmap(cpu_ptr);
+
+ for_each_possible_cpu(cpu) {
+ switch (cpu) {
+ case 0:
+ break;
+ case 1:
+ remapper_address(MSM8625_CPU_PHYS, 0x34);
+ per_cpu_data(cpu, 0x0, 0x3c,
+ MSM8625_INT_ACSR_MP_CORE_IPC1);
+ enable_boot_remapper(BIT(26), 0x30);
+ break;
+ case 2:
+ remapper_address((MSM8625_CPU_PHYS >> 16), 0x4C);
+ per_cpu_data(cpu, 0x8, 0x50,
+ MSM8625_INT_ACSR_MP_CORE_IPC2);
+ enable_boot_remapper(BIT(25), 0x48);
+ break;
+ case 3:
+ value = __raw_readl(MSM_CFG_CTL_BASE + 0x4C);
+ remapper_address(value | MSM8625_CPU_PHYS, 0x4C);
+ per_cpu_data(cpu, 0xC, 0x50,
+ MSM8625_INT_ACSR_MP_CORE_IPC3);
+ enable_boot_remapper(BIT(26), 0x48);
+ break;
+ }
+
+ }
}
diff --git a/arch/arm/mach-msm/platsmp-8910.c b/arch/arm/mach-msm/platsmp-8910.c
new file mode 100644
index 0000000..5d055bd
--- /dev/null
+++ b/arch/arm/mach-msm/platsmp-8910.c
@@ -0,0 +1,190 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/jiffies.h>
+#include <linux/smp.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include <asm/cacheflush.h>
+#include <asm/smp_plat.h>
+#include <asm/hardware/gic.h>
+#include <mach/msm_iomap.h>
+#include "pm.h"
+
+#define BOOT_REMAP_ENABLE 0x01
+
+/*
+ * control for which core is the next to come out of the secondary
+ * boot "holding pen"
+ */
+volatile int __cpuinitdata pen_release = -1;
+
+/*
+ * Write pen_release in a way that is guaranteed to be visible to all
+ * observers, irrespective of whether they're taking part in coherency
+ * or not. This is necessary for the hotplug code to work reliably.
+ */
+static void __cpuinit write_pen_release(int val)
+{
+ pen_release = val;
+ smp_wmb();
+ __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
+ outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
+}
+
+static DEFINE_SPINLOCK(boot_lock);
+
+void __cpuinit platform_secondary_init(unsigned int cpu)
+{
+ WARN_ON(msm_platform_secondary_init(cpu));
+
+ /*
+ * if any interrupts are already enabled for the primary
+ * core (e.g. timer irq), then they will not have been enabled
+ * for us: do so
+ */
+ gic_secondary_init(0);
+
+ /*
+ * let the primary processor know we're out of the
+ * pen, then head off into the C entry point
+ */
+ write_pen_release(-1);
+
+ /*
+ * Synchronise with the boot thread.
+ */
+ spin_lock(&boot_lock);
+ spin_unlock(&boot_lock);
+}
+
+static int __cpuinit release_secondary_sim(unsigned long base, int cpu)
+{
+ void *base_ptr;
+
+ base_ptr = ioremap_nocache(base + (cpu * 0x10000), SZ_4K);
+ if (!base_ptr) {
+ pr_err("Failed to release core %u\n", cpu);
+ return -ENODEV;
+ }
+
+ writel_relaxed(0x800, base_ptr+0x04);
+ writel_relaxed(0x3FFF, base_ptr+0x14);
+ mb();
+
+ return 0;
+}
+
+DEFINE_PER_CPU(int, cold_boot_done);
+
+int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+ unsigned long timeout;
+
+ preset_lpj = loops_per_jiffy;
+
+ if (per_cpu(cold_boot_done, cpu) == false) {
+ release_secondary_sim(0xF9088000, cpu);
+ per_cpu(cold_boot_done, cpu) = true;
+ }
+
+ /*
+ * Set synchronisation state between this boot processor
+ * and the secondary one
+ */
+ spin_lock(&boot_lock);
+
+ /*
+ * The secondary processor is waiting to be released from
+ * the holding pen - release it, then wait for it to flag
+ * that it has been released by resetting pen_release.
+ *
+ * Note that "pen_release" is the hardware CPU ID, whereas
+ * "cpu" is Linux's internal ID.
+ */
+ write_pen_release(cpu_logical_map(cpu));
+
+ /*
+ * Send the secondary CPU a soft interrupt, thereby causing
+ * the boot monitor to read the system wide flags register,
+ * and branch to the address found there.
+ */
+
+ gic_raise_softirq(cpumask_of(cpu), 1);
+
+ timeout = jiffies + (1 * HZ);
+ while (time_before(jiffies, timeout)) {
+ smp_rmb();
+ if (pen_release == -1)
+ break;
+
+ udelay(10);
+ }
+
+ /*
+ * now the secondary core is starting up let it run its
+ * calibrations, then wait for it to finish
+ */
+ spin_unlock(&boot_lock);
+
+ return 0;
+}
+
+/*
+ * Initialise the CPU possible map early - this describes the CPUs
+ * which may be present or become present in the system
+ */
+void __init smp_init_cpus(void)
+{
+ unsigned int i, ncores;
+
+ ncores = (__raw_readl(MSM_APCS_GCC_BASE + 0x30)) & 0xF;
+
+ for (i = 0; i < ncores; i++)
+ set_cpu_possible(i, true);
+
+ set_smp_cross_call(gic_raise_softirq);
+}
+
+void __init platform_smp_prepare_cpus(unsigned int max_cpus)
+{
+ int i;
+ void __iomem *remap_ptr;
+
+ /*
+ * Initialise the present map, which describes the set of CPUs
+ * actually populated at the present time
+ */
+ for (i = 0; i < max_cpus; i++)
+ set_cpu_present(i, true);
+
+ /*
+ * Enable boot remapping and write the address of secondary
+ * startup into boot remapper register
+ */
+ remap_ptr = ioremap_nocache(0xF9010000, SZ_4K); /* APCS_CFG_SECURE */
+ if (!remap_ptr) {
+ pr_err("Failed to ioremap for secondary cores\n");
+ return;
+ }
+
+ __raw_writel((virt_to_phys(msm_secondary_startup)|BOOT_REMAP_ENABLE),
+ (remap_ptr + 0x4));
+ mb();
+ iounmap(remap_ptr);
+}
diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c
index c9517b6..5f05f98 100644
--- a/arch/arm/mach-msm/platsmp.c
+++ b/arch/arm/mach-msm/platsmp.c
@@ -42,6 +42,19 @@
*/
volatile int pen_release = -1;
+/*
+ * Write pen_release in a way that is guaranteed to be visible to all
+ * observers, irrespective of whether they're taking part in coherency
+ * or not. This is necessary for the hotplug code to work reliably.
+ */
+static void __cpuinit write_pen_release(int val)
+{
+ pen_release = val;
+ smp_wmb();
+ __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
+ outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
+}
+
static DEFINE_SPINLOCK(boot_lock);
void __cpuinit platform_secondary_init(unsigned int cpu)
@@ -56,6 +69,12 @@
gic_secondary_init(0);
/*
+ * let the primary processor know we're out of the
+ * pen, then head off into the C entry point
+ */
+ write_pen_release(-1);
+
+ /*
* Synchronise with the boot thread.
*/
spin_lock(&boot_lock);
@@ -164,9 +183,8 @@
machine_is_msm8226_sim())
return krait_release_secondary_sim(0xf9088000, cpu);
- if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm8930aa() ||
- cpu_is_apq8064() || cpu_is_msm8627() || cpu_is_msm8960ab() ||
- cpu_is_apq8064ab())
+ if (soc_class_is_msm8960() || soc_class_is_msm8930() ||
+ soc_class_is_apq8064())
return krait_release_secondary(0x02088000, cpu);
if (cpu_is_msm8974())
@@ -177,17 +195,9 @@
}
DEFINE_PER_CPU(int, cold_boot_done);
-static int cold_boot_flags[] = {
- 0,
- SCM_FLAG_COLDBOOT_CPU1,
- SCM_FLAG_COLDBOOT_CPU2,
- SCM_FLAG_COLDBOOT_CPU3,
-};
int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
{
- int ret;
- unsigned int flag = 0;
unsigned long timeout;
pr_debug("Starting secondary CPU %d\n", cpu);
@@ -195,19 +205,8 @@
/* Set preset_lpj to avoid subsequent lpj recalculations */
preset_lpj = loops_per_jiffy;
- if (cpu > 0 && cpu < ARRAY_SIZE(cold_boot_flags))
- flag = cold_boot_flags[cpu];
- else
- __WARN();
-
if (per_cpu(cold_boot_done, cpu) == false) {
- ret = scm_set_boot_addr(virt_to_phys(msm_secondary_startup),
- flag);
- if (ret == 0)
- release_secondary(cpu);
- else
- printk(KERN_DEBUG "Failed to set secondary core boot "
- "address\n");
+ release_secondary(cpu);
per_cpu(cold_boot_done, cpu) = true;
}
@@ -225,9 +224,7 @@
* Note that "pen_release" is the hardware CPU ID, whereas
* "cpu" is Linux's internal ID.
*/
- pen_release = cpu_logical_map(cpu);
- __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
- outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
+ write_pen_release(cpu_logical_map(cpu));
/*
* Send the secondary CPU a soft interrupt, thereby causing
@@ -242,8 +239,6 @@
if (pen_release == -1)
break;
- dmac_inv_range((char *)&pen_release,
- (char *)&pen_release + sizeof(pen_release));
udelay(10);
}
@@ -275,6 +270,28 @@
set_smp_cross_call(gic_raise_softirq);
}
+static int cold_boot_flags[] __initdata = {
+ 0,
+ SCM_FLAG_COLDBOOT_CPU1,
+ SCM_FLAG_COLDBOOT_CPU2,
+ SCM_FLAG_COLDBOOT_CPU3,
+};
+
void __init platform_smp_prepare_cpus(unsigned int max_cpus)
{
+ int cpu, map;
+ unsigned int flags = 0;
+
+ for_each_present_cpu(cpu) {
+ map = cpu_logical_map(cpu);
+ if (map > ARRAY_SIZE(cold_boot_flags)) {
+ set_cpu_present(cpu, false);
+ __WARN();
+ continue;
+ }
+ flags |= cold_boot_flags[map];
+ }
+
+ if (scm_set_boot_addr(virt_to_phys(msm_secondary_startup), flags))
+ pr_warn("Failed to set CPU boot address\n");
}
diff --git a/arch/arm/mach-msm/pm-8x60.c b/arch/arm/mach-msm/pm-8x60.c
index 60ee8f0..5c40750 100644
--- a/arch/arm/mach-msm/pm-8x60.c
+++ b/arch/arm/mach-msm/pm-8x60.c
@@ -30,6 +30,7 @@
#include <mach/system.h>
#include <mach/scm.h>
#include <mach/socinfo.h>
+#include <mach/msm-krait-l2-accessors.h>
#include <asm/cacheflush.h>
#include <asm/hardware/gic.h>
#include <asm/pgtable.h>
@@ -413,10 +414,63 @@
}
EXPORT_SYMBOL(msm_pm_set_max_sleep_time);
+struct reg_data {
+ uint32_t reg;
+ uint32_t val;
+};
-/******************************************************************************
- *
- *****************************************************************************/
+static struct reg_data reg_saved_state[] = {
+ { .reg = 0x4501, },
+ { .reg = 0x5501, },
+ { .reg = 0x6501, },
+ { .reg = 0x7501, },
+ { .reg = 0x0500, },
+};
+
+static unsigned int active_vdd;
+static bool msm_pm_save_cp15;
+static const unsigned int pc_vdd = 0x98;
+
+static void msm_pm_save_cpu_reg(void)
+{
+ int i;
+
+ /* Only on core0 */
+ if (smp_processor_id())
+ return;
+
+ /**
+ * On some targets, L2 PC will turn off may reset the core
+ * configuration for the mux and the default may not make the core
+ * happy when it resumes.
+ * Save the active vdd, and set the core vdd to QSB max vdd, so that
+ * when the core resumes, it is capable of supporting the current QSB
+ * rate. Then restore the active vdd before switching the acpuclk rate.
+ */
+ if (msm_pm_get_l2_flush_flag() == 1) {
+ active_vdd = msm_spm_get_vdd(0);
+ for (i = 0; i < ARRAY_SIZE(reg_saved_state); i++)
+ reg_saved_state[i].val =
+ get_l2_indirect_reg(reg_saved_state[i].reg);
+ msm_spm_set_vdd(0, pc_vdd);
+ }
+}
+
+static void msm_pm_restore_cpu_reg(void)
+{
+ int i;
+
+ /* Only on core0 */
+ if (smp_processor_id())
+ return;
+
+ if (msm_pm_get_l2_flush_flag() == 1) {
+ for (i = 0; i < ARRAY_SIZE(reg_saved_state); i++)
+ set_l2_indirect_reg(reg_saved_state[i].reg,
+ reg_saved_state[i].val);
+ msm_spm_set_vdd(0, active_vdd);
+ }
+}
static void *msm_pm_idle_rs_limits;
static bool msm_pm_use_qtimer;
@@ -558,8 +612,14 @@
pr_info("CPU%u: %s: change clock rate (old rate = %lu)\n",
cpu, __func__, saved_acpuclk_rate);
+ if (msm_pm_save_cp15)
+ msm_pm_save_cpu_reg();
+
collapsed = msm_pm_spm_power_collapse(cpu, from_idle, true);
+ if (msm_pm_save_cp15)
+ msm_pm_restore_cpu_reg();
+
if (cpu_online(cpu)) {
if (MSM_PM_DEBUG_CLOCK & msm_pm_debug_mask)
pr_info("CPU%u: %s: restore clock rate to %lu\n",
@@ -593,9 +653,12 @@
return collapsed;
}
-static void msm_pm_qtimer_available(void)
+static void msm_pm_target_init(void)
{
- if (machine_is_msm8974())
+ if (cpu_is_apq8064())
+ msm_pm_save_cp15 = true;
+
+ if (cpu_is_msm8974())
msm_pm_use_qtimer = true;
}
@@ -718,11 +781,17 @@
switch (mode) {
case MSM_PM_SLEEP_MODE_POWER_COLLAPSE:
+ if (num_online_cpus() > 1) {
+ allow = false;
+ break;
+ }
+ /* fall through */
case MSM_PM_SLEEP_MODE_RETENTION:
if (!allow)
break;
- if (num_online_cpus() > 1) {
+ if (msm_pm_retention_tz_call &&
+ num_online_cpus() > 1) {
allow = false;
break;
}
@@ -1087,7 +1156,7 @@
msm_pm_add_stats(enable_stats, ARRAY_SIZE(enable_stats));
suspend_set_ops(&msm_pm_ops);
- msm_pm_qtimer_available();
+ msm_pm_target_init();
hrtimer_init(&pm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
msm_cpuidle_init();
platform_driver_register(&msm_pc_counter_driver);
diff --git a/arch/arm/mach-msm/pm-boot.c b/arch/arm/mach-msm/pm-boot.c
index ed15a0c..7bc4fe0 100644
--- a/arch/arm/mach-msm/pm-boot.c
+++ b/arch/arm/mach-msm/pm-boot.c
@@ -211,7 +211,10 @@
char *key = NULL;
uint32_t val = 0;
int ret = 0;
- int flag = 0;
+ uint32_t vaddr_val;
+
+ pdata.p_addr = 0;
+ vaddr_val = 0;
key = "qcom,mode";
ret = of_property_read_u32(pdev->dev.of_node, key, &val);
@@ -223,24 +226,43 @@
key = "qcom,phy-addr";
ret = of_property_read_u32(pdev->dev.of_node, key, &val);
- if (ret && pdata.mode == MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS)
- goto fail;
- if (!ret) {
+ if (!ret)
pdata.p_addr = val;
- flag++;
- }
+
key = "qcom,virt-addr";
- ret = of_property_read_u32(pdev->dev.of_node, key, &val);
- if (ret && pdata.mode == MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT)
- goto fail;
- if (!ret) {
- pdata.v_addr = (void *)val;
- flag++;
- }
+ ret = of_property_read_u32(pdev->dev.of_node, key, &vaddr_val);
- if (pdata.mode == MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR && (flag != 2)) {
- key = "addresses for boot remap";
+ switch (pdata.mode) {
+ case MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS:
+ if (!pdata.p_addr) {
+ key = "qcom,phy-addr";
+ goto fail;
+ }
+ break;
+ case MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT:
+ if (!vaddr_val)
+ goto fail;
+
+ pdata.v_addr = (void *)vaddr_val;
+ break;
+ case MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR:
+ if (!vaddr_val)
+ goto fail;
+
+ pdata.v_addr = ioremap_nocache(vaddr_val, SZ_8);
+
+ pdata.p_addr = allocate_contiguous_ebi_nomap(SZ_8, SZ_64K);
+ if (!pdata.p_addr) {
+ key = "qcom,phy-addr";
+ goto fail;
+ }
+ break;
+ case MSM_PM_BOOT_CONFIG_TZ:
+ break;
+ default:
+ pr_err("%s: Unsupported boot mode %d",
+ __func__, pdata.mode);
goto fail;
}
diff --git a/arch/arm/mach-msm/pm2.c b/arch/arm/mach-msm/pm2.c
index 10c5445..427e39f 100644
--- a/arch/arm/mach-msm/pm2.c
+++ b/arch/arm/mach-msm/pm2.c
@@ -487,7 +487,7 @@
void __iomem *base_ptr;
unsigned int value = 0;
- base_ptr = core1_reset_base();
+ base_ptr = core_reset_base(1);
if (!base_ptr)
return;
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_a2dp_in.c b/arch/arm/mach-msm/qdsp5v2/audio_a2dp_in.c
index 733b7a1..e396186 100644
--- a/arch/arm/mach-msm/qdsp5v2/audio_a2dp_in.c
+++ b/arch/arm/mach-msm/qdsp5v2/audio_a2dp_in.c
@@ -5,6 +5,7 @@
*
* Copyright (C) 2008 HTC Corporation
* Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2012 The Linux Foundation. All rights reserved.
*
* All source code in this file is licensed under the following license except
* where indicated.
@@ -34,7 +35,7 @@
#include <linux/dma-mapping.h>
#include <linux/msm_audio.h>
#include <linux/msm_audio_sbc.h>
-#include <linux/android_pmem.h>
+#include <linux/msm_ion.h>
#include <linux/memory_alloc.h>
#include <mach/iommu.h>
@@ -115,6 +116,8 @@
int stopped; /* set when stopped, cleared on flush */
int abort; /* set when error, like sample rate mismatch */
char *build_id;
+ struct ion_client *client;
+ struct ion_handle *output_buff_handle;
};
static struct audio_a2dp_in the_audio_a2dp_in;
@@ -848,10 +851,11 @@
audio->audrec = NULL;
audio->opened = 0;
if (audio->data) {
- iounmap(audio->msm_map);
- free_contiguous_memory_by_paddr(audio->phys);
+ ion_unmap_kernel(audio->client, audio->output_buff_handle);
+ ion_free(audio->client, audio->output_buff_handle);
audio->data = NULL;
}
+ ion_client_destroy(audio->client);
mutex_unlock(&audio->lock);
return 0;
}
@@ -861,6 +865,11 @@
struct audio_a2dp_in *audio = &the_audio_a2dp_in;
int rc;
int encid;
+ int len = 0;
+ unsigned long ionflag = 0;
+ ion_phys_addr_t addr = 0;
+ struct ion_handle *handle = NULL;
+ struct ion_client *client = NULL;
mutex_lock(&audio->lock);
if (audio->opened) {
@@ -868,22 +877,56 @@
goto done;
}
- audio->phys = allocate_contiguous_ebi_nomap(DMASZ, SZ_4K);
- if (audio->phys) {
- audio->msm_map = ioremap(audio->phys, DMASZ);
- if (IS_ERR(audio->msm_map)) {
- MM_ERR("could not map the phys address to kernel"
- "space\n");
+ client = msm_ion_client_create(UINT_MAX, "Audio_a2dp_in_client");
+ if (IS_ERR_OR_NULL(client)) {
+ MM_ERR("Unable to create ION client\n");
rc = -ENOMEM;
- free_contiguous_memory_by_paddr(audio->phys);
- goto done;
- }
- audio->data = (u8 *)audio->msm_map;
- } else {
- MM_ERR("could not allocate DMA buffers\n");
- rc = -ENOMEM;
- goto done;
+ goto client_create_error;
}
+ audio->client = client;
+
+ MM_DBG("allocating mem sz = %d\n", DMASZ);
+ handle = ion_alloc(client, DMASZ, SZ_4K,
+ ION_HEAP(ION_AUDIO_HEAP_ID), 0);
+ if (IS_ERR_OR_NULL(handle)) {
+ MM_ERR("Unable to create allocate O/P buffers\n");
+ rc = -ENOMEM;
+ goto output_buff_alloc_error;
+ }
+
+ audio->output_buff_handle = handle;
+
+ rc = ion_phys(client , handle, &addr, &len);
+ if (rc) {
+ MM_ERR("O/P buffers:Invalid phy: %x sz: %x\n",
+ (unsigned int) addr, (unsigned int) len);
+ rc = -ENOMEM;
+ goto output_buff_get_phys_error;
+ } else {
+ MM_INFO("O/P buffers:valid phy: %x sz: %x\n",
+ (unsigned int) addr, (unsigned int) len);
+ }
+ audio->phys = (int32_t)addr;
+
+ rc = ion_handle_get_flags(client, handle, &ionflag);
+ if (rc) {
+ MM_ERR("could not get flags for the handle\n");
+ rc = -ENOMEM;
+ goto output_buff_get_flags_error;
+ }
+
+ audio->msm_map = ion_map_kernel(client, handle);
+ if (IS_ERR(audio->data)) {
+ MM_ERR("could not map read buffers,freeing instance 0x%08x\n",
+ (int)audio);
+ rc = -ENOMEM;
+ goto output_buff_map_error;
+ }
+ MM_DBG("read buf: phy addr 0x%08x kernel addr 0x%08x\n",
+ audio->phys, (int)audio->data);
+
+ audio->data = (char *)audio->msm_map;
+
MM_DBG("Memory addr = 0x%8x phy addr = 0x%8x\n",\
(int) audio->data, (int) audio->phys);
@@ -953,6 +996,13 @@
mutex_unlock(&audio->lock);
return rc;
evt_error:
+output_buff_map_error:
+output_buff_get_phys_error:
+output_buff_get_flags_error:
+ ion_free(client, audio->output_buff_handle);
+output_buff_alloc_error:
+ ion_client_destroy(client);
+client_create_error:
msm_adsp_put(audio->audrec);
audpreproc_aenc_free(audio->enc_id);
mutex_unlock(&audio->lock);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_lpa.c b/arch/arm/mach-msm/qdsp5v2/audio_lpa.c
index 60f43b9..7ec0617 100644
--- a/arch/arm/mach-msm/qdsp5v2/audio_lpa.c
+++ b/arch/arm/mach-msm/qdsp5v2/audio_lpa.c
@@ -2,7 +2,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2012, 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
@@ -27,7 +27,7 @@
#include <linux/delay.h>
#include <linux/earlysuspend.h>
#include <linux/list.h>
-#include <linux/android_pmem.h>
+#include <linux/msm_ion.h>
#include <asm/atomic.h>
#include <asm/ioctls.h>
#include <mach/msm_adsp.h>
@@ -136,9 +136,9 @@
union msm_audio_event_payload payload;
};
-struct audlpa_pmem_region {
+struct audlpa_ion_region {
struct list_head list;
- struct file *file;
+ struct ion_handle *handle;
int fd;
void *vaddr;
unsigned long paddr;
@@ -170,7 +170,7 @@
static void audio_dsp_event(void *private, unsigned id, uint16_t *msg);
static void audlpa_post_event(struct audio *audio, int type,
union msm_audio_event_payload payload);
-static unsigned long audlpa_pmem_fixup(struct audio *audio, void *addr,
+static unsigned long audlpa_ion_fixup(struct audio *audio, void *addr,
unsigned long len, int ref_up);
static void audlpa_async_send_data(struct audio *audio, unsigned needed,
uint32_t *payload);
@@ -778,7 +778,7 @@
if (drv_evt->event_type == AUDIO_EVENT_WRITE_DONE ||
drv_evt->event_type == AUDIO_EVENT_READ_DONE) {
mutex_lock(&audio->lock);
- audlpa_pmem_fixup(audio, drv_evt->payload.aio_buf.buf_addr,
+ audlpa_ion_fixup(audio, drv_evt->payload.aio_buf.buf_addr,
drv_evt->payload.aio_buf.buf_len, 0);
mutex_unlock(&audio->lock);
}
@@ -788,94 +788,118 @@
return rc;
}
-static int audlpa_pmem_check(struct audio *audio,
+static int audlpa_ion_check(struct audio *audio,
void *vaddr, unsigned long len)
{
- struct audlpa_pmem_region *region_elt;
- struct audlpa_pmem_region t = { .vaddr = vaddr, .len = len };
+ struct audlpa_ion_region *region_elt;
+ struct audlpa_ion_region t = {.vaddr = vaddr, .len = len };
- list_for_each_entry(region_elt, &audio->pmem_region_queue, list) {
+ list_for_each_entry(region_elt, &audio->ion_region_queue, list) {
if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) ||
OVERLAPS(region_elt, &t)) {
- MM_ERR("region (vaddr %p len %ld)"
+ MM_ERR("[%p]:region (vaddr %p len %ld)"
" clashes with registered region"
" (vaddr %p paddr %p len %ld)\n",
- vaddr, len,
+ audio, vaddr, len,
region_elt->vaddr,
- (void *)region_elt->paddr,
- region_elt->len);
+ (void *)region_elt->paddr, region_elt->len);
return -EINVAL;
}
}
return 0;
}
-
-static int audlpa_pmem_add(struct audio *audio,
- struct msm_audio_pmem_info *info)
+static int audlpa_ion_add(struct audio *audio,
+ struct msm_audio_ion_info *info)
{
- unsigned long paddr, kvaddr, len;
- struct file *file;
- struct audlpa_pmem_region *region;
+ ion_phys_addr_t paddr;
+ size_t len;
+ unsigned long kvaddr;
+ struct audlpa_ion_region *region;
int rc = -EINVAL;
+ struct ion_handle *handle;
+ unsigned long ionflag;
- MM_DBG("\n"); /* Macro prints the file name and function */
+ MM_ERR("\n"); /* Macro prints the file name and function */
region = kmalloc(sizeof(*region), GFP_KERNEL);
if (!region) {
rc = -ENOMEM;
goto end;
}
-
- if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) {
- kfree(region);
- goto end;
+ handle = ion_import_dma_buf(audio->client, info->fd);
+ if (IS_ERR_OR_NULL(handle)) {
+ pr_err("%s: could not get handle of the given fd\n", __func__);
+ goto import_error;
}
-
- rc = audlpa_pmem_check(audio, info->vaddr, len);
+ rc = ion_handle_get_flags(audio->client, handle, &ionflag);
+ if (rc) {
+ pr_err("%s: could not get flags for the handle\n", __func__);
+ goto flag_error;
+ }
+ kvaddr = (unsigned long)ion_map_kernel(audio->client, handle);
+ if (IS_ERR_OR_NULL((void *)kvaddr)) {
+ pr_err("%s: could not get virtual address\n", __func__);
+ goto map_error;
+ }
+ rc = ion_phys(audio->client, handle, &paddr, &len);
+ if (rc) {
+ pr_err("%s: could not get physical address\n", __func__);
+ goto ion_error;
+ }
+ rc = audlpa_ion_check(audio, info->vaddr, len);
if (rc < 0) {
- put_pmem_file(file);
- kfree(region);
- goto end;
+ MM_ERR("audpcm_ion_check failed\n");
+ goto ion_error;
}
-
+ region->handle = handle;
region->vaddr = info->vaddr;
region->fd = info->fd;
region->paddr = paddr;
region->kvaddr = kvaddr;
region->len = len;
- region->file = file;
region->ref_cnt = 0;
- MM_DBG("add region paddr %lx vaddr %p, len %lu\n", region->paddr,
- region->vaddr, region->len);
- list_add_tail(®ion->list, &audio->pmem_region_queue);
+ MM_DBG("[%p]:add region paddr %lx vaddr %p, len %lu kvaddr %lx\n",
+ audio, region->paddr, region->vaddr,
+ region->len, region->kvaddr);
+ list_add_tail(®ion->list, &audio->ion_region_queue);
+
+ return rc;
+
+ion_error:
+ ion_unmap_kernel(audio->client, handle);
+map_error:
+flag_error:
+ ion_free(audio->client, handle);
+import_error:
+ kfree(region);
end:
return rc;
}
-static int audlpa_pmem_remove(struct audio *audio,
- struct msm_audio_pmem_info *info)
+static int audlpa_ion_remove(struct audio *audio,
+ struct msm_audio_ion_info *info)
{
- struct audlpa_pmem_region *region;
+ struct audlpa_ion_region *region;
struct list_head *ptr, *next;
int rc = -EINVAL;
- MM_DBG("info fd %d vaddr %p\n", info->fd, info->vaddr);
+ list_for_each_safe(ptr, next, &audio->ion_region_queue) {
+ region = list_entry(ptr, struct audlpa_ion_region, list);
- list_for_each_safe(ptr, next, &audio->pmem_region_queue) {
- region = list_entry(ptr, struct audlpa_pmem_region, list);
-
- if ((region->fd == info->fd) &&
+ if (region != NULL && (region->fd == info->fd) &&
(region->vaddr == info->vaddr)) {
if (region->ref_cnt) {
- MM_DBG("region %p in use ref_cnt %d\n",
- region, region->ref_cnt);
+ MM_DBG("%s[%p]:region %p in use ref_cnt %d\n",
+ __func__, audio, region,
+ region->ref_cnt);
break;
}
MM_DBG("remove region fd %d vaddr %p\n",
info->fd, info->vaddr);
list_del(®ion->list);
- put_pmem_file(region->file);
+ ion_unmap_kernel(audio->client, region->handle);
+ ion_free(audio->client, region->handle);
kfree(region);
rc = 0;
break;
@@ -885,23 +909,20 @@
return rc;
}
-static int audlpa_pmem_lookup_vaddr(struct audio *audio, void *addr,
- unsigned long len, struct audlpa_pmem_region **region)
+static int audlpa_ion_lookup_vaddr(struct audio *audio, void *addr,
+ unsigned long len, struct audlpa_ion_region **region)
{
- struct audlpa_pmem_region *region_elt;
-
+ struct audlpa_ion_region *region_elt;
int match_count = 0;
-
*region = NULL;
/* returns physical address or zero */
- list_for_each_entry(region_elt, &audio->pmem_region_queue,
- list) {
+ list_for_each_entry(region_elt, &audio->ion_region_queue, list) {
if (addr >= region_elt->vaddr &&
addr < region_elt->vaddr + region_elt->len &&
addr + len <= region_elt->vaddr + region_elt->len) {
/* offset since we could pass vaddr inside a registerd
- * pmem buffer
+ * ion buffer
*/
match_count++;
@@ -911,13 +932,16 @@
}
if (match_count > 1) {
- MM_ERR("multiple hits for vaddr %p, len %ld\n", addr, len);
- list_for_each_entry(region_elt,
- &audio->pmem_region_queue, list) {
+ MM_ERR("%s[%p]:multiple hits for vaddr %p, len %ld\n",
+ __func__, audio, addr, len);
+ list_for_each_entry(region_elt, &audio->ion_region_queue,
+ list) {
if (addr >= region_elt->vaddr &&
addr < region_elt->vaddr + region_elt->len &&
addr + len <= region_elt->vaddr + region_elt->len)
- MM_ERR("\t%p, %ld --> %p\n", region_elt->vaddr,
+ MM_ERR("\t%s[%p]:%p, %ld --> %p\n",
+ __func__, audio,
+ region_elt->vaddr,
region_elt->len,
(void *)region_elt->paddr);
}
@@ -925,17 +949,17 @@
return *region ? 0 : -1;
}
-
-unsigned long audlpa_pmem_fixup(struct audio *audio, void *addr,
+static unsigned long audlpa_ion_fixup(struct audio *audio, void *addr,
unsigned long len, int ref_up)
{
- struct audlpa_pmem_region *region;
+ struct audlpa_ion_region *region;
unsigned long paddr;
int ret;
- ret = audlpa_pmem_lookup_vaddr(audio, addr, len, ®ion);
+ ret = audlpa_ion_lookup_vaddr(audio, addr, len, ®ion);
if (ret) {
- MM_ERR("lookup (%p, %ld) failed\n", addr, len);
+ MM_ERR("%s[%p]:lookup (%p, %ld) failed\n",
+ __func__, audio, addr, len);
return 0;
}
if (ref_up)
@@ -969,7 +993,7 @@
buf_node->buf.buf_addr, buf_node->buf.buf_len,
buf_node->buf.data_len);
- buf_node->paddr = audlpa_pmem_fixup(
+ buf_node->paddr = audlpa_ion_fixup(
audio, buf_node->buf.buf_addr,
buf_node->buf.buf_len, 1);
@@ -1269,25 +1293,26 @@
audio->drv_status &= ~ADRV_STATUS_PAUSE;
break;
- case AUDIO_REGISTER_PMEM: {
- struct msm_audio_pmem_info info;
- MM_DBG("AUDIO_REGISTER_PMEM\n");
- if (copy_from_user(&info, (void *) arg, sizeof(info)))
+ case AUDIO_REGISTER_ION: {
+ struct msm_audio_ion_info info;
+ MM_DBG("AUDIO_REGISTER_ION\n");
+ if (copy_from_user(&info, (void *) arg, sizeof(info)))
rc = -EFAULT;
else
- rc = audlpa_pmem_add(audio, &info);
+ rc = audlpa_ion_add(audio, &info);
break;
}
- case AUDIO_DEREGISTER_PMEM: {
- struct msm_audio_pmem_info info;
- MM_DBG("AUDIO_DEREGISTER_PMEM\n");
- if (copy_from_user(&info, (void *) arg, sizeof(info)))
+ case AUDIO_DEREGISTER_ION: {
+ struct msm_audio_ion_info info;
+ MM_DBG("AUDIO_DEREGISTER_ION\n");
+ if (copy_from_user(&info, (void *) arg, sizeof(info)))
rc = -EFAULT;
else
- rc = audlpa_pmem_remove(audio, &info);
+ rc = audlpa_ion_remove(audio, &info);
break;
}
+
case AUDIO_ASYNC_WRITE:
if (audio->drv_status & ADRV_STATUS_FSYNC)
rc = -EBUSY;
@@ -1373,15 +1398,16 @@
return audlpa_async_fsync(audio);
}
-static void audlpa_reset_pmem_region(struct audio *audio)
+static void audpcm_reset_ion_region(struct audio *audio)
{
- struct audlpa_pmem_region *region;
+ struct audlpa_ion_region *region;
struct list_head *ptr, *next;
- list_for_each_safe(ptr, next, &audio->pmem_region_queue) {
- region = list_entry(ptr, struct audlpa_pmem_region, list);
+ list_for_each_safe(ptr, next, &audio->ion_region_queue) {
+ region = list_entry(ptr, struct audlpa_ion_region, list);
list_del(®ion->list);
- put_pmem_file(region->file);
+ ion_unmap_kernel(audio->client, region->handle);
+ ion_free(audio->client, region->handle);
kfree(region);
}
@@ -1399,7 +1425,7 @@
auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
audio_disable(audio);
audlpa_async_flush(audio);
- audlpa_reset_pmem_region(audio);
+ audpcm_reset_ion_region(audio);
msm_adsp_put(audio->audplay);
audpp_adec_free(audio->dec_id);
@@ -1410,13 +1436,12 @@
audio->event_abort = 1;
wake_up(&audio->event_wait);
audlpa_reset_event_queue(audio);
- iounmap(audio->data);
- free_contiguous_memory_by_paddr(audio->phys);
mutex_unlock(&audio->lock);
#ifdef CONFIG_DEBUG_FS
if (audio->dentry)
debugfs_remove(audio->dentry);
#endif
+ ion_client_destroy(audio->client);
kfree(audio);
return 0;
}
@@ -1589,7 +1614,7 @@
spin_lock_init(&audio->dsp_lock);
init_waitqueue_head(&audio->write_wait);
INIT_LIST_HEAD(&audio->out_queue);
- INIT_LIST_HEAD(&audio->pmem_region_queue);
+ INIT_LIST_HEAD(&audio->ion_region_queue);
INIT_LIST_HEAD(&audio->free_event_queue);
INIT_LIST_HEAD(&audio->event_queue);
init_waitqueue_head(&audio->wait);
@@ -1650,13 +1675,19 @@
break;
}
}
+
+ audio->client = msm_ion_client_create(UINT_MAX, "Audio_LPA_Client");
+ if (IS_ERR_OR_NULL(audio->client)) {
+ pr_err("Unable to create ION client\n");
+ goto err;
+ }
+ MM_DBG("Ion client created\n");
+
done:
return rc;
event_err:
msm_adsp_put(audio->audplay);
err:
- iounmap(audio->data);
- free_contiguous_memory_by_paddr(audio->phys);
audpp_adec_free(audio->dec_id);
MM_INFO("audio instance 0x%08x freeing\n", (int)audio);
kfree(audio);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_out.c b/arch/arm/mach-msm/qdsp5v2/audio_out.c
index 147ac77..e5c59ba 100644
--- a/arch/arm/mach-msm/qdsp5v2/audio_out.c
+++ b/arch/arm/mach-msm/qdsp5v2/audio_out.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-2012, 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
@@ -43,7 +43,7 @@
#include <mach/qdsp5v2/audio_dev_ctl.h>
#include <mach/msm_memtypes.h>
#include <mach/cpuidle.h>
-
+#include <linux/msm_ion.h>
#include <mach/htc_pwrsink.h>
#include <mach/debug_mm.h>
@@ -98,6 +98,8 @@
struct pm_qos_request pm_qos_req;
struct audpp_cmd_cfg_object_params_volume vol_pan;
+ struct ion_client *client;
+ struct ion_handle *buff_handle;
};
static void audio_out_listener(u32 evt_id, union auddev_evt_data *evt_payload,
@@ -702,19 +704,53 @@
static int __init audio_init(void)
{
- the_audio.phys = allocate_contiguous_ebi_nomap(DMASZ, SZ_4K);
- if (the_audio.phys) {
- the_audio.map_v_write = ioremap(the_audio.phys, DMASZ);
- if (IS_ERR(the_audio.map_v_write)) {
- MM_ERR("could not map physical buffers\n");
- free_contiguous_memory_by_paddr(the_audio.phys);
- return -ENOMEM;
- }
- the_audio.data = the_audio.map_v_write;
- } else {
- MM_ERR("could not allocate physical buffers\n");
- return -ENOMEM;
+ unsigned long ionflag = 0;
+ ion_phys_addr_t addr = 0;
+ int rc;
+ int len = 0;
+ struct ion_handle *handle = NULL;
+ struct ion_client *client = NULL;
+
+ client = msm_ion_client_create(UINT_MAX, "HostPCM");
+ if (IS_ERR_OR_NULL(client)) {
+ MM_ERR("Unable to create ION client\n");
+ rc = -ENOMEM;
+ goto client_create_error;
}
+ the_audio.client = client;
+
+ handle = ion_alloc(client, DMASZ, SZ_4K,
+ ION_HEAP(ION_AUDIO_HEAP_ID), 0);
+ if (IS_ERR_OR_NULL(handle)) {
+ MM_ERR("Unable to create allocate O/P buffers\n");
+ rc = -ENOMEM;
+ goto buff_alloc_error;
+ }
+ the_audio.buff_handle = handle;
+
+ rc = ion_phys(client, handle, &addr, &len);
+ if (rc) {
+ MM_ERR("O/P buffers:Invalid phy: %x sz: %x\n",
+ (unsigned int) addr, (unsigned int) len);
+ goto buff_get_phys_error;
+ } else
+ MM_INFO("O/P buffers:valid phy: %x sz: %x\n",
+ (unsigned int) addr, (unsigned int) len);
+ the_audio.phys = (int32_t)addr;
+
+ rc = ion_handle_get_flags(client, handle, &ionflag);
+ if (rc) {
+ MM_ERR("could not get flags for the handle\n");
+ goto buff_get_flags_error;
+ }
+
+ the_audio.map_v_write = ion_map_kernel(client, handle);
+ if (IS_ERR(the_audio.map_v_write)) {
+ MM_ERR("could not map write buffers\n");
+ rc = -ENOMEM;
+ goto buff_map_error;
+ }
+ the_audio.data = (char *)the_audio.map_v_write;
MM_DBG("Memory addr = 0x%8x phy addr = 0x%8x\n",\
(int) the_audio.data, (int) the_audio.phys);
mutex_init(&the_audio.lock);
@@ -725,6 +761,15 @@
pm_qos_add_request(&the_audio.pm_qos_req, PM_QOS_CPU_DMA_LATENCY,
PM_QOS_DEFAULT_VALUE);
return misc_register(&audio_misc);
+buff_map_error:
+buff_get_phys_error:
+buff_get_flags_error:
+ ion_free(client, the_audio.buff_handle);
+buff_alloc_error:
+ ion_client_destroy(client);
+client_create_error:
+ return rc;
+
}
late_initcall(audio_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c b/arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c
index ce67ebb..ff3a696 100644
--- a/arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c
+++ b/arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-2012, 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
@@ -27,7 +27,7 @@
#include <linux/wait.h>
#include <linux/dma-mapping.h>
#include <linux/msm_audio.h>
-#include <linux/android_pmem.h>
+#include <linux/msm_ion.h>
#include <linux/memory_alloc.h>
#include <mach/msm_memtypes.h>
@@ -121,6 +121,8 @@
int abort; /* set when error, like sample rate mismatch */
int dual_mic_config;
char *build_id;
+ struct ion_client *client;
+ struct ion_handle *output_buff_handle;
};
static struct audio_in the_audio_in;
@@ -842,10 +844,11 @@
audio->audrec = NULL;
audio->opened = 0;
if (audio->data) {
- iounmap(audio->map_v_read);
- free_contiguous_memory_by_paddr(audio->phys);
+ ion_unmap_kernel(audio->client, audio->output_buff_handle);
+ ion_free(audio->client, audio->output_buff_handle);
audio->data = NULL;
}
+ ion_client_destroy(audio->client);
mutex_unlock(&audio->lock);
return 0;
}
@@ -855,27 +858,68 @@
struct audio_in *audio = &the_audio_in;
int rc;
int encid;
+ int len = 0;
+ unsigned long ionflag = 0;
+ ion_phys_addr_t addr = 0;
+ struct ion_handle *handle = NULL;
+ struct ion_client *client = NULL;
mutex_lock(&audio->lock);
if (audio->opened) {
rc = -EBUSY;
goto done;
}
- audio->phys = allocate_contiguous_ebi_nomap(DMASZ, SZ_4K);
- if (audio->phys) {
- audio->map_v_read = ioremap(audio->phys, DMASZ);
- if (IS_ERR(audio->map_v_read)) {
- MM_ERR("could not map read phys buffers\n");
+
+ client = msm_ion_client_create(UINT_MAX, "Audio_PCM_in_client");
+ if (IS_ERR_OR_NULL(client)) {
+ MM_ERR("Unable to create ION client\n");
rc = -ENOMEM;
- free_contiguous_memory_by_paddr(audio->phys);
- goto done;
- }
- audio->data = audio->map_v_read;
- } else {
- MM_ERR("could not allocate read buffers\n");
- rc = -ENOMEM;
- goto done;
+ goto client_create_error;
}
+ audio->client = client;
+
+ MM_DBG("allocating mem sz = %d\n", DMASZ);
+ handle = ion_alloc(client, DMASZ, SZ_4K,
+ ION_HEAP(ION_AUDIO_HEAP_ID), 0);
+ if (IS_ERR_OR_NULL(handle)) {
+ MM_ERR("Unable to create allocate O/P buffers\n");
+ rc = -ENOMEM;
+ goto output_buff_alloc_error;
+ }
+
+ audio->output_buff_handle = handle;
+
+ rc = ion_phys(client , handle, &addr, &len);
+ if (rc) {
+ MM_ERR("O/P buffers:Invalid phy: %x sz: %x\n",
+ (unsigned int) addr, (unsigned int) len);
+ rc = -ENOMEM;
+ goto output_buff_get_phys_error;
+ } else {
+ MM_INFO("O/P buffers:valid phy: %x sz: %x\n",
+ (unsigned int) addr, (unsigned int) len);
+ }
+ audio->phys = (int32_t)addr;
+
+ rc = ion_handle_get_flags(client, handle, &ionflag);
+ if (rc) {
+ MM_ERR("could not get flags for the handle\n");
+ rc = -ENOMEM;
+ goto output_buff_get_flags_error;
+ }
+
+ audio->map_v_read = ion_map_kernel(client, handle);
+ if (IS_ERR(audio->data)) {
+ MM_ERR("could not map read buffers,freeing instance 0x%08x\n",
+ (int)audio);
+ rc = -ENOMEM;
+ goto output_buff_map_error;
+ }
+ MM_DBG("read buf: phy addr 0x%08x kernel addr 0x%08x\n",
+ audio->phys, (int)audio->data);
+
+ audio->data = (char *)audio->map_v_read;
+
MM_DBG("Memory addr = 0x%8x phy addr = 0x%8x\n",\
(int) audio->data, (int) audio->phys);
if ((file->f_mode & FMODE_WRITE) &&
@@ -941,6 +985,13 @@
mutex_unlock(&audio->lock);
return rc;
evt_error:
+output_buff_map_error:
+output_buff_get_phys_error:
+output_buff_get_flags_error:
+ ion_free(client, audio->output_buff_handle);
+output_buff_alloc_error:
+ ion_client_destroy(client);
+client_create_error:
msm_adsp_put(audio->audrec);
audpreproc_aenc_free(audio->enc_id);
mutex_unlock(&audio->lock);
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_acdb.c b/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
index 7272f97..a24b9ec 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
@@ -10,6 +10,7 @@
* GNU General Public License for more details.
*
*/
+#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
@@ -20,7 +21,14 @@
#include <mach/qdsp6v2/audio_acdb.h>
-#define MAX_NETWORKS 15
+#define MAX_NETWORKS 15
+#define MAX_IOCTL_DATA (MAX_NETWORKS * 2)
+#define MAX_COL_SIZE 324
+
+#define ACDB_BLOCK_SIZE 4096
+#define NUM_VOCPROC_BLOCKS (6 * MAX_NETWORKS)
+#define ACDB_TOTAL_VOICE_ALLOCATION (ACDB_BLOCK_SIZE * NUM_VOCPROC_BLOCKS)
+
struct sidetone_atomic_cal {
atomic_t enable;
@@ -56,6 +64,15 @@
atomic_t vocstrm_total_cal_size;
atomic_t vocvol_total_cal_size;
+ /* Voice Column data */
+ struct acdb_atomic_cal_block vocproc_col_cal[MAX_VOCPROC_TYPES];
+ uint32_t *col_data[MAX_VOCPROC_TYPES];
+
+ /* VocProc dev cfg cal*/
+ struct acdb_atomic_cal_block vocproc_dev_cal[MAX_NETWORKS];
+ atomic_t vocproc_dev_cal_size;
+ atomic_t vocproc_dev_total_cal_size;
+
/* AFE cal */
struct acdb_atomic_cal_block afe_cal[MAX_AUDPROC_TYPES];
@@ -124,6 +141,15 @@
atomic_set(&acdb_data.asm_topology, topology);
}
+void get_voice_cal_allocation(struct acdb_cal_block *cal_block)
+{
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.vocproc_cal[0].cal_kvaddr);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.vocproc_cal[0].cal_paddr);
+ cal_block->cal_size = ACDB_TOTAL_VOICE_ALLOCATION;
+}
+
void get_all_voice_cal(struct acdb_cal_block *cal_block)
{
cal_block->cal_kvaddr =
@@ -177,6 +203,45 @@
atomic_read(&acdb_data.vocvol_total_cal_size);
}
+void get_voice_col_data(uint32_t vocproc_type,
+ struct acdb_cal_block *cal_block)
+{
+ if (cal_block == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ goto done;
+ }
+
+ cal_block->cal_kvaddr = atomic_read(&acdb_data.
+ vocproc_col_cal[vocproc_type].cal_kvaddr);
+ cal_block->cal_paddr = atomic_read(&acdb_data.
+ vocproc_col_cal[vocproc_type].cal_paddr);
+ cal_block->cal_size = atomic_read(&acdb_data.
+ vocproc_col_cal[vocproc_type].cal_size);
+done:
+ return;
+}
+
+void store_voice_col_data(uint32_t vocproc_type, uint32_t cal_size,
+ uint32_t *cal_data)
+{
+ if (cal_size > MAX_COL_SIZE) {
+ pr_err("%s: col size is to big %d\n", __func__,
+ cal_size);
+ goto done;
+ }
+ if (copy_from_user(acdb_data.col_data[vocproc_type],
+ (void *)((uint8_t *)cal_data + sizeof(cal_size)),
+ cal_size)) {
+ pr_err("%s: fail to copy col size %d\n",
+ __func__, cal_size);
+ goto done;
+ }
+ atomic_set(&acdb_data.vocproc_col_cal[vocproc_type].cal_size,
+ cal_size);
+done:
+ return;
+}
+
void get_anc_cal(struct acdb_cal_block *cal_block)
{
pr_debug("%s\n", __func__);
@@ -417,6 +482,56 @@
return;
}
+void store_vocproc_dev_cfg_cal(int32_t len, struct cal_block *cal_blocks)
+{
+ int i;
+ pr_debug("%s\n", __func__);
+
+ if (len > MAX_NETWORKS) {
+ pr_err("%s: Calibration sent for %d networks, only %d are supported!\n",
+ __func__, len, MAX_NETWORKS);
+ goto done;
+ }
+
+ atomic_set(&acdb_data.vocproc_dev_total_cal_size, 0);
+ for (i = 0; i < len; i++) {
+ if (cal_blocks[i].cal_offset >
+ atomic64_read(&acdb_data.mem_len)) {
+ pr_err("%s: offset %d is > mem_len %ld\n",
+ __func__, cal_blocks[i].cal_offset,
+ (long)atomic64_read(&acdb_data.mem_len));
+ atomic_set(&acdb_data.vocproc_dev_cal[i].cal_size, 0);
+ } else {
+ atomic_add(cal_blocks[i].cal_size,
+ &acdb_data.vocproc_dev_total_cal_size);
+ atomic_set(&acdb_data.vocproc_dev_cal[i].cal_size,
+ cal_blocks[i].cal_size);
+ atomic_set(&acdb_data.vocproc_dev_cal[i].cal_paddr,
+ cal_blocks[i].cal_offset +
+ atomic64_read(&acdb_data.paddr));
+ atomic_set(&acdb_data.vocproc_dev_cal[i].cal_kvaddr,
+ cal_blocks[i].cal_offset +
+ atomic64_read(&acdb_data.kvaddr));
+ }
+ }
+ atomic_set(&acdb_data.vocproc_dev_cal_size, len);
+done:
+ return;
+}
+
+void get_vocproc_dev_cfg_cal(struct acdb_cal_block *cal_block)
+{
+ pr_debug("%s\n", __func__);
+
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.vocproc_dev_cal[0].cal_kvaddr);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.vocproc_dev_cal[0].cal_paddr);
+ cal_block->cal_size =
+ atomic_read(&acdb_data.vocproc_dev_total_cal_size);
+}
+
+
void store_vocproc_cal(int32_t len, struct cal_block *cal_blocks)
{
@@ -698,7 +813,7 @@
int32_t size;
int32_t map_fd;
uint32_t topology;
- struct cal_block data[MAX_NETWORKS];
+ uint32_t data[MAX_IOCTL_DATA];
pr_debug("%s\n", __func__);
switch (cmd) {
@@ -777,6 +892,18 @@
goto done;
}
+ switch (cmd) {
+ case AUDIO_SET_VOCPROC_COL_CAL:
+ store_voice_col_data(VOCPROC_CAL, size, (uint32_t *)arg);
+ goto done;
+ case AUDIO_SET_VOCSTRM_COL_CAL:
+ store_voice_col_data(VOCSTRM_CAL, size, (uint32_t *)arg);
+ goto done;
+ case AUDIO_SET_VOCVOL_COL_CAL:
+ store_voice_col_data(VOCVOL_CAL, size, (uint32_t *)arg);
+ goto done;
+ }
+
if (copy_from_user(data, (void *)(arg + sizeof(size)), size)) {
pr_err("%s: fail to copy table size %d\n", __func__, size);
@@ -795,58 +922,65 @@
if (size > sizeof(struct cal_block))
pr_err("%s: More Audproc Cal then expected, "
"size received: %d\n", __func__, size);
- store_audproc_cal(TX_CAL, data);
+ store_audproc_cal(TX_CAL, (struct cal_block *)data);
break;
case AUDIO_SET_AUDPROC_RX_CAL:
if (size > sizeof(struct cal_block))
pr_err("%s: More Audproc Cal then expected, "
"size received: %d\n", __func__, size);
- store_audproc_cal(RX_CAL, data);
+ store_audproc_cal(RX_CAL, (struct cal_block *)data);
break;
case AUDIO_SET_AUDPROC_TX_STREAM_CAL:
if (size > sizeof(struct cal_block))
pr_err("%s: More Audproc Cal then expected, "
"size received: %d\n", __func__, size);
- store_audstrm_cal(TX_CAL, data);
+ store_audstrm_cal(TX_CAL, (struct cal_block *)data);
break;
case AUDIO_SET_AUDPROC_RX_STREAM_CAL:
if (size > sizeof(struct cal_block))
pr_err("%s: More Audproc Cal then expected, "
"size received: %d\n", __func__, size);
- store_audstrm_cal(RX_CAL, data);
+ store_audstrm_cal(RX_CAL, (struct cal_block *)data);
break;
case AUDIO_SET_AUDPROC_TX_VOL_CAL:
if (size > sizeof(struct cal_block))
pr_err("%s: More Audproc Cal then expected, "
"size received: %d\n", __func__, size);
- store_audvol_cal(TX_CAL, data);
+ store_audvol_cal(TX_CAL, (struct cal_block *)data);
break;
case AUDIO_SET_AUDPROC_RX_VOL_CAL:
if (size > sizeof(struct cal_block))
pr_err("%s: More Audproc Cal then expected, "
"size received: %d\n", __func__, size);
- store_audvol_cal(RX_CAL, data);
+ store_audvol_cal(RX_CAL, (struct cal_block *)data);
break;
case AUDIO_SET_AFE_TX_CAL:
if (size > sizeof(struct cal_block))
pr_err("%s: More AFE Cal then expected, "
"size received: %d\n", __func__, size);
- store_afe_cal(TX_CAL, data);
+ store_afe_cal(TX_CAL, (struct cal_block *)data);
break;
case AUDIO_SET_AFE_RX_CAL:
if (size > sizeof(struct cal_block))
pr_err("%s: More AFE Cal then expected, "
"size received: %d\n", __func__, size);
- store_afe_cal(RX_CAL, data);
+ store_afe_cal(RX_CAL, (struct cal_block *)data);
break;
case AUDIO_SET_VOCPROC_CAL:
- store_vocproc_cal(size / sizeof(struct cal_block), data);
+ store_vocproc_cal(size / sizeof(struct cal_block),
+ (struct cal_block *)data);
break;
case AUDIO_SET_VOCPROC_STREAM_CAL:
- store_vocstrm_cal(size / sizeof(struct cal_block), data);
+ store_vocstrm_cal(size / sizeof(struct cal_block),
+ (struct cal_block *)data);
break;
case AUDIO_SET_VOCPROC_VOL_CAL:
- store_vocvol_cal(size / sizeof(struct cal_block), data);
+ store_vocvol_cal(size / sizeof(struct cal_block),
+ (struct cal_block *)data);
+ break;
+ case AUDIO_SET_VOCPROC_DEV_CFG_CAL:
+ store_vocproc_dev_cfg_cal(size / sizeof(struct cal_block),
+ (struct cal_block *)data);
break;
case AUDIO_SET_SIDETONE_CAL:
if (size > sizeof(struct sidetone_cal))
@@ -855,7 +989,7 @@
store_sidetone_cal((struct sidetone_cal *)data);
break;
case AUDIO_SET_ANC_CAL:
- store_anc_cal(data);
+ store_anc_cal((struct cal_block *)data);
break;
default:
pr_err("ACDB=> ACDB ioctl not found!\n");
@@ -895,6 +1029,7 @@
static int acdb_release(struct inode *inode, struct file *f)
{
+ int i;
s32 result = 0;
atomic_dec(&usage_count);
@@ -903,6 +1038,11 @@
pr_debug("%s: ref count %d!\n", __func__,
atomic_read(&usage_count));
+ for (i = 0; i < MAX_VOCPROC_TYPES; i++) {
+ kfree(acdb_data.col_data[i]);
+ acdb_data.col_data[i] = NULL;
+ }
+
if (atomic_read(&usage_count) >= 1)
result = -EBUSY;
else
@@ -927,9 +1067,16 @@
static int __init acdb_init(void)
{
+ int i;
memset(&acdb_data, 0, sizeof(acdb_data));
mutex_init(&acdb_data.acdb_mutex);
atomic_set(&usage_count, 0);
+
+ for (i = 0; i < MAX_VOCPROC_TYPES; i++) {
+ acdb_data.col_data[i] = kmalloc(MAX_COL_SIZE, GFP_KERNEL);
+ atomic_set(&acdb_data.vocproc_col_cal[i].cal_kvaddr,
+ (uint32_t)acdb_data.col_data[i]);
+ }
return misc_register(&acdb_misc);
}
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_lpa.c b/arch/arm/mach-msm/qdsp6v2/audio_lpa.c
index 176f364..edf8f77 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_lpa.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_lpa.c
@@ -707,15 +707,6 @@
case RESET_EVENTS:
reset_device();
break;
- case APR_BASIC_RSP_RESULT:
- switch (payload[0]) {
- case ASM_STREAM_CMD_CLOSE:
- audlpa_unmap_ion_region(audio);
- break;
- default:
- break;
- }
- break;
default:
break;
}
@@ -1136,6 +1127,7 @@
audlpa_async_flush(audio);
audio->wflush = 0;
audio_disable(audio);
+ audlpa_unmap_ion_region(audio);
msm_clear_session_id(audio->ac->session);
auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->ac->session);
q6asm_audio_client_free(audio->ac);
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
index 28bf5c6..d6abdda 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
@@ -403,7 +403,7 @@
return;
}
-void audio_aio_unmap_ion_region(struct q6audio_aio *audio)
+static void audio_aio_unmap_ion_region(struct q6audio_aio *audio)
{
struct audio_aio_ion_region *region;
struct list_head *ptr, *next;
@@ -436,6 +436,7 @@
audio->drv_ops.out_flush(audio);
audio->drv_ops.in_flush(audio);
audio_aio_disable(audio);
+ audio_aio_unmap_ion_region(audio);
audio_aio_reset_ion_region(audio);
ion_client_destroy(audio->client);
audio->event_abort = 1;
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.h b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.h
index 2b936c5..b2829c3 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.h
+++ b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.h
@@ -210,7 +210,6 @@
int audio_aio_fsync(struct file *file, loff_t start, loff_t end, int datasync);
void audio_aio_async_out_flush(struct q6audio_aio *audio);
void audio_aio_async_in_flush(struct q6audio_aio *audio);
-void audio_aio_unmap_ion_region(struct q6audio_aio *audio);
#ifdef CONFIG_DEBUG_FS
ssize_t audio_aio_debug_open(struct inode *inode, struct file *file);
ssize_t audio_aio_debug_read(struct file *file, char __user *buf,
diff --git a/arch/arm/mach-msm/qdsp6v2/q6audio_v1_aio.c b/arch/arm/mach-msm/qdsp6v2/q6audio_v1_aio.c
index b46e0d3..96f823f 100644
--- a/arch/arm/mach-msm/qdsp6v2/q6audio_v1_aio.c
+++ b/arch/arm/mach-msm/qdsp6v2/q6audio_v1_aio.c
@@ -37,7 +37,6 @@
case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
case ASM_DATA_EVENT_ENC_SR_CM_NOTIFY:
- case APR_BASIC_RSP_RESULT:
audio_aio_cb(opcode, token, payload, audio);
break;
default:
@@ -107,15 +106,6 @@
e_payload.stream_info.sample_rate = audio->pcm_cfg.sample_rate;
audio_aio_post_event(audio, AUDIO_EVENT_STREAM_INFO, e_payload);
break;
- case APR_BASIC_RSP_RESULT:
- switch (payload[0]) {
- case ASM_STREAM_CMD_CLOSE:
- audio_aio_unmap_ion_region(audio);
- break;
- default:
- break;
- }
- break;
default:
break;
}
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.c b/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.c
index 5400ccc..dce3812 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.c
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.c
@@ -515,6 +515,9 @@
case FORMAT_USRAW:
int_format = US_RAW_FORMAT;
break;
+ case FORMAT_USPROX:
+ int_format = US_PROX_FORMAT;
+ break;
default:
pr_err("%s: Invalid format[%d]\n", __func__, ext_format);
break;
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h b/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h
index 1338e86..1fe71bf 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h
@@ -20,6 +20,7 @@
#define FORMAT_USPS_EPOS 0x00000000
#define FORMAT_USRAW 0x00000001
+#define FORMAT_USPROX 0x00000002
#define INVALID_FORMAT 0xffffffff
#define IN 0x000
diff --git a/arch/arm/mach-msm/restart_7k.c b/arch/arm/mach-msm/restart_7k.c
index dc9edf4..9675b61 100644
--- a/arch/arm/mach-msm/restart_7k.c
+++ b/arch/arm/mach-msm/restart_7k.c
@@ -21,13 +21,11 @@
#include <mach/proc_comm.h>
#include "devices-msm7x2xa.h"
-#include "smd_rpcrouter.h"
static uint32_t restart_reason = 0x776655AA;
static void msm_pm_power_off(void)
{
- msm_rpcrouter_close();
msm_proc_comm(PCOM_POWER_DOWN, 0, 0);
for (;;)
;
@@ -35,7 +33,6 @@
static void msm_pm_restart(char str, const char *cmd)
{
- msm_rpcrouter_close();
pr_debug("The reset reason is %x\n", restart_reason);
/* Disable interrupts */
diff --git a/arch/arm/mach-msm/rpm-regulator-8960.c b/arch/arm/mach-msm/rpm-regulator-8960.c
index 8fe3571..c5c01c2 100644
--- a/arch/arm/mach-msm/rpm-regulator-8960.c
+++ b/arch/arm/mach-msm/rpm-regulator-8960.c
@@ -325,3 +325,32 @@
{
return &config;
}
+
+struct vreg_config *get_config_8960_pm8917(void)
+{
+ int i;
+
+ /*
+ * PM8917 regulators L24, L25, L26, L27, and L28 require CXO to be ON
+ * while they are enabled. These same regulators on PM8921 do not
+ * require CXO to be ON. Therefore, set the require_cxo flag for these
+ * regulators only when using PM8917.
+ *
+ * Do not apply the workaround to L24 (VDD_MX) because it is always on
+ * and using the TCXO workaround with it would result in additional
+ * latency during every Krait upscaling event.
+ */
+ for (i = 0; i < ARRAY_SIZE(vregs); i++) {
+ switch (vregs[i].id) {
+ case RPM_VREG_ID_PM8921_L25:
+ case RPM_VREG_ID_PM8921_L26:
+ case RPM_VREG_ID_PM8921_L27:
+ case RPM_VREG_ID_PM8921_L28:
+ vregs[i].requires_cxo = true;
+ default:
+ break;
+ }
+ }
+
+ return &config;
+}
diff --git a/arch/arm/mach-msm/rpm-regulator-private.h b/arch/arm/mach-msm/rpm-regulator-private.h
index d55bd73..703335f 100644
--- a/arch/arm/mach-msm/rpm-regulator-private.h
+++ b/arch/arm/mach-msm/rpm-regulator-private.h
@@ -158,11 +158,16 @@
#if defined(CONFIG_MSM_RPM_REGULATOR) && \
(defined(CONFIG_ARCH_MSM8960) || defined(CONFIG_ARCH_APQ8064))
struct vreg_config *get_config_8960(void);
+struct vreg_config *get_config_8960_pm8917(void);
#else
static inline struct vreg_config *get_config_8960(void)
{
return NULL;
}
+static inline struct vreg_config *get_config_8960_pm8917(void)
+{
+ return NULL;
+}
#endif
#if defined(CONFIG_MSM_RPM_REGULATOR) && defined(CONFIG_ARCH_MSM9615)
diff --git a/arch/arm/mach-msm/rpm-regulator-smd.c b/arch/arm/mach-msm/rpm-regulator-smd.c
index a8af9e7..d1c61fe 100644
--- a/arch/arm/mach-msm/rpm-regulator-smd.c
+++ b/arch/arm/mach-msm/rpm-regulator-smd.c
@@ -112,7 +112,7 @@
PARAM(HEAD_ROOM, 1, 0, 0, 1, "hr", 0, 0x7FFFFFFF, "qcom,init-head-room"),
PARAM(QUIET_MODE, 0, 1, 0, 0, "qm", 0, 2, "qcom,init-quiet-mode"),
PARAM(FREQ_REASON, 0, 1, 0, 1, "resn", 0, 8, "qcom,init-freq-reason"),
- PARAM(CORNER, 0, 1, 0, 0, "corn", 0, 6, "qcom,init-voltage-corner"),
+ PARAM(CORNER, 1, 1, 0, 0, "corn", 0, 6, "qcom,init-voltage-corner"),
PARAM(BYPASS, 1, 0, 0, 0, "bypa", 0, 1, "qcom,init-disallow-bypass"),
};
@@ -1037,6 +1037,19 @@
.enable_time = rpm_vreg_enable_time,
};
+static struct regulator_ops ldo_corner_ops = {
+ .enable = rpm_vreg_enable,
+ .disable = rpm_vreg_disable,
+ .is_enabled = rpm_vreg_is_enabled,
+ .set_voltage = rpm_vreg_set_voltage_corner,
+ .get_voltage = rpm_vreg_get_voltage_corner,
+ .list_voltage = rpm_vreg_list_voltage,
+ .set_mode = rpm_vreg_set_mode,
+ .get_mode = rpm_vreg_get_mode,
+ .get_optimum_mode = rpm_vreg_get_optimum_mode,
+ .enable_time = rpm_vreg_enable_time,
+};
+
static struct regulator_ops smps_ops = {
.enable = rpm_vreg_enable,
.disable = rpm_vreg_disable,
@@ -1194,11 +1207,14 @@
/*
* Switch to voltage corner regulator ops if qcom,use-voltage-corner
- * is specified in the device node (SMPS only).
+ * is specified in the device node (SMPS and LDO only).
*/
- if (of_find_property(node, "qcom,use-voltage-corner", NULL)
- && regulator_type == RPM_REGULATOR_SMD_TYPE_SMPS)
- reg->rdesc.ops = &smps_corner_ops;
+ if (of_property_read_bool(node, "qcom,use-voltage-corner")) {
+ if (regulator_type == RPM_REGULATOR_SMD_TYPE_SMPS)
+ reg->rdesc.ops = &smps_corner_ops;
+ else if (regulator_type == RPM_REGULATOR_SMD_TYPE_LDO)
+ reg->rdesc.ops = &ldo_corner_ops;
+ }
if (regulator_type == RPM_REGULATOR_SMD_TYPE_VS)
reg->rdesc.n_voltages = 0;
@@ -1286,7 +1302,7 @@
}
}
- of_property_read_u32(node, "qcom,system_load", ®->system_load);
+ of_property_read_u32(node, "qcom,system-load", ®->system_load);
rpm_vreg_lock(rpm_vreg);
list_add(®->list, &rpm_vreg->reg_list);
diff --git a/arch/arm/mach-msm/rpm-regulator.c b/arch/arm/mach-msm/rpm-regulator.c
index 424a4fe..01543a2 100644
--- a/arch/arm/mach-msm/rpm-regulator.c
+++ b/arch/arm/mach-msm/rpm-regulator.c
@@ -63,6 +63,7 @@
[RPM_VREG_VERSION_9615] = get_config_9615,
[RPM_VREG_VERSION_8930] = get_config_8930,
[RPM_VREG_VERSION_8930_PM8917] = get_config_8930_pm8917,
+ [RPM_VREG_VERSION_8960_PM8917] = get_config_8960_pm8917,
};
static struct rpm_regulator_consumer_mapping *consumer_map;
diff --git a/arch/arm/mach-msm/rpm-smd.c b/arch/arm/mach-msm/rpm-smd.c
index a9d1ed8..f97eb9a 100644
--- a/arch/arm/mach-msm/rpm-smd.c
+++ b/arch/arm/mach-msm/rpm-smd.c
@@ -149,8 +149,6 @@
uint32_t id_ack;
};
-static int irq_process;
-
LIST_HEAD(msm_rpm_ack_list);
static void msm_rpm_notify_sleep_chain(struct rpm_message_header *hdr,
@@ -518,20 +516,18 @@
uint32_t msg_id;
int errno;
char buf[MAX_ERR_BUFFER_SIZE] = {0};
- unsigned long flags;
- while (smd_is_pkt_avail(msm_rpm_data.ch_info) && !irq_process) {
- spin_lock_irqsave(&msm_rpm_data.smd_lock_read, flags);
+ if (!spin_trylock(&msm_rpm_data.smd_lock_read))
+ return;
+ while (smd_is_pkt_avail(msm_rpm_data.ch_info)) {
if (msm_rpm_read_smd_data(buf)) {
- spin_unlock_irqrestore(&msm_rpm_data.smd_lock_read,
- flags);
break;
}
msg_id = msm_rpm_get_msg_id_from_ack(buf);
errno = msm_rpm_get_error_from_ack(buf);
msm_rpm_process_ack(msg_id, errno);
- spin_unlock_irqrestore(&msm_rpm_data.smd_lock_read, flags);
}
+ spin_unlock(&msm_rpm_data.smd_lock_read);
}
#define DEBUG_PRINT_BUFFER_SIZE 512
@@ -783,7 +779,14 @@
int msm_rpm_send_request(struct msm_rpm_request *handle)
{
- return msm_rpm_send_data(handle, MSM_RPM_MSG_REQUEST_TYPE, false);
+ int ret;
+ static DEFINE_MUTEX(send_mtx);
+
+ mutex_lock(&send_mtx);
+ ret = msm_rpm_send_data(handle, MSM_RPM_MSG_REQUEST_TYPE, false);
+ mutex_unlock(&send_mtx);
+
+ return ret;
}
EXPORT_SYMBOL(msm_rpm_send_request);
@@ -837,7 +840,6 @@
return 0;
spin_lock_irqsave(&msm_rpm_data.smd_lock_read, flags);
- irq_process = true;
elem = msm_rpm_get_entry_from_msg_id(msg_id);
@@ -868,7 +870,6 @@
rc = elem->errno;
msm_rpm_free_list_entry(elem);
wait_ack_cleanup:
- irq_process = false;
spin_unlock_irqrestore(&msm_rpm_data.smd_lock_read, flags);
return rc;
}
@@ -942,7 +943,7 @@
static bool msm_rpm_set_standalone(void)
{
- if (machine_is_msm8974()) {
+ if (machine_is_msm9625()) {
pr_warn("%s(): Running in standalone mode, requests "
"will not be sent to RPM\n", __func__);
standalone = true;
diff --git a/arch/arm/mach-msm/rpm_resources.c b/arch/arm/mach-msm/rpm_resources.c
index dfed3aa..43073d3 100644
--- a/arch/arm/mach-msm/rpm_resources.c
+++ b/arch/arm/mach-msm/rpm_resources.c
@@ -1131,9 +1131,8 @@
static int __init msm_rpmrs_l2_init(void)
{
- if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm8930aa() ||
- cpu_is_apq8064() || cpu_is_msm8627() || cpu_is_msm8960ab() ||
- cpu_is_apq8064ab()) {
+ if (soc_class_is_msm8960() || soc_class_is_msm8930() ||
+ soc_class_is_apq8064()) {
msm_pm_set_l2_flush_flag(0);
diff --git a/arch/arm/mach-msm/rpm_stats.h b/arch/arm/mach-msm/rpm_stats.h
index 918d4fb..a3beaa4 100644
--- a/arch/arm/mach-msm/rpm_stats.h
+++ b/arch/arm/mach-msm/rpm_stats.h
@@ -20,4 +20,19 @@
phys_addr_t phys_addr_base;
u32 phys_size;
};
+
+struct msm_rpm_master_stats_platform_data {
+ phys_addr_t phys_addr_base;
+ u32 phys_size;
+ char **masters;
+ /*
+ * RPM maintains PC stats for each master in MSG RAM,
+ * it allocates 256 bytes for this use.
+ * No of masters differs for different targets.
+ * Based on the number of masters, linux rpm stat
+ * driver reads (32 * nomasters) bytes to display
+ * master stats.
+ */
+ u32 nomasters;
+};
#endif
diff --git a/arch/arm/mach-msm/scm-pas.h b/arch/arm/mach-msm/scm-pas.h
index dd24e20..8da1d75 100644
--- a/arch/arm/mach-msm/scm-pas.h
+++ b/arch/arm/mach-msm/scm-pas.h
@@ -25,8 +25,29 @@
PAS_VIDC,
};
+#ifdef CONFIG_MSM_PIL
extern int pas_init_image(enum pas_id id, const u8 *metadata, size_t size);
extern int pas_auth_and_reset(enum pas_id id);
extern int pas_shutdown(enum pas_id id);
extern int pas_supported(enum pas_id id);
+#else
+static inline int pas_init_image(enum pas_id id, const u8 *metadata,
+ size_t size)
+{
+ return 0;
+}
+static inline int pas_auth_and_reset(enum pas_id id)
+{
+ return 0;
+}
+static inline int pas_shutdown(enum pas_id id)
+{
+ return 0;
+}
+static inline int pas_supported(enum pas_id id)
+{
+ return 0;
+}
+#endif
+
#endif
diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c
index b228147..71fb87c 100644
--- a/arch/arm/mach-msm/smd.c
+++ b/arch/arm/mach-msm/smd.c
@@ -35,6 +35,7 @@
#include <linux/wakelock.h>
#include <linux/notifier.h>
#include <linux/sort.h>
+#include <linux/suspend.h>
#include <mach/msm_smd.h>
#include <mach/msm_iomap.h>
#include <mach/system.h>
@@ -553,6 +554,26 @@
}
}
+static int smsm_pm_notifier(struct notifier_block *nb,
+ unsigned long event, void *unused)
+{
+ switch (event) {
+ case PM_SUSPEND_PREPARE:
+ smsm_change_state(SMSM_APPS_STATE, SMSM_PROC_AWAKE, 0);
+ break;
+
+ case PM_POST_SUSPEND:
+ smsm_change_state(SMSM_APPS_STATE, 0, SMSM_PROC_AWAKE);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block smsm_pm_nb = {
+ .notifier_call = smsm_pm_notifier,
+ .priority = 0,
+};
+
void smd_diag(void)
{
char *x;
@@ -2545,6 +2566,12 @@
return i;
wmb();
+
+ smsm_pm_notifier(&smsm_pm_nb, PM_POST_SUSPEND, NULL);
+ i = register_pm_notifier(&smsm_pm_nb);
+ if (i)
+ pr_err("%s: power state notif error %d\n", __func__, i);
+
return 0;
}
@@ -3164,6 +3191,7 @@
flags, "smd_dev", 0);
if (r < 0)
return r;
+ interrupt_stats[SMD_MODEM].smd_interrupt_id = INT_A9_M2A_0;
r = enable_irq_wake(INT_A9_M2A_0);
if (r < 0)
pr_err("smd_core_init: "
@@ -3175,6 +3203,7 @@
free_irq(INT_A9_M2A_0, 0);
return r;
}
+ interrupt_stats[SMD_MODEM].smsm_interrupt_id = INT_A9_M2A_5;
r = enable_irq_wake(INT_A9_M2A_5);
if (r < 0)
pr_err("smd_core_init: "
@@ -3192,6 +3221,7 @@
return r;
}
+ interrupt_stats[SMD_Q6].smd_interrupt_id = INT_ADSP_A11;
r = request_irq(INT_ADSP_A11_SMSM, smsm_dsp_irq_handler,
flags, "smsm_dev", smsm_dsp_irq_handler);
if (r < 0) {
@@ -3201,6 +3231,7 @@
return r;
}
+ interrupt_stats[SMD_Q6].smsm_interrupt_id = INT_ADSP_A11_SMSM;
r = enable_irq_wake(INT_ADSP_A11);
if (r < 0)
pr_err("smd_core_init: "
@@ -3226,6 +3257,7 @@
return r;
}
+ interrupt_stats[SMD_DSPS].smd_interrupt_id = INT_DSPS_A11;
r = enable_irq_wake(INT_DSPS_A11);
if (r < 0)
pr_err("smd_core_init: "
@@ -3244,6 +3276,7 @@
return r;
}
+ interrupt_stats[SMD_WCNSS].smd_interrupt_id = INT_WCNSS_A11;
r = enable_irq_wake(INT_WCNSS_A11);
if (r < 0)
pr_err("smd_core_init: "
@@ -3261,6 +3294,7 @@
return r;
}
+ interrupt_stats[SMD_WCNSS].smsm_interrupt_id = INT_WCNSS_A11_SMSM;
r = enable_irq_wake(INT_WCNSS_A11_SMSM);
if (r < 0)
pr_err("smd_core_init: "
@@ -3281,6 +3315,7 @@
return r;
}
+ interrupt_stats[SMD_DSPS].smsm_interrupt_id = INT_DSPS_A11_SMSM;
r = enable_irq_wake(INT_DSPS_A11_SMSM);
if (r < 0)
pr_err("smd_core_init: "
@@ -3410,6 +3445,8 @@
goto intr_failed;
}
+ interrupt_stats[cfg->irq_config_id].smd_interrupt_id
+ = cfg->smd_int.irq_id;
/* only init smsm structs if this edge supports smsm */
if (cfg->smsm_int.irq_id)
ret = intr_init(
@@ -3425,6 +3462,9 @@
goto intr_failed;
}
+ if (cfg->smsm_int.irq_id)
+ interrupt_stats[cfg->irq_config_id].smsm_interrupt_id
+ = cfg->smsm_int.irq_id;
if (cfg->subsys_name)
strlcpy(edge_to_pids[cfg->edge].subsys_name,
cfg->subsys_name, SMD_MAX_CH_NAME_LEN);
@@ -3513,7 +3553,7 @@
static struct restart_notifier_block restart_notifiers[] = {
{SMD_MODEM, "modem", .nb.notifier_call = restart_notifier_cb},
{SMD_Q6, "lpass", .nb.notifier_call = restart_notifier_cb},
- {SMD_WCNSS, "riva", .nb.notifier_call = restart_notifier_cb},
+ {SMD_WCNSS, "wcnss", .nb.notifier_call = restart_notifier_cb},
{SMD_DSPS, "dsps", .nb.notifier_call = restart_notifier_cb},
{SMD_MODEM, "gss", .nb.notifier_call = restart_notifier_cb},
{SMD_Q6, "adsp", .nb.notifier_call = restart_notifier_cb},
diff --git a/arch/arm/mach-msm/smd_debug.c b/arch/arm/mach-msm/smd_debug.c
index d64bcf2..cc82a01 100644
--- a/arch/arm/mach-msm/smd_debug.c
+++ b/arch/arm/mach-msm/smd_debug.c
@@ -103,22 +103,24 @@
const char *subsys_name;
i += scnprintf(buf + i, max - i,
- " Subsystem | In | Out (Hardcoded) |"
+ " Subsystem | Interrupt ID | In | Out (Hardcoded) |"
" Out (Configured) |\n");
for (subsys = 0; subsys < NUM_SMD_SUBSYSTEMS; ++subsys) {
subsys_name = smd_pid_to_subsystem(subsys);
if (subsys_name) {
i += scnprintf(buf + i, max - i,
- "%-10s %4s | %9u | %9u | %9u |\n",
+ "%-10s %4s | %9d | %9u | %9u | %9u |\n",
smd_pid_to_subsystem(subsys), "smd",
+ stats->smd_interrupt_id,
stats->smd_in_count,
stats->smd_out_hardcode_count,
stats->smd_out_config_count);
i += scnprintf(buf + i, max - i,
- "%-10s %4s | %9u | %9u | %9u |\n",
+ "%-10s %4s | %9d | %9u | %9u | %9u |\n",
smd_pid_to_subsystem(subsys), "smsm",
+ stats->smsm_interrupt_id,
stats->smsm_in_count,
stats->smsm_out_hardcode_count,
stats->smsm_out_config_count);
diff --git a/arch/arm/mach-msm/smd_private.h b/arch/arm/mach-msm/smd_private.h
index 9117280..7f39a24 100644
--- a/arch/arm/mach-msm/smd_private.h
+++ b/arch/arm/mach-msm/smd_private.h
@@ -255,10 +255,12 @@
uint32_t smd_in_count;
uint32_t smd_out_hardcode_count;
uint32_t smd_out_config_count;
+ uint32_t smd_interrupt_id;
uint32_t smsm_in_count;
uint32_t smsm_out_hardcode_count;
uint32_t smsm_out_config_count;
+ uint32_t smsm_interrupt_id;
};
extern struct interrupt_stat interrupt_stats[NUM_SMD_SUBSYSTEMS];
diff --git a/arch/arm/mach-msm/smd_rpcrouter.c b/arch/arm/mach-msm/smd_rpcrouter.c
index 68c3dd3..80a639c 100644
--- a/arch/arm/mach-msm/smd_rpcrouter.c
+++ b/arch/arm/mach-msm/smd_rpcrouter.c
@@ -1,7 +1,7 @@
/* arch/arm/mach-msm/smd_rpcrouter.c
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2007-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2007-2012, The Linux Foundation. All rights reserved.
* Author: San Mehat <san@android.com>
*
* This software is licensed under the terms of the GNU General Public
@@ -40,6 +40,7 @@
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/debugfs.h>
+#include <linux/reboot.h>
#include <asm/byteorder.h>
@@ -158,6 +159,7 @@
static void do_read_data(struct work_struct *work);
static void do_create_pdevs(struct work_struct *work);
static void do_create_rpcrouter_pdev(struct work_struct *work);
+static int msm_rpcrouter_close(void);
static DECLARE_WORK(work_create_pdevs, do_create_pdevs);
static DECLARE_WORK(work_create_rpcrouter_pdev, do_create_rpcrouter_pdev);
@@ -213,6 +215,24 @@
DECLARE_COMPLETION(rpc_remote_router_up);
static atomic_t pending_close_count = ATOMIC_INIT(0);
+static int msm_rpc_reboot_call(struct notifier_block *this,
+ unsigned long code, void *_cmd)
+{
+ switch (code) {
+ case SYS_RESTART:
+ case SYS_HALT:
+ case SYS_POWER_OFF:
+ msm_rpcrouter_close();
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block msm_rpc_reboot_notifier = {
+ .notifier_call = msm_rpc_reboot_call,
+ .priority = 100
+};
+
/*
* Search for transport (xprt) that matches the provided PID.
*
@@ -2124,7 +2144,7 @@
return rc;
}
-int msm_rpcrouter_close(void)
+static int msm_rpcrouter_close(void)
{
struct rpcrouter_xprt_info *xprt_info;
union rr_control_msg ctl;
@@ -2508,7 +2528,9 @@
msm_rpc_connect_timeout_ms = 0;
smd_rpcrouter_debug_mask |= SMEM_LOG;
debugfs_init();
-
+ ret = register_reboot_notifier(&msm_rpc_reboot_notifier);
+ if (ret)
+ pr_err("%s: Failed to register reboot notifier", __func__);
/* Initialize what we need to start processing */
rpcrouter_workqueue =
diff --git a/arch/arm/mach-msm/smd_rpcrouter.h b/arch/arm/mach-msm/smd_rpcrouter.h
index 1da7579..2ad85a4 100644
--- a/arch/arm/mach-msm/smd_rpcrouter.h
+++ b/arch/arm/mach-msm/smd_rpcrouter.h
@@ -1,7 +1,7 @@
/** arch/arm/mach-msm/smd_rpcrouter.h
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2007-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2007-2012, The Linux Foundation. All rights reserved.
* Author: San Mehat <san@android.com>
*
* This software is licensed under the terms of the GNU General Public
@@ -240,7 +240,6 @@
struct rr_fragment **frag,
unsigned len, long timeout);
-int msm_rpcrouter_close(void);
struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev);
int msm_rpcrouter_destroy_local_endpoint(struct msm_rpc_endpoint *ept);
diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c
index ac077e9..0beb952 100644
--- a/arch/arm/mach-msm/socinfo.c
+++ b/arch/arm/mach-msm/socinfo.c
@@ -35,6 +35,8 @@
HW_PLATFORM_LIQUID = 9,
/* Dragonboard platform id is assigned as 10 in CDT */
HW_PLATFORM_DRAGON = 10,
+ HW_PLATFORM_HRD = 13,
+ HW_PLATFORM_DTV = 14,
HW_PLATFORM_INVALID
};
@@ -47,7 +49,9 @@
[HW_PLATFORM_SVLTE_SURF] = "SLVTE_SURF",
[HW_PLATFORM_MTP] = "MTP",
[HW_PLATFORM_LIQUID] = "Liquid",
- [HW_PLATFORM_DRAGON] = "Dragon"
+ [HW_PLATFORM_DRAGON] = "Dragon",
+ [HW_PLATFORM_HRD] = "HRD",
+ [HW_PLATFORM_DTV] = "DTV",
};
enum {
@@ -286,6 +290,9 @@
/* 8092 IDs */
[146] = MSM_CPU_8092,
+ /* 8910 IDs */
+ [147] = MSM_CPU_8910,
+
/* 8064AB IDs */
[153] = MSM_CPU_8064AB,
@@ -732,6 +739,10 @@
dummy_socinfo.id = 146;
strlcpy(dummy_socinfo.build_id, "mpq8092 - ",
sizeof(dummy_socinfo.build_id));
+ } else if (early_machine_is_msm8910()) {
+ dummy_socinfo.id = 147;
+ strlcpy(dummy_socinfo.build_id, "msm8910 - ",
+ sizeof(dummy_socinfo.build_id));
}
strlcat(dummy_socinfo.build_id, "Dummy socinfo",
sizeof(dummy_socinfo.build_id));
diff --git a/arch/arm/mach-msm/spm-v2.c b/arch/arm/mach-msm/spm-v2.c
index 1eab9bf..17997d2 100644
--- a/arch/arm/mach-msm/spm-v2.c
+++ b/arch/arm/mach-msm/spm-v2.c
@@ -84,10 +84,6 @@
[MSM_SPM_REG_SAW2_VERSION] = 0xFD0,
};
-/******************************************************************************
- * Internal helper functions
- *****************************************************************************/
-
static inline uint32_t msm_spm_drv_get_num_spm_entry(
struct msm_spm_driver_data *dev)
{
@@ -150,6 +146,12 @@
{
unsigned int pmic_data = 0;
+ /**
+ * VCTL_PORT has to be 0, for PMIC_STS register to be updated.
+ * Ensure that vctl_port is always set to 0.
+ */
+ WARN_ON(dev->vctl_port);
+
pmic_data |= vlevel;
pmic_data |= (dev->vctl_port & 0x7) << 16;
@@ -179,7 +181,7 @@
}
}
-static inline uint32_t msm_spm_drv_get_sts_curr_pmic_data(
+uint32_t msm_spm_drv_get_sts_curr_pmic_data(
struct msm_spm_driver_data *dev)
{
if (dev->major == SAW2_MAJOR_2) {
@@ -213,10 +215,6 @@
return ret;
}
-/******************************************************************************
- * Public functions
- *****************************************************************************/
-
inline int msm_spm_drv_set_spm_enable(
struct msm_spm_driver_data *dev, bool enable)
{
@@ -381,69 +379,92 @@
goto set_vdd_bail;
}
- /* Set AVS min/max */
- msm_spm_drv_set_avs_vlevel(dev, vlevel);
-
if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL)
- pr_info("%s: done, remaining timeout %uus\n",
+ pr_info("%s: done, remaining timeout %u us\n",
__func__, timeout_us);
+ /* Set AVS min/max */
+ msm_spm_drv_set_avs_vlevel(dev, vlevel);
msm_spm_drv_enable_avs(dev);
+
return 0;
set_vdd_bail:
msm_spm_drv_enable_avs(dev);
- pr_err("%s: failed %#x, remaining timeout %uus, vlevel %#x\n",
+ pr_err("%s: failed %#x, remaining timeout %u us, vlevel %#x\n",
__func__, vlevel, timeout_us, new_level);
return -EIO;
}
-int msm_spm_drv_set_phase(struct msm_spm_driver_data *dev,
- unsigned int phase_cnt)
+static int msm_spm_drv_get_pmic_port(struct msm_spm_driver_data *dev,
+ enum msm_spm_pmic_port port)
+{
+ int index = -1;
+
+ switch (port) {
+ case MSM_SPM_PMIC_VCTL_PORT:
+ index = dev->vctl_port;
+ break;
+ case MSM_SPM_PMIC_PHASE_PORT:
+ index = dev->phase_port;
+ break;
+ case MSM_SPM_PMIC_PFM_PORT:
+ index = dev->pfm_port;
+ break;
+ default:
+ break;
+ }
+
+ return index;
+}
+
+int msm_spm_drv_set_pmic_data(struct msm_spm_driver_data *dev,
+ enum msm_spm_pmic_port port, unsigned int data)
{
unsigned int pmic_data = 0;
unsigned int timeout_us = 0;
+ int index = 0;
if (dev->major != SAW2_MAJOR_2)
return -ENODEV;
- pmic_data |= phase_cnt & 0xFF;
- pmic_data |= (dev->phase_port & 0x7) << 16;
+ if (!msm_spm_pmic_arb_present(dev))
+ return -ENOSYS;
+
+ index = msm_spm_drv_get_pmic_port(dev, port);
+ if (index < 0)
+ return -ENODEV;
+
+ pmic_data |= data & 0xFF;
+ pmic_data |= (index & 0x7) << 16;
dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] &= ~0x700FF;
dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] |= pmic_data;
msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_VCTL);
mb();
- /* Wait for PMIC state to return to idle or until timeout */
timeout_us = dev->vctl_timeout_us;
- while (msm_spm_drv_get_sts_pmic_state(dev) != MSM_SPM_PMIC_STATE_IDLE) {
- if (!timeout_us)
- goto set_phase_bail;
+ /**
+ * Confirm the pmic data set was what hardware sent by
+ * checking the PMIC FSM state.
+ * We cannot use the sts_pmic_data and check it against
+ * the value like we do fot set_vdd, since the PMIC_STS
+ * is only updated for SAW_VCTL sent with port index 0.
+ */
+ do {
+ if (msm_spm_drv_get_sts_pmic_state(dev) ==
+ MSM_SPM_PMIC_STATE_IDLE)
+ break;
+ udelay(1);
+ } while (--timeout_us);
- if (timeout_us > 10) {
- udelay(10);
- timeout_us -= 10;
- } else {
- udelay(timeout_us);
- timeout_us = 0;
- }
+ if (!timeout_us) {
+ pr_err("%s: failed, remaining timeout %u us, data %d\n",
+ __func__, timeout_us, data);
+ return -EIO;
}
- if (msm_spm_drv_get_sts_curr_pmic_data(dev) != phase_cnt)
- goto set_phase_bail;
-
- if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL)
- pr_info("%s: done, remaining timeout %uus\n",
- __func__, timeout_us);
-
return 0;
-
-set_phase_bail:
- pr_err("%s: failed, remaining timeout %uus, phase count %d\n",
- __func__, timeout_us, msm_spm_drv_get_sts_curr_pmic_data(dev));
- return -EIO;
-
}
void msm_spm_drv_reinit(struct msm_spm_driver_data *dev)
@@ -472,6 +493,7 @@
dev->vctl_port = data->vctl_port;
dev->phase_port = data->phase_port;
+ dev->pfm_port = data->pfm_port;
dev->reg_base_addr = data->reg_base_addr;
memcpy(dev->reg_shadow, data->reg_init_values,
sizeof(data->reg_init_values));
diff --git a/arch/arm/mach-msm/spm.h b/arch/arm/mach-msm/spm.h
index e81e335..4cdfcf8 100644
--- a/arch/arm/mach-msm/spm.h
+++ b/arch/arm/mach-msm/spm.h
@@ -112,6 +112,7 @@
uint32_t ver_reg;
uint32_t vctl_port;
uint32_t phase_port;
+ uint32_t pfm_port;
uint8_t awake_vlevel;
uint32_t vctl_timeout_us;
@@ -141,6 +142,13 @@
int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel);
/**
+ * msm_spm_get_vdd(): Get core voltage
+ * @cpu: core id
+ * @return: Returns encoded PMIC data.
+ */
+unsigned int msm_spm_get_vdd(unsigned int cpu);
+
+/**
* msm_spm_turn_on_cpu_rail(): Power on cpu rail before turning on core
* @cpu: core id
*/
@@ -189,6 +197,12 @@
*/
int msm_spm_apcs_set_phase(unsigned int phase_cnt);
+/** msm_spm_enable_fts_lpm() : Enable FTS to switch to low power
+ * when the cores are in low power modes
+ * @mode: The mode configuration for FTS
+ */
+int msm_spm_enable_fts_lpm(uint32_t mode);
+
/* Internal low power management specific functions */
/**
@@ -227,6 +241,11 @@
{
return -ENOSYS;
}
+
+static inline int msm_spm_enable_fts_lpm(uint32_t mode)
+{
+ return -ENOSYS;
+}
#endif /* defined(CONFIG_MSM_L2_SPM) */
#else /* defined(CONFIG_MSM_SPM_V1) || defined(CONFIG_MSM_SPM_V2) */
static inline int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm)
@@ -239,6 +258,11 @@
return -ENOSYS;
}
+static inline unsigned int msm_spm_get_vdd(unsigned int cpu)
+{
+ return 0;
+}
+
static inline void msm_spm_reinit(void)
{
/* empty */
diff --git a/arch/arm/mach-msm/spm_devices.c b/arch/arm/mach-msm/spm_devices.c
index feddde8..3fe3bd7 100644
--- a/arch/arm/mach-msm/spm_devices.c
+++ b/arch/arm/mach-msm/spm_devices.c
@@ -48,7 +48,6 @@
static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_spm_device, msm_cpu_spm_device);
-/* Must be called on the same cpu as the one being set to */
static void msm_spm_smp_set_vdd(void *data)
{
struct msm_spm_device *dev;
@@ -66,15 +65,41 @@
info.cpu = cpu;
info.vlevel = vlevel;
- /* Set to true to block on vdd change */
- ret = smp_call_function_single(cpu, msm_spm_smp_set_vdd, &info, true);
- if (!ret)
+ if (cpu_online(cpu)) {
+ /**
+ * We do not want to set the voltage of another core from
+ * this core, as its possible that we may race the vdd change
+ * with the SPM state machine of that core, which could also
+ * be changing the voltage of that core during power collapse.
+ * Hence, set the function to be executed on that core and block
+ * until the vdd change is complete.
+ */
+ ret = smp_call_function_single(cpu, msm_spm_smp_set_vdd,
+ &info, true);
+ if (!ret)
+ ret = info.err;
+ } else {
+ /**
+ * Since the core is not online, it is safe to set the vdd
+ * directly.
+ */
+ msm_spm_smp_set_vdd(&info);
ret = info.err;
+ }
return ret;
}
EXPORT_SYMBOL(msm_spm_set_vdd);
+unsigned int msm_spm_get_vdd(unsigned int cpu)
+{
+ struct msm_spm_device *dev;
+
+ dev = &per_cpu(msm_cpu_spm_device, cpu);
+ return msm_spm_drv_get_sts_curr_pmic_data(&dev->reg_data);
+}
+EXPORT_SYMBOL(msm_spm_get_vdd);
+
static int msm_spm_dev_set_low_power_mode(struct msm_spm_device *dev,
unsigned int mode, bool notify_rpm)
{
@@ -158,9 +183,8 @@
reg = saw_bases[cpu];
- if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm8930aa() ||
- cpu_is_apq8064() || cpu_is_msm8627() || cpu_is_msm8960ab() ||
- cpu_is_apq8064ab()) {
+ if (soc_class_is_msm8960() || soc_class_is_msm8930() ||
+ soc_class_is_apq8064()) {
val = 0xA4;
reg += 0x14;
timeout = 512;
@@ -235,10 +259,18 @@
int msm_spm_apcs_set_phase(unsigned int phase_cnt)
{
- return msm_spm_drv_set_phase(&msm_spm_l2_device.reg_data, phase_cnt);
+ return msm_spm_drv_set_pmic_data(&msm_spm_l2_device.reg_data,
+ MSM_SPM_PMIC_PHASE_PORT, phase_cnt);
}
EXPORT_SYMBOL(msm_spm_apcs_set_phase);
+int msm_spm_enable_fts_lpm(uint32_t mode)
+{
+ return msm_spm_drv_set_pmic_data(&msm_spm_l2_device.reg_data,
+ MSM_SPM_PMIC_PFM_PORT, mode);
+}
+EXPORT_SYMBOL(msm_spm_enable_fts_lpm);
+
/* Board file init function */
int __init msm_spm_l2_init(struct msm_spm_platform_data *data)
{
@@ -336,25 +368,6 @@
if (!ret)
spm_data.vctl_timeout_us = val;
- /* optional */
- key = "qcom,vctl-port";
- ret = of_property_read_u32(node, key, &val);
- if (!ret)
- spm_data.vctl_port = val;
-
- /* optional */
- key = "qcom,phase-port";
- ret = of_property_read_u32(node, key, &val);
- if (!ret)
- spm_data.phase_port = val;
-
- for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
- ret = of_property_read_u32(node, spm_of_data[i].key, &val);
- if (ret)
- continue;
- spm_data.reg_init_values[spm_of_data[i].id] = val;
- }
-
/*
* Device with id 0..NR_CPUS are SPM for apps cores
* Device with id 0xFFFF is for L2 SPM.
@@ -370,6 +383,35 @@
dev = &msm_spm_l2_device;
}
+ spm_data.vctl_port = -1;
+ spm_data.phase_port = -1;
+ spm_data.pfm_port = -1;
+
+ /* optional */
+ if (dev == &msm_spm_l2_device) {
+ key = "qcom,vctl-port";
+ ret = of_property_read_u32(node, key, &val);
+ if (!ret)
+ spm_data.vctl_port = val;
+
+ key = "qcom,phase-port";
+ ret = of_property_read_u32(node, key, &val);
+ if (!ret)
+ spm_data.phase_port = val;
+
+ key = "qcom,pfm-port";
+ ret = of_property_read_u32(node, key, &val);
+ if (!ret)
+ spm_data.pfm_port = val;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
+ ret = of_property_read_u32(node, spm_of_data[i].key, &val);
+ if (ret)
+ continue;
+ spm_data.reg_init_values[spm_of_data[i].id] = val;
+ }
+
for (i = 0; i < num_modes; i++) {
key = mode_of_data[i].key;
modes[mode_count].cmd =
diff --git a/arch/arm/mach-msm/spm_driver.h b/arch/arm/mach-msm/spm_driver.h
index f272adb..1beaffb 100644
--- a/arch/arm/mach-msm/spm_driver.h
+++ b/arch/arm/mach-msm/spm_driver.h
@@ -14,12 +14,19 @@
#include "spm.h"
+enum msm_spm_pmic_port {
+ MSM_SPM_PMIC_VCTL_PORT,
+ MSM_SPM_PMIC_PHASE_PORT,
+ MSM_SPM_PMIC_PFM_PORT,
+};
+
struct msm_spm_driver_data {
uint32_t major;
uint32_t minor;
uint32_t ver_reg;
uint32_t vctl_port;
uint32_t phase_port;
+ uint32_t pfm_port;
void __iomem *reg_base_addr;
uint32_t vctl_timeout_us;
uint32_t avs_timeout_us;
@@ -35,11 +42,13 @@
uint32_t addr);
int msm_spm_drv_set_vdd(struct msm_spm_driver_data *dev,
unsigned int vlevel);
+uint32_t msm_spm_drv_get_sts_curr_pmic_data(
+ struct msm_spm_driver_data *dev);
int msm_spm_drv_write_seq_data(struct msm_spm_driver_data *dev,
uint8_t *cmd, uint32_t *offset);
void msm_spm_drv_flush_seq_entry(struct msm_spm_driver_data *dev);
int msm_spm_drv_set_spm_enable(struct msm_spm_driver_data *dev,
bool enable);
-int msm_spm_drv_set_phase(struct msm_spm_driver_data *dev,
- unsigned int phase_cnt);
+int msm_spm_drv_set_pmic_data(struct msm_spm_driver_data *dev,
+ enum msm_spm_pmic_port port, unsigned int data);
#endif
diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c
index b361d9d..b61604a 100644
--- a/arch/arm/mach-msm/timer.c
+++ b/arch/arm/mach-msm/timer.c
@@ -964,9 +964,8 @@
if (!smp_processor_id())
return 0;
- if (cpu_is_msm8x60() || cpu_is_msm8960() || cpu_is_apq8064() ||
- cpu_is_msm8930() || cpu_is_msm8930aa() || cpu_is_msm8627() ||
- cpu_is_msm8960ab() || cpu_is_apq8064ab())
+ if (cpu_is_msm8x60() || soc_class_is_msm8960() ||
+ soc_class_is_apq8064() || soc_class_is_msm8930())
__raw_writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL);
if (__get_cpu_var(first_boot)) {
@@ -1062,9 +1061,8 @@
sclk_hz = 32765;
gpt->flags |= MSM_CLOCK_FLAGS_UNSTABLE_COUNT;
dgt->flags |= MSM_CLOCK_FLAGS_UNSTABLE_COUNT;
- } else if (cpu_is_msm8960() || cpu_is_apq8064() || cpu_is_msm8930() ||
- cpu_is_msm8930aa() || cpu_is_msm8627() ||
- cpu_is_msm8960ab() || cpu_is_apq8064ab()) {
+ } else if (soc_class_is_msm8960() || soc_class_is_apq8064() ||
+ soc_class_is_msm8930()) {
global_timer_offset = MSM_TMR0_BASE - MSM_TMR_BASE;
dgt->freq = 6750000;
__raw_writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL);
@@ -1073,8 +1071,7 @@
gpt->freq = 32765;
gpt_hz = 32765;
sclk_hz = 32765;
- if (!cpu_is_msm8930() && !cpu_is_msm8930aa() &&
- !cpu_is_msm8627() && !cpu_is_msm8960ab()) {
+ if (!soc_class_is_msm8930() && !cpu_is_msm8960ab()) {
gpt->flags |= MSM_CLOCK_FLAGS_UNSTABLE_COUNT;
dgt->flags |= MSM_CLOCK_FLAGS_UNSTABLE_COUNT;
}
@@ -1124,10 +1121,9 @@
"failed for %s\n", cs->name);
ce->irq = clock->irq;
- if (cpu_is_msm8x60() || cpu_is_msm8960() || cpu_is_apq8064() ||
- cpu_is_msm8930() || cpu_is_msm9615() || cpu_is_msm8625() ||
- cpu_is_msm8627() || cpu_is_msm8930aa() ||
- cpu_is_msm8960ab() || cpu_is_apq8064ab()) {
+ if (cpu_is_msm8x60() || cpu_is_msm9615() || cpu_is_msm8625() ||
+ soc_class_is_msm8960() || soc_class_is_apq8064() ||
+ soc_class_is_msm8930()) {
clock->percpu_evt = alloc_percpu(struct clock_event_device *);
if (!clock->percpu_evt) {
pr_err("msm_timer_init: memory allocation "
diff --git a/arch/arm/mach-msm/tz_log.c b/arch/arm/mach-msm/tz_log.c
index db797cd..5a7ec69 100644
--- a/arch/arm/mach-msm/tz_log.c
+++ b/arch/arm/mach-msm/tz_log.c
@@ -496,17 +496,23 @@
*/
tzdiag_phy_iobase = readl_relaxed(virt_iobase);
- /*
- * Map the 4KB diagnostic information area
- */
- tzdbg.virt_iobase = devm_ioremap_nocache(&pdev->dev,
- tzdiag_phy_iobase, DEBUG_MAX_RW_BUF);
+ if (!pdev->dev.of_node) {
- if (!tzdbg.virt_iobase) {
- dev_err(&pdev->dev,
- "%s: ERROR could not ioremap: start=%p, len=%u\n",
- __func__, (void *) tzdiag_phy_iobase, DEBUG_MAX_RW_BUF);
- return -ENXIO;
+ /*
+ * Map the 4KB diagnostic information area
+ */
+ tzdbg.virt_iobase = devm_ioremap_nocache(&pdev->dev,
+ tzdiag_phy_iobase, DEBUG_MAX_RW_BUF);
+
+ if (!tzdbg.virt_iobase) {
+ dev_err(&pdev->dev,
+ "%s: ERROR could not ioremap: start=%p, len=%u\n",
+ __func__, (void *) tzdiag_phy_iobase,
+ DEBUG_MAX_RW_BUF);
+ return -ENXIO;
+ }
+ } else {
+ tzdbg.virt_iobase = virt_iobase;
}
ptr = kzalloc(DEBUG_MAX_RW_BUF, GFP_KERNEL);
diff --git a/arch/arm/mach-msm/wcnss-ssr-8960.c b/arch/arm/mach-msm/wcnss-ssr-8960.c
deleted file mode 100644
index f014df9..0000000
--- a/arch/arm/mach-msm/wcnss-ssr-8960.c
+++ /dev/null
@@ -1,277 +0,0 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/reboot.h>
-#include <linux/workqueue.h>
-#include <linux/io.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/wcnss_wlan.h>
-#include <linux/err.h>
-#include <asm/mach-types.h>
-#include <mach/irqs.h>
-#include <mach/scm.h>
-#include <mach/subsystem_restart.h>
-#include <mach/subsystem_notif.h>
-#include <mach/peripheral-loader.h>
-#include "smd_private.h"
-#include "ramdump.h"
-
-#define MODULE_NAME "wcnss_8960"
-#define MAX_BUF_SIZE 0x51
-
-
-
-static struct delayed_work cancel_vote_work;
-static void *riva_ramdump_dev;
-static int riva_crash;
-static int ss_restart_inprogress;
-static int enable_riva_ssr;
-static struct subsys_device *riva_8960_dev;
-
-static void smsm_state_cb_hdlr(void *data, uint32_t old_state,
- uint32_t new_state)
-{
- char *smem_reset_reason;
- char buffer[MAX_BUF_SIZE];
- unsigned smem_reset_size;
- unsigned size;
-
- riva_crash = true;
-
- pr_err("%s: smsm state changed\n", MODULE_NAME);
-
- if (!(new_state & SMSM_RESET))
- return;
-
- if (ss_restart_inprogress) {
- pr_err("%s: Ignoring smsm reset req, restart in progress\n",
- MODULE_NAME);
- return;
- }
-
- if (!enable_riva_ssr)
- panic(MODULE_NAME ": SMSM reset request received from Riva");
-
- smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_WCNSS0,
- &smem_reset_size);
-
- if (!smem_reset_reason || !smem_reset_size) {
- pr_err("%s: wcnss subsystem failure reason: %s\n",
- __func__, "(unknown, smem_get_entry failed)");
- } else if (!smem_reset_reason[0]) {
- pr_err("%s: wcnss subsystem failure reason: %s\n",
- __func__, "(unknown, init string found)");
- } else {
- size = smem_reset_size < MAX_BUF_SIZE ? smem_reset_size :
- (MAX_BUF_SIZE - 1);
- memcpy(buffer, smem_reset_reason, size);
- buffer[size] = '\0';
- pr_err("%s: wcnss subsystem failure reason: %s\n",
- __func__, buffer);
- memset(smem_reset_reason, 0, smem_reset_size);
- wmb();
- }
-
- ss_restart_inprogress = true;
- subsystem_restart_dev(riva_8960_dev);
-}
-
-static irqreturn_t riva_wdog_bite_irq_hdlr(int irq, void *dev_id)
-{
- riva_crash = true;
-
- if (ss_restart_inprogress) {
- pr_err("%s: Ignoring riva bite irq, restart in progress\n",
- MODULE_NAME);
- return IRQ_HANDLED;
- }
-
- if (!enable_riva_ssr)
- panic(MODULE_NAME ": Watchdog bite received from Riva");
-
- ss_restart_inprogress = true;
- subsystem_restart_dev(riva_8960_dev);
-
- return IRQ_HANDLED;
-}
-
-/* SMSM reset Riva */
-static void smsm_riva_reset(void)
-{
- /* per SS reset request bit is not available now,
- * all SS host modules are setting this bit
- * This is still under discussion*/
- smsm_change_state(SMSM_APPS_STATE, SMSM_RESET, SMSM_RESET);
-}
-
-static void riva_post_bootup(struct work_struct *work)
-{
- struct platform_device *pdev = wcnss_get_platform_device();
- struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();
-
- pr_debug(MODULE_NAME ": Cancel APPS vote for Iris & Riva\n");
-
- wcnss_wlan_power(&pdev->dev, pwlanconfig,
- WCNSS_WLAN_SWITCH_OFF);
-}
-
-/* Subsystem handlers */
-static int riva_shutdown(const struct subsys_desc *subsys)
-{
- pil_force_shutdown("wcnss");
- flush_delayed_work(&cancel_vote_work);
- wcnss_flush_delayed_boot_votes();
- disable_irq_nosync(RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ);
-
- return 0;
-}
-
-static int riva_powerup(const struct subsys_desc *subsys)
-{
- struct platform_device *pdev = wcnss_get_platform_device();
- struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();
- int ret = -1;
-
- if (pdev && pwlanconfig)
- ret = wcnss_wlan_power(&pdev->dev, pwlanconfig,
- WCNSS_WLAN_SWITCH_ON);
- /* delay PIL operation, this SSR may be happening soon after kernel
- * resumes because of a SMSM RESET by Riva when APPS was suspended.
- * PIL fails to locate the images without this delay */
- if (!ret) {
- msleep(1000);
- pil_force_boot("wcnss");
- }
- ss_restart_inprogress = false;
- enable_irq(RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ);
- schedule_delayed_work(&cancel_vote_work, msecs_to_jiffies(5000));
-
- return ret;
-}
-
-/* 5MB RAM segments for Riva SS */
-static struct ramdump_segment riva_segments[] = {{0x8f200000,
- 0x8f700000 - 0x8f200000} };
-
-static int riva_ramdump(int enable, const struct subsys_desc *subsys)
-{
- pr_debug("%s: enable[%d]\n", MODULE_NAME, enable);
- if (enable)
- return do_ramdump(riva_ramdump_dev,
- riva_segments,
- ARRAY_SIZE(riva_segments));
- else
- return 0;
-}
-
-/* Riva crash handler */
-static void riva_crash_shutdown(const struct subsys_desc *subsys)
-{
- pr_err("%s: crash shutdown : %d\n", MODULE_NAME, riva_crash);
- if (riva_crash != true)
- smsm_riva_reset();
-}
-
-static struct subsys_desc riva_8960 = {
- .name = "riva",
- .shutdown = riva_shutdown,
- .powerup = riva_powerup,
- .ramdump = riva_ramdump,
- .crash_shutdown = riva_crash_shutdown
-};
-
-static int enable_riva_ssr_set(const char *val, struct kernel_param *kp)
-{
- int ret;
-
- ret = param_set_int(val, kp);
- if (ret)
- return ret;
-
- if (enable_riva_ssr)
- pr_info(MODULE_NAME ": Subsystem restart activated for riva.\n");
-
- return 0;
-}
-
-module_param_call(enable_riva_ssr, enable_riva_ssr_set, param_get_int,
- &enable_riva_ssr, S_IRUGO | S_IWUSR);
-
-static int __init riva_restart_init(void)
-{
- riva_8960_dev = subsys_register(&riva_8960);
- if (IS_ERR(riva_8960_dev))
- return PTR_ERR(riva_8960_dev);
- return 0;
-}
-
-static int __init riva_ssr_module_init(void)
-{
- int ret;
-
- if (machine_is_mpq8064_hrd()) {
- pr_err("Riva not supported on this target\n");
- return 0;
- }
-
- ret = smsm_state_cb_register(SMSM_WCNSS_STATE, SMSM_RESET,
- smsm_state_cb_hdlr, 0);
- if (ret < 0) {
- pr_err("%s: Unable to register smsm callback for Riva Reset! %d\n",
- MODULE_NAME, ret);
- goto out;
- }
- ret = request_irq(RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ,
- riva_wdog_bite_irq_hdlr, IRQF_TRIGGER_HIGH,
- "riva_wdog", NULL);
-
- if (ret < 0) {
- pr_err("%s: Unable to register for Riva bite interrupt (%d)\n",
- MODULE_NAME, ret);
- goto out;
- }
- ret = riva_restart_init();
- if (ret < 0) {
- pr_err("%s: Unable to register with ssr. (%d)\n",
- MODULE_NAME, ret);
- goto out;
- }
- riva_ramdump_dev = create_ramdump_device("riva");
- if (!riva_ramdump_dev) {
- pr_err("%s: Unable to create ramdump device.\n",
- MODULE_NAME);
- ret = -ENOMEM;
- goto out;
- }
- INIT_DELAYED_WORK(&cancel_vote_work, riva_post_bootup);
-
- pr_info("%s: module initialized\n", MODULE_NAME);
-out:
- return ret;
-}
-
-static void __exit riva_ssr_module_exit(void)
-{
- if (machine_is_mpq8064_hrd())
- return;
- subsys_unregister(riva_8960_dev);
- free_irq(RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ, NULL);
-}
-
-module_init(riva_ssr_module_init);
-module_exit(riva_ssr_module_exit);
-
-MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/wcnss-ssr-8974.c b/arch/arm/mach-msm/wcnss-ssr-8974.c
deleted file mode 100644
index b837efc..0000000
--- a/arch/arm/mach-msm/wcnss-ssr-8974.c
+++ /dev/null
@@ -1,163 +0,0 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/err.h>
-
-#include <mach/subsystem_restart.h>
-#include <mach/msm_smsm.h>
-
-#define MODULE_NAME "wcnss_8974"
-#define MAX_SSR_REASON_LEN 0x51
-
-static int ss_restart_inprogress;
-static int wcnss_crash;
-static struct subsys_device *wcnss_ssr_dev;
-
-#define WCNSS_APSS_WDOG_BITE_RESET_RDY_IRQ 181
-
-static void log_wcnss_sfr(void)
-{
- char *smem_reset_reason;
- char buffer[MAX_SSR_REASON_LEN];
- unsigned smem_reset_size;
- unsigned size;
-
- smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_WCNSS0,
- &smem_reset_size);
-
- if (!smem_reset_reason || !smem_reset_size) {
- pr_err("%s: wcnss subsystem failure reason: %s\n",
- __func__, "(unknown, smem_get_entry failed)");
- } else if (!smem_reset_reason[0]) {
- pr_err("%s: wcnss subsystem failure reason: %s\n",
- __func__, "(unknown, init string found)");
- } else {
- size = smem_reset_size < MAX_SSR_REASON_LEN ? smem_reset_size :
- (MAX_SSR_REASON_LEN - 1);
- memcpy(buffer, smem_reset_reason, size);
- buffer[size] = '\0';
- pr_err("%s: wcnss subsystem failure reason: %s\n",
- __func__, buffer);
- memset(smem_reset_reason, 0, smem_reset_size);
- wmb();
- }
-}
-
-static void restart_wcnss(void)
-{
- log_wcnss_sfr();
- subsystem_restart("wcnss");
-}
-
-static void smsm_state_cb_hdlr(void *data, uint32_t old_state,
- uint32_t new_state)
-{
- wcnss_crash = true;
-
- pr_err("%s: smsm state changed\n", MODULE_NAME);
-
- if (!(new_state & SMSM_RESET))
- return;
-
- if (ss_restart_inprogress) {
- pr_err("%s: Ignoring smsm reset req, restart in progress\n",
- MODULE_NAME);
- return;
- }
-
- ss_restart_inprogress = true;
- restart_wcnss();
-}
-
-
-static irqreturn_t wcnss_wdog_bite_irq_hdlr(int irq, void *dev_id)
-{
- wcnss_crash = true;
-
- if (ss_restart_inprogress) {
- pr_err("%s: Ignoring wcnss bite irq, restart in progress\n",
- MODULE_NAME);
- return IRQ_HANDLED;
- }
-
- ss_restart_inprogress = true;
- restart_wcnss();
-
- return IRQ_HANDLED;
-}
-
-
-static int wcnss_shutdown(const struct subsys_desc *subsys)
-{
- return 0;
-}
-
-static int wcnss_powerup(const struct subsys_desc *subsys)
-{
- return 0;
-}
-
-/* wcnss crash handler */
-static void wcnss_crash_shutdown(const struct subsys_desc *subsys)
-{
- pr_err("%s: crash shutdown : %d\n", MODULE_NAME, wcnss_crash);
- if (wcnss_crash != true)
- smsm_change_state(SMSM_APPS_STATE, SMSM_RESET, SMSM_RESET);
-}
-
-static int wcnss_ramdump(int enable,
- const struct subsys_desc *crashed_subsys)
-{
- return 0;
-}
-
-static struct subsys_desc wcnss_ssr = {
- .name = "wcnss",
- .shutdown = wcnss_shutdown,
- .powerup = wcnss_powerup,
- .ramdump = wcnss_ramdump,
- .crash_shutdown = wcnss_crash_shutdown
-};
-
-static int __init wcnss_ssr_init(void)
-{
- int ret;
-
- ret = smsm_state_cb_register(SMSM_WCNSS_STATE, SMSM_RESET,
- smsm_state_cb_hdlr, 0);
- if (ret < 0) {
- pr_err("%s: Unable to register smsm callback for wcnss Reset! %d\n",
- MODULE_NAME, ret);
- goto out;
- }
- ret = request_irq(WCNSS_APSS_WDOG_BITE_RESET_RDY_IRQ,
- wcnss_wdog_bite_irq_hdlr, IRQF_TRIGGER_HIGH,
- "wcnss_wdog", NULL);
-
- if (ret < 0) {
- pr_err("%s: Unable to register for wcnss bite interrupt (%d)\n",
- MODULE_NAME, ret);
- goto out;
- }
- wcnss_ssr_dev = subsys_register(&wcnss_ssr);
- if (IS_ERR(wcnss_ssr_dev))
- return PTR_ERR(wcnss_ssr_dev);
-
- pr_info("%s: module initialized\n", MODULE_NAME);
-out:
- return ret;
-}
-
-arch_initcall(wcnss_ssr_init);
diff --git a/arch/arm/mach-msm/wdog_debug.c b/arch/arm/mach-msm/wdog_debug.c
index 82800cf..08dd9ce 100644
--- a/arch/arm/mach-msm/wdog_debug.c
+++ b/arch/arm/mach-msm/wdog_debug.c
@@ -110,7 +110,6 @@
goto err;
wdog_data->dev = &pdev->dev;
platform_set_drvdata(pdev, wdog_data);
- msm_enable_wdog_debug();
return 0;
err:
kzfree(wdog_data);
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index cb245ee..e351eb0 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -880,6 +880,16 @@
This option enables optimisations for the PL310 cache
controller.
+config CACHE_PL310_ERP
+ tristate "PL310 CACHE Error Reporting"
+ depends on CACHE_PL310
+ help
+ Say 'Y' here to enable reporting of external L2 cache errors.
+ This feature can be used as a system debugging technique if cache
+ corruption is suspected.
+ Cache error statistics will also be reported in sysfs
+ /sys/devices/platform/pl310_erp/cache_erp
+
config CACHE_TAUROS2
bool "Enable the Tauros2 L2 cache controller"
depends on (ARCH_DOVE || ARCH_MMP || CPU_PJ4)
diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile
index 1c415af..6314e94 100644
--- a/arch/arm/mm/Makefile
+++ b/arch/arm/mm/Makefile
@@ -100,6 +100,7 @@
obj-$(CONFIG_CACHE_FEROCEON_L2) += cache-feroceon-l2.o
obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o
+obj-$(CONFIG_CACHE_PL310_ERP) += cache-pl310-erp.o
obj-$(CONFIG_CACHE_XSC3L2) += cache-xsc3l2.o
obj-$(CONFIG_CACHE_TAUROS2) += cache-tauros2.o
obj-$(CONFIG_VCM) += vcm.o vcm_alloc.o
diff --git a/arch/arm/mm/cache-pl310-erp.c b/arch/arm/mm/cache-pl310-erp.c
new file mode 100644
index 0000000..ad75143
--- /dev/null
+++ b/arch/arm/mm/cache-pl310-erp.c
@@ -0,0 +1,283 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/cpu.h>
+#include <linux/io.h>
+#include <asm/cputype.h>
+#include <asm/hardware/cache-l2x0.h>
+
+#define MODULE_NAME "pl310_erp"
+
+struct pl310_drv_data {
+ unsigned int irq;
+ unsigned int ecntr;
+ unsigned int parrt;
+ unsigned int parrd;
+ unsigned int errwd;
+ unsigned int errwt;
+ unsigned int errrt;
+ unsigned int errrd;
+ unsigned int slverr;
+ unsigned int decerr;
+ void __iomem *base;
+};
+
+#define ECNTR BIT(0)
+#define PARRT BIT(1)
+#define PARRD BIT(2)
+#define ERRWT BIT(3)
+#define ERRWD BIT(4)
+#define ERRRT BIT(5)
+#define ERRRD BIT(6)
+#define SLVERR BIT(7)
+#define DECERR BIT(8)
+
+static irqreturn_t pl310_erp_irq(int irq, void *dev_id)
+{
+ struct pl310_drv_data *p = platform_get_drvdata(dev_id);
+ uint16_t mask_int_stat, int_clear = 0, error = 0;
+
+ mask_int_stat = readl_relaxed(p->base + L2X0_MASKED_INTR_STAT);
+
+ if (mask_int_stat & ECNTR) {
+ pr_alert("Event Counter1/0 Overflow Increment error\n");
+ p->ecntr++;
+ int_clear = mask_int_stat & ECNTR;
+ }
+
+ if (mask_int_stat & PARRT) {
+ pr_alert("Read parity error on L2 Tag RAM\n");
+ p->parrt++;
+ error = 1;
+ int_clear = mask_int_stat & PARRT;
+ }
+
+ if (mask_int_stat & PARRD) {
+ pr_alert("Read parity error on L2 Tag RAM\n");
+ p->parrd++;
+ error = 1;
+ int_clear = mask_int_stat & PARRD;
+ }
+
+ if (mask_int_stat & ERRWT) {
+ pr_alert("Write error on L2 Tag RAM\n");
+ p->errwt++;
+ int_clear = mask_int_stat & ERRWT;
+ }
+
+ if (mask_int_stat & ERRWD) {
+ pr_alert("Write error on L2 Data RAM\n");
+ p->errwd++;
+ int_clear = mask_int_stat & ERRWD;
+ }
+
+ if (mask_int_stat & ERRRT) {
+ pr_alert("Read error on L2 Tag RAM\n");
+ p->errrt++;
+ int_clear = mask_int_stat & ERRRT;
+ }
+
+ if (mask_int_stat & ERRRD) {
+ pr_alert("Read error on L2 Data RAM\n");
+ p->errrd++;
+ int_clear = mask_int_stat & ERRRD;
+ }
+
+ if (mask_int_stat & DECERR) {
+ pr_alert("L2 master port decode error\n");
+ p->decerr++;
+ int_clear = mask_int_stat & DECERR;
+ }
+
+ if (mask_int_stat & SLVERR) {
+ pr_alert("L2 slave port error\n");
+ p->slverr++;
+ int_clear = mask_int_stat & SLVERR;
+ }
+
+ writel_relaxed(int_clear, p->base + L2X0_INTR_CLEAR);
+
+ /* Make sure the interrupts are cleared */
+ mb();
+
+ /* WARNING will be thrown whenever we receive any L2 interrupt.
+ * Other than parity on tag/data ram, irrespective of the bits
+ * set we will throw a warning.
+ */
+ WARN_ON(!error);
+
+ /* Panic in case we encounter parity error in TAG/DATA Ram */
+ BUG_ON(error);
+
+ return IRQ_HANDLED;
+}
+
+static void pl310_mask_int(struct pl310_drv_data *p, bool enable)
+{
+ uint16_t mask;
+
+ if (enable)
+ mask = 0x1FF;
+ else
+ mask = 0x0;
+
+ writel_relaxed(mask, p->base + L2X0_INTR_MASK);
+
+ /* Make sure Mask is updated */
+ mb();
+
+ pr_debug("Mask interrupt %x\n",
+ readl_relaxed(p->base + L2X0_INTR_MASK));
+}
+
+static int pl310_erp_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct pl310_drv_data *p = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE,
+ "L2CC Interrupt Number:\t\t\t%d\n"\
+ "Event Counter1/0 Overflow Increment:\t%u\n"\
+ "Parity Error on L2 Tag RAM (Read):\t%u\n"\
+ "Parity Error on L2 Data RAM (Read):\t%u\n"\
+ "Error on L2 Tag RAM (Write):\t\t%u\n"\
+ "Error on L2 Data RAM (Write):\t\t%u\n"\
+ "Error on L2 Tag RAM (Read):\t\t%u\n"\
+ "Error on L2 Data RAM (Read):\t\t%u\n"\
+ "SLave Error from L3 Port:\t\t%u\n"\
+ "Decode Error from L3 Port:\t\t%u\n",
+ p->irq, p->ecntr, p->parrt, p->parrd, p->errwt, p->errwd,
+ p->errrt, p->errrd, p->slverr, p->decerr);
+}
+
+static DEVICE_ATTR(cache_erp, 0664, pl310_erp_show, NULL);
+
+static int __init pl310_create_sysfs(struct device *dev)
+{
+ /* create a sysfs entry at
+ * /sys/devices/platform/pl310_erp/cache_erp
+ */
+ return device_create_file(dev, &dev_attr_cache_erp);
+}
+
+static int __devinit pl310_cache_erp_probe(struct platform_device *pdev)
+{
+ struct resource *r;
+ struct pl310_drv_data *drv_data;
+ int ret;
+
+ drv_data = devm_kzalloc(&pdev->dev, sizeof(struct pl310_drv_data),
+ GFP_KERNEL);
+ if (drv_data == NULL) {
+ dev_err(&pdev->dev, "cannot allocate memory\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "No L2 base address\n");
+ ret = -ENODEV;
+ goto error;
+ }
+
+ if (!devm_request_mem_region(&pdev->dev, r->start, resource_size(r),
+ "erp")) {
+ ret = -EBUSY;
+ goto error;
+ }
+
+ drv_data->base = devm_ioremap_nocache(&pdev->dev, r->start,
+ resource_size(r));
+ if (!drv_data->base) {
+ dev_err(&pdev->dev, "errored to ioremap 0x%x\n", r->start);
+ ret = -ENOMEM;
+ goto error;
+ }
+ dev_dbg(&pdev->dev, "L2CC base 0x%p\n", drv_data->base);
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "l2_irq");
+ if (!r) {
+ dev_err(&pdev->dev, "No L2 IRQ resource\n");
+ ret = -ENODEV;
+ goto error;
+ }
+
+ drv_data->irq = r->start;
+
+ ret = devm_request_irq(&pdev->dev, drv_data->irq, pl310_erp_irq,
+ IRQF_TRIGGER_RISING, "l2cc_intr", pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "request irq for L2 interrupt failed\n");
+ goto error;
+ }
+
+ platform_set_drvdata(pdev, drv_data);
+
+ pl310_mask_int(drv_data, true);
+
+ ret = pl310_create_sysfs(&pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to create sysfs entry\n");
+ goto sysfs_err;
+ }
+
+ return 0;
+
+sysfs_err:
+ platform_set_drvdata(pdev, NULL);
+ pl310_mask_int(drv_data, false);
+error:
+ return ret;
+}
+
+static int __devexit pl310_cache_erp_remove(struct platform_device *pdev)
+{
+ struct pl310_drv_data *p = platform_get_drvdata(pdev);
+
+ pl310_mask_int(p, false);
+
+ device_remove_file(&pdev->dev, &dev_attr_cache_erp);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver pl310_cache_erp_driver = {
+ .probe = pl310_cache_erp_probe,
+ .remove = __devexit_p(pl310_cache_erp_remove),
+ .driver = {
+ .name = MODULE_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pl310_cache_erp_init(void)
+{
+ return platform_driver_register(&pl310_cache_erp_driver);
+}
+module_init(pl310_cache_erp_init);
+
+static void __exit pl310_cache_erp_exit(void)
+{
+ platform_driver_unregister(&pl310_cache_erp_driver);
+}
+module_exit(pl310_cache_erp_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PL310 cache error reporting driver");
diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types
index d283705..2b31f47 100644
--- a/arch/arm/tools/mach-types
+++ b/arch/arm/tools/mach-types
@@ -1196,3 +1196,4 @@
msm8625_qrd7 MACH_MSM8625_QRD7 MSM8625_QRD7 4095
msm8625_ffa MACH_MSM8625_FFA MSM8625_FFA 4166
msm8625_evt MACH_MSM8625_EVT MSM8625_EVT 4193
+qrd_skud_prime MACH_QRD_SKUD_PRIME QRD_SKUD_PRIME 4393
diff --git a/block/row-iosched.c b/block/row-iosched.c
index f24437c..b7965c6 100644
--- a/block/row-iosched.c
+++ b/block/row-iosched.c
@@ -387,12 +387,19 @@
if (list_empty(&rd->row_queues[currq].rqueue.fifo)) {
/* check idling */
if (delayed_work_pending(&rd->read_idle.idle_work)) {
- row_log_rowq(rd, currq,
- "Delayed work pending. Exiting");
- goto done;
+ if (force) {
+ (void)cancel_delayed_work(
+ &rd->read_idle.idle_work);
+ row_log_rowq(rd, currq,
+ "Canceled delayed work - forced dispatch");
+ } else {
+ row_log_rowq(rd, currq,
+ "Delayed work pending. Exiting");
+ goto done;
+ }
}
- if (queue_idling_enabled[currq] &&
+ if (!force && queue_idling_enabled[currq] &&
rd->row_queues[currq].rqueue.idle_data.begin_idling) {
if (!queue_delayed_work(rd->read_idle.idle_workqueue,
&rd->read_idle.idle_work,
@@ -603,29 +610,27 @@
return ret; \
}
STORE_FUNCTION(row_hp_read_quantum_store,
- &rowd->row_queues[ROWQ_PRIO_HIGH_READ].disp_quantum, 0,
- INT_MAX, 0);
+&rowd->row_queues[ROWQ_PRIO_HIGH_READ].disp_quantum, 1, INT_MAX, 0);
STORE_FUNCTION(row_rp_read_quantum_store,
- &rowd->row_queues[ROWQ_PRIO_REG_READ].disp_quantum, 0,
- INT_MAX, 0);
+ &rowd->row_queues[ROWQ_PRIO_REG_READ].disp_quantum,
+ 1, INT_MAX, 0);
STORE_FUNCTION(row_hp_swrite_quantum_store,
- &rowd->row_queues[ROWQ_PRIO_HIGH_SWRITE].disp_quantum, 0,
- INT_MAX, 0);
+ &rowd->row_queues[ROWQ_PRIO_HIGH_SWRITE].disp_quantum,
+ 1, INT_MAX, 0);
STORE_FUNCTION(row_rp_swrite_quantum_store,
- &rowd->row_queues[ROWQ_PRIO_REG_SWRITE].disp_quantum, 0,
- INT_MAX, 0);
+ &rowd->row_queues[ROWQ_PRIO_REG_SWRITE].disp_quantum,
+ 1, INT_MAX, 0);
STORE_FUNCTION(row_rp_write_quantum_store,
- &rowd->row_queues[ROWQ_PRIO_REG_WRITE].disp_quantum, 0,
- INT_MAX, 0);
+ &rowd->row_queues[ROWQ_PRIO_REG_WRITE].disp_quantum,
+ 1, INT_MAX, 0);
STORE_FUNCTION(row_lp_read_quantum_store,
- &rowd->row_queues[ROWQ_PRIO_LOW_READ].disp_quantum, 0,
- INT_MAX, 0);
+ &rowd->row_queues[ROWQ_PRIO_LOW_READ].disp_quantum,
+ 1, INT_MAX, 0);
STORE_FUNCTION(row_lp_swrite_quantum_store,
- &rowd->row_queues[ROWQ_PRIO_LOW_SWRITE].disp_quantum, 0,
- INT_MAX, 1);
+ &rowd->row_queues[ROWQ_PRIO_LOW_SWRITE].disp_quantum,
+ 1, INT_MAX, 1);
STORE_FUNCTION(row_read_idle_store, &rowd->read_idle.idle_time, 1, INT_MAX, 1);
-STORE_FUNCTION(row_read_idle_freq_store, &rowd->read_idle.freq,
- 1, INT_MAX, 1);
+STORE_FUNCTION(row_read_idle_freq_store, &rowd->read_idle.freq, 1, INT_MAX, 1);
#undef STORE_FUNCTION
diff --git a/drivers/bluetooth/hci_ibs.c b/drivers/bluetooth/hci_ibs.c
index 6253605..acff5a5 100644
--- a/drivers/bluetooth/hci_ibs.c
+++ b/drivers/bluetooth/hci_ibs.c
@@ -254,13 +254,14 @@
struct ibs_struct *ibs = container_of(work, struct ibs_struct,
ws_awake_device);
struct hci_uart *hu = (struct hci_uart *)ibs->ibs_hu;
+ unsigned long flags;
BT_DBG(" %p ", hu);
/* Vote for serial clock */
ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_ON, hu);
- spin_lock(&ibs->hci_ibs_lock);
+ spin_lock_irqsave(&ibs->hci_ibs_lock, flags);
/* send wake indication to device */
if (send_hci_ibs_cmd(HCI_IBS_WAKE_IND, hu) < 0)
@@ -271,7 +272,8 @@
/* start retransmit timer */
mod_timer(&ibs->wake_retrans_timer, jiffies + wake_retrans);
- spin_unlock(&ibs->hci_ibs_lock);
+ spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
+
}
static void ibs_wq_awake_rx(struct work_struct *work)
@@ -279,12 +281,14 @@
struct ibs_struct *ibs = container_of(work, struct ibs_struct,
ws_awake_rx);
struct hci_uart *hu = (struct hci_uart *)ibs->ibs_hu;
+ unsigned long flags;
BT_DBG(" %p ", hu);
ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_ON, hu);
- spin_lock(&ibs->hci_ibs_lock);
+ spin_lock_irqsave(&ibs->hci_ibs_lock, flags);
+
ibs->rx_ibs_state = HCI_IBS_RX_AWAKE;
/* Always acknowledge device wake up,
* sending IBS message doesn't count as TX ON
@@ -294,7 +298,8 @@
ibs->ibs_sent_wacks++; /* debug */
- spin_unlock(&ibs->hci_ibs_lock);
+ spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
+
/* actually send the packets */
hci_uart_tx_wakeup(hu);
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index 2860055..7c0c0b9 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -28,17 +28,23 @@
#include "diagmem.h"
#include "diagchar.h"
#include "diagfwd.h"
+#include "diagfwd_cntl.h"
#include "diag_dci.h"
unsigned int dci_max_reg = 100;
unsigned int dci_max_clients = 10;
+unsigned char dci_cumulative_log_mask[DCI_LOG_MASK_SIZE];
+unsigned char dci_cumulative_event_mask[DCI_EVENT_MASK_SIZE];
+
+#define DCI_CHK_CAPACITY(entry, new_data_len) \
+((entry->data_len + new_data_len > entry->total_capacity) ? 1 : 0) \
static void diag_smd_dci_send_req(int proc_num)
{
void *buf = NULL;
smd_channel_t *smd_ch = NULL;
- int i, r, found = 1;
- int cmd_code_len = 1;
+ int recd_bytes, read_bytes, dci_pkt_len, i;
+ uint8_t recv_pkt_cmd_code;
if (driver->in_busy_dci)
return;
@@ -51,58 +57,237 @@
if (!smd_ch || !buf)
return;
- r = smd_read_avail(smd_ch);
- if (r > IN_BUF_SIZE) {
- if (r < MAX_IN_BUF_SIZE) {
- pr_err("diag: SMD DCI sending pkt upto %d bytes", r);
- buf = krealloc(buf, r, GFP_KERNEL);
+ recd_bytes = smd_read_avail(smd_ch);
+ if (recd_bytes > IN_BUF_SIZE) {
+ if (recd_bytes < MAX_IN_BUF_SIZE) {
+ pr_err("diag: SMD DCI sending pkt upto %d bytes",
+ recd_bytes);
+ buf = krealloc(buf, recd_bytes, GFP_KERNEL);
} else {
pr_err("diag: DCI pkt > %d bytes", MAX_IN_BUF_SIZE);
return;
}
}
- if (buf && r > 0) {
- smd_read(smd_ch, buf, r);
- pr_debug("diag: data received ---\n");
- for (i = 0; i < r; i++)
- pr_debug("\t %x \t", *(((unsigned char *)buf)+i));
+ if (buf && recd_bytes > 0) {
+ smd_read(smd_ch, buf, recd_bytes);
+ pr_debug("diag: data received %d bytes\n", recd_bytes);
+ /* Each SMD read can have multiple DCI packets */
+ read_bytes = 0;
+ while (read_bytes < recd_bytes) {
+ /* read actual length of dci pkt */
+ dci_pkt_len = *(uint16_t *)(buf+2);
+ /* process one dci packet */
+ pr_debug("diag: bytes read = %d, single dci pkt len = %d\n",
+ read_bytes, dci_pkt_len);
+ /* print_hex_dump(KERN_DEBUG, "Single DCI packet :",
+ DUMP_PREFIX_ADDRESS, 16, 1, buf, 5 + dci_pkt_len, 1);*/
+ recv_pkt_cmd_code = *(uint8_t *)(buf+4);
+ if (recv_pkt_cmd_code == LOG_CMD_CODE)
+ extract_dci_log(buf+4);
+ else if (recv_pkt_cmd_code == EVENT_CMD_CODE)
+ extract_dci_events(buf+4);
+ else
+ extract_dci_pkt_rsp(buf); /* pkt response */
+ read_bytes += 5 + dci_pkt_len;
+ buf += 5 + dci_pkt_len; /* advance to next DCI pkt */
+ }
+ driver->in_busy_dci = 1;
+ /* wake up all sleeping DCI clients which have some data */
+ for (i = 0; i < MAX_DCI_CLIENTS; i++)
+ if (driver->dci_client_tbl[i].client &&
+ driver->dci_client_tbl[i].data_len)
+ diag_update_sleeping_process(
+ driver->dci_client_tbl[i].client->tgid,
+ DCI_DATA_TYPE);
+ }
+}
- if (*(uint8_t *)(buf+4) != DCI_CMD_CODE)
- cmd_code_len = 4; /* delayed response */
- driver->write_ptr_dci->length =
- (int)(*(uint16_t *)(buf+2)) - (4+cmd_code_len);
- pr_debug("diag: len = %d\n", (int)(*(uint16_t *)(buf+2))
- - (4+cmd_code_len));
- /* look up DCI client with tag */
- for (i = 0; i < dci_max_reg; i++) {
- if (driver->dci_tbl[i].tag ==
- *(int *)(buf+(4+cmd_code_len))) {
- found = 0;
- break;
+void extract_dci_pkt_rsp(unsigned char *buf)
+{
+ int i = 0, index = -1, cmd_code_len = 1;
+ int curr_client_pid = 0, write_len;
+ struct diag_dci_client_tbl *entry;
+ void *temp_buf = NULL;
+ uint8_t recv_pkt_cmd_code;
+
+ recv_pkt_cmd_code = *(uint8_t *)(buf+4);
+ if (recv_pkt_cmd_code != DCI_PKT_RSP_CODE)
+ cmd_code_len = 4; /* delayed response */
+ write_len = (int)(*(uint16_t *)(buf+2)) - cmd_code_len;
+ pr_debug("diag: len = %d\n", write_len);
+ /* look up DCI client with tag */
+ for (i = 0; i < dci_max_reg; i++) {
+ if (driver->req_tracking_tbl[i].tag ==
+ *(int *)(buf+(4+cmd_code_len))) {
+ *(int *)(buf+4+cmd_code_len) =
+ driver->req_tracking_tbl[i].uid;
+ curr_client_pid =
+ driver->req_tracking_tbl[i].pid;
+ index = i;
+ break;
+ }
+ }
+ if (index == -1)
+ pr_alert("diag: No matching PID for DCI data\n");
+ /* Using PID of client process, find client buffer */
+ for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+ if (curr_client_pid == driver->dci_client_tbl[i].client->tgid) {
+ /* copy pkt rsp in client buf */
+ entry = &(driver->dci_client_tbl[i]);
+ if (DCI_CHK_CAPACITY(entry, 8+write_len)) {
+ pr_alert("diag: create capacity for pkt rsp\n");
+ entry->total_capacity += 8+write_len;
+ temp_buf = krealloc(entry->dci_data,
+ entry->total_capacity, GFP_KERNEL);
+ if (!temp_buf) {
+ pr_err("diag: DCI realloc failed\n");
+ break;
+ } else {
+ entry->dci_data = temp_buf;
+ }
+ }
+ *(int *)(entry->dci_data+entry->data_len) =
+ DCI_PKT_RSP_TYPE;
+ entry->data_len += 4;
+ *(int *)(entry->dci_data+entry->data_len) = write_len;
+ entry->data_len += 4;
+ memcpy(entry->dci_data+entry->data_len,
+ buf+4+cmd_code_len, write_len);
+ entry->data_len += write_len;
+ /* delete immediate response entry */
+ if (driver->buf_in_dci[8+cmd_code_len] != 0x80)
+ driver->req_tracking_tbl[index].pid = 0;
+ break;
+ }
+ }
+}
+
+void extract_dci_events(unsigned char *buf)
+{
+ uint16_t event_id, event_id_packet;
+ uint8_t *event_mask_ptr, byte_mask, payload_len;
+ uint8_t event_data[MAX_EVENT_SIZE], timestamp[8];
+ int i, byte_index, bit_index, length, temp_len;
+ int total_event_len, payload_len_field, timestamp_len;
+ struct diag_dci_client_tbl *entry;
+
+ length = *(uint16_t *)(buf+1); /* total length of event series */
+ temp_len = 0;
+ buf = buf + 3; /* start of event series */
+ while (temp_len < length-1) {
+ *event_data = EVENT_CMD_CODE;
+ event_id_packet = *(uint16_t *)(buf+temp_len);
+ event_id = event_id_packet & 0x0FFF; /* extract 12 bits */
+ if (event_id_packet & 0x8000) {
+ timestamp_len = 2;
+ } else {
+ timestamp_len = 8;
+ memcpy(timestamp, buf+temp_len+2, 8);
+ }
+ if (((event_id_packet & 0x6000) >> 13) == 3) {
+ payload_len_field = 1;
+ payload_len = *(uint8_t *)
+ (buf+temp_len+2+timestamp_len);
+ memcpy(event_data+13, buf+temp_len+2+timestamp_len, 1);
+ memcpy(event_data+14, buf+temp_len+2+timestamp_len+1,
+ payload_len);
+ } else {
+ payload_len_field = 0;
+ payload_len = (event_id_packet & 0x6000) >> 13;
+ if (payload_len < MAX_EVENT_SIZE)
+ memcpy(event_data+13,
+ buf+temp_len+2+timestamp_len, payload_len);
+ else
+ pr_alert("diag: event > %d\n", MAX_EVENT_SIZE);
+ }
+ /* 2 bytes for the event id & timestamp len is hard coded to 8,
+ as individual events have full timestamp */
+ *(uint16_t *)(event_data+1) = 10+payload_len_field+payload_len;
+ *(uint16_t *)(event_data+3) = event_id_packet & 0x7FFF;
+ memcpy(event_data+5, timestamp, 8);
+ total_event_len = 3 + 10 + payload_len_field + payload_len;
+ byte_index = event_id/8;
+ bit_index = event_id % 8;
+ byte_mask = 0x1 << bit_index;
+ /* parse through event mask tbl of each client and check mask */
+ for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+ if (driver->dci_client_tbl[i].client) {
+ entry = &(driver->dci_client_tbl[i]);
+ event_mask_ptr = entry->dci_event_mask +
+ byte_index;
+ if (*event_mask_ptr & byte_mask) {
+ /* copy to client buffer */
+ if (DCI_CHK_CAPACITY(entry,
+ 4 + total_event_len)) {
+ pr_err("diag:DCI event drop\n");
+ driver->dci_client_tbl[i].
+ dropped_events++;
+ return;
+ }
+ *(int *)(entry->dci_data+
+ entry->data_len) = DCI_EVENT_TYPE;
+ memcpy(entry->dci_data+
+ entry->data_len+4, event_data, total_event_len);
+ entry->data_len += 4 + total_event_len;
+ }
}
}
- if (found)
- pr_alert("diag: No matching PID for DCI data\n");
- pr_debug("\n diag PID = %d", driver->dci_tbl[i].pid);
- if (driver->dci_tbl[i].pid == 0)
- pr_alert("diag: Receiving DCI process deleted\n");
- *(int *)(buf+4+cmd_code_len) = driver->dci_tbl[i].uid;
- /* update len after adding UID */
- driver->write_ptr_dci->length =
- driver->write_ptr_dci->length + 4;
- pr_debug("diag: data receivd, wake process\n");
- driver->in_busy_dci = 1;
- diag_update_sleeping_process(driver->dci_tbl[i].pid,
- DCI_DATA_TYPE);
- /* delete immediate response entry */
- if (driver->buf_in_dci[8+cmd_code_len] != 0x80)
- driver->dci_tbl[i].pid = 0;
- for (i = 0; i < dci_max_reg; i++)
- if (driver->dci_tbl[i].pid != 0)
- pr_debug("diag: PID = %d, UID = %d, tag = %d\n",
- driver->dci_tbl[i].pid, driver->dci_tbl[i].uid,
- driver->dci_tbl[i].tag);
- pr_debug("diag: completed clearing table\n");
+ temp_len += 2 + timestamp_len + payload_len_field + payload_len;
+ }
+}
+
+void extract_dci_log(unsigned char *buf)
+{
+ uint16_t log_code, item_num;
+ uint8_t equip_id, *log_mask_ptr, byte_mask;
+ int i, byte_index, found = 0;
+ struct diag_dci_client_tbl *entry;
+
+ log_code = *(uint16_t *)(buf+6);
+ equip_id = LOG_GET_EQUIP_ID(log_code);
+ item_num = LOG_GET_ITEM_NUM(log_code);
+ byte_index = item_num/8 + 2;
+ byte_mask = 0x01 << (item_num % 8);
+
+ /* parse through log mask table of each client and check mask */
+ for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+ if (driver->dci_client_tbl[i].client) {
+ entry = &(driver->dci_client_tbl[i]);
+ log_mask_ptr = entry->dci_log_mask;
+ found = 0;
+ while (log_mask_ptr) {
+ if (*log_mask_ptr == equip_id) {
+ found = 1;
+ pr_debug("diag: find equip id = %x at %p\n",
+ equip_id, log_mask_ptr);
+ break;
+ } else {
+ pr_debug("diag: did not find equip id = %x at %p\n",
+ equip_id, log_mask_ptr);
+ log_mask_ptr += 514;
+ }
+ }
+ if (!found)
+ pr_err("diag: dci equip id not found\n");
+ log_mask_ptr = log_mask_ptr + byte_index;
+ if (*log_mask_ptr & byte_mask) {
+ pr_debug("\t log code %x needed by client %d",
+ log_code, entry->client->tgid);
+ /* copy to client buffer */
+ if (DCI_CHK_CAPACITY(entry,
+ 4 + *(uint16_t *)(buf+2))) {
+ pr_err("diag:DCI log drop\n");
+ driver->dci_client_tbl[i].
+ dropped_logs++;
+ return;
+ }
+ *(int *)(entry->dci_data+entry->data_len) =
+ DCI_LOG_TYPE;
+ memcpy(entry->dci_data+entry->data_len+4, buf+4,
+ *(uint16_t *)(buf+2));
+ entry->data_len += 4 + *(uint16_t *)(buf+2);
+ }
+ }
}
}
@@ -113,7 +298,7 @@
static void diag_smd_dci_notify(void *ctxt, unsigned event)
{
- queue_work(driver->diag_wq, &(driver->diag_read_smd_dci_work));
+ queue_work(driver->diag_dci_wq, &(driver->diag_read_smd_dci_work));
}
void diag_dci_notify_client(int peripheral_mask)
@@ -121,11 +306,11 @@
int i, stat;
/* Notify the DCI process that the peripheral DCI Channel is up */
- for (i = 0; i < MAX_DCI_CLIENT; i++) {
- if (driver->dci_notify_tbl[i].list & peripheral_mask) {
- pr_debug("diag: sending signal now\n");
- stat = send_sig(driver->dci_notify_tbl[i].signal_type,
- driver->dci_notify_tbl[i].client, 0);
+ for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+ if (driver->dci_client_tbl[i].list & peripheral_mask) {
+ pr_info("diag: sending signal now\n");
+ stat = send_sig(driver->dci_client_tbl[i].signal_type,
+ driver->dci_client_tbl[i].client, 0);
if (stat)
pr_err("diag: Err send sig stat: %d\n", stat);
break;
@@ -139,7 +324,7 @@
if (pdev->id == SMD_APPS_MODEM) {
err = smd_open("DIAG_2", &driver->ch_dci, driver,
- diag_smd_dci_notify);
+ diag_smd_dci_notify);
if (err)
pr_err("diag: cannot open DCI port, Id = %d, err ="
" %d\n", pdev->id, err);
@@ -163,10 +348,12 @@
driver->apps_dci_buf[0] = CONTROL_CHAR; /* start */
driver->apps_dci_buf[1] = 1; /* version */
*(uint16_t *)(driver->apps_dci_buf + 2) = len + 4 + 1; /* length */
- driver->apps_dci_buf[4] = DCI_CMD_CODE; /* DCI ID */
- *(int *)(driver->apps_dci_buf + 5) = driver->dci_tbl[index].tag;
+ driver->apps_dci_buf[4] = DCI_PKT_RSP_CODE;
+ *(int *)(driver->apps_dci_buf + 5) =
+ driver->req_tracking_tbl[index].tag;
for (i = 0; i < len; i++)
driver->apps_dci_buf[i+9] = *(buf+i);
+
driver->apps_dci_buf[9+len] = CONTROL_CHAR; /* end */
if (entry.client_id == MODEM_PROC && driver->ch_dci) {
@@ -185,7 +372,7 @@
int i, new_dci_client = 1, ret = -1;
for (i = 0; i < dci_max_reg; i++) {
- if (driver->dci_tbl[i].pid == current->tgid) {
+ if (driver->req_tracking_tbl[i].pid == current->tgid) {
new_dci_client = 0;
break;
}
@@ -193,7 +380,7 @@
mutex_lock(&driver->dci_mutex);
if (new_dci_client)
driver->num_dci_client++;
- if (driver->num_dci_client > MAX_DCI_CLIENT) {
+ if (driver->num_dci_client > MAX_DCI_CLIENTS) {
pr_info("diag: Max DCI Client limit reached\n");
driver->num_dci_client--;
mutex_unlock(&driver->dci_mutex);
@@ -202,10 +389,10 @@
/* Make an entry in kernel DCI table */
driver->dci_tag++;
for (i = 0; i < dci_max_reg; i++) {
- if (driver->dci_tbl[i].pid == 0) {
- driver->dci_tbl[i].pid = current->tgid;
- driver->dci_tbl[i].uid = uid;
- driver->dci_tbl[i].tag = driver->dci_tag;
+ if (driver->req_tracking_tbl[i].pid == 0) {
+ driver->req_tracking_tbl[i].pid = current->tgid;
+ driver->req_tracking_tbl[i].uid = uid;
+ driver->req_tracking_tbl[i].tag = driver->dci_tag;
ret = i;
break;
}
@@ -214,62 +401,313 @@
return ret;
}
-int diag_process_dci_client(unsigned char *buf, int len)
+int diag_process_dci_transaction(unsigned char *buf, int len)
{
unsigned char *temp = buf;
- uint16_t subsys_cmd_code;
- int subsys_id, cmd_code, i, ret = -1, index = -1;
+ uint16_t subsys_cmd_code, log_code, item_num;
+ int subsys_id, cmd_code, i, ret = -1, index = -1, found = 0;
struct diag_master_table entry;
+ int count, set_mask, num_codes, byte_index, bit_index, event_id;
+ uint8_t equip_id, *log_mask_ptr, *head_log_mask_ptr, byte_mask;
+ uint8_t *event_mask_ptr;
- /* enter this UID into kernel table and return index */
- index = diag_register_dci_transaction(*(int *)temp);
- if (index < 0) {
- pr_alert("diag: registering new DCI transaction failed\n");
- return DIAG_DCI_NO_REG;
- }
- temp += 4;
- /* Check for registered peripheral and fwd pkt to apropriate proc */
- cmd_code = (int)(*(char *)buf);
- temp++;
- subsys_id = (int)(*(char *)temp);
- temp++;
- subsys_cmd_code = *(uint16_t *)temp;
- temp += 2;
- pr_debug("diag: %d %d %d", cmd_code, subsys_id, subsys_cmd_code);
- for (i = 0; i < diag_max_reg; i++) {
- entry = driver->table[i];
- if (entry.process_id != NO_PROCESS) {
- if (entry.cmd_code == cmd_code && entry.subsys_id ==
- subsys_id && entry.cmd_code_lo <=
- subsys_cmd_code &&
- entry.cmd_code_hi >= subsys_cmd_code) {
- ret = diag_send_dci_pkt(entry, buf, len, index);
- } else if (entry.cmd_code == 255
- && cmd_code == 75) {
- if (entry.subsys_id ==
- subsys_id &&
- entry.cmd_code_lo <=
- subsys_cmd_code &&
- entry.cmd_code_hi >=
- subsys_cmd_code) {
- ret = diag_send_dci_pkt(entry, buf, len,
- index);
- }
- } else if (entry.cmd_code == 255 &&
- entry.subsys_id == 255) {
- if (entry.cmd_code_lo <=
- cmd_code &&
- entry.
- cmd_code_hi >= cmd_code) {
- ret = diag_send_dci_pkt(entry, buf, len,
- index);
+ /* This is Pkt request/response transaction */
+ if (*(int *)temp > 0) {
+ /* enter this UID into kernel table and return index */
+ index = diag_register_dci_transaction(*(int *)temp);
+ if (index < 0) {
+ pr_alert("diag: registering new DCI transaction failed\n");
+ return DIAG_DCI_NO_REG;
+ }
+ temp += 4;
+ /*
+ * Check for registered peripheral and fwd pkt to
+ * appropriate proc
+ */
+ cmd_code = (int)(*(char *)temp);
+ temp++;
+ subsys_id = (int)(*(char *)temp);
+ temp++;
+ subsys_cmd_code = *(uint16_t *)temp;
+ temp += 2;
+ pr_debug("diag: %d %d %d", cmd_code, subsys_id,
+ subsys_cmd_code);
+ for (i = 0; i < diag_max_reg; i++) {
+ entry = driver->table[i];
+ if (entry.process_id != NO_PROCESS) {
+ if (entry.cmd_code == cmd_code &&
+ entry.subsys_id == subsys_id &&
+ entry.cmd_code_lo <= subsys_cmd_code &&
+ entry.cmd_code_hi >= subsys_cmd_code) {
+ ret = diag_send_dci_pkt(entry, buf,
+ len, index);
+ } else if (entry.cmd_code == 255
+ && cmd_code == 75) {
+ if (entry.subsys_id == subsys_id &&
+ entry.cmd_code_lo <=
+ subsys_cmd_code &&
+ entry.cmd_code_hi >=
+ subsys_cmd_code) {
+ ret = diag_send_dci_pkt(entry,
+ buf, len, index);
+ }
+ } else if (entry.cmd_code == 255 &&
+ entry.subsys_id == 255) {
+ if (entry.cmd_code_lo <= cmd_code &&
+ entry.cmd_code_hi >=
+ cmd_code) {
+ ret = diag_send_dci_pkt(entry,
+ buf, len, index);
+ }
}
}
}
+ } else if (*(int *)temp == DCI_LOG_TYPE) {
+ /* find client id and table */
+ for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+ if (driver->dci_client_tbl[i].client->tgid ==
+ current->tgid) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ pr_err("diag: dci client not registered/found\n");
+ return ret;
+ }
+ /* Extract each log code and put in client table */
+ temp += 4;
+ set_mask = *(int *)temp;
+ temp += 4;
+ num_codes = *(int *)temp;
+ temp += 4;
+
+ head_log_mask_ptr = driver->dci_client_tbl[i].dci_log_mask;
+ pr_info("diag: head of dci log mask %p\n", head_log_mask_ptr);
+ count = 0; /* iterator for extracting log codes */
+ while (count < num_codes) {
+ log_code = *(uint16_t *)temp;
+ equip_id = LOG_GET_EQUIP_ID(log_code);
+ item_num = LOG_GET_ITEM_NUM(log_code);
+ byte_index = item_num/8 + 2;
+ byte_mask = 0x01 << (item_num % 8);
+ /*
+ * Parse through log mask table and find
+ * relevant range
+ */
+ log_mask_ptr = head_log_mask_ptr;
+ found = 0;
+ while (log_mask_ptr) {
+ if (*log_mask_ptr == equip_id) {
+ found = 1;
+ pr_info("diag: find equip id = %x at %p\n",
+ equip_id, log_mask_ptr);
+ break;
+ } else {
+ pr_info("diag: did not find equip id = %x at %p\n",
+ equip_id, log_mask_ptr);
+ log_mask_ptr += 514;
+ }
+ }
+ if (!found) {
+ pr_err("diag: dci equip id not found\n");
+ return ret;
+ }
+ *(log_mask_ptr+1) = 1; /* set the dirty byte */
+ log_mask_ptr = log_mask_ptr + byte_index;
+ if (set_mask)
+ *log_mask_ptr |= byte_mask;
+ else
+ *log_mask_ptr &= ~byte_mask;
+ temp += 2;
+ count++;
+ ret = DIAG_DCI_NO_ERROR;
+ }
+ /* add to cumulative mask */
+ update_dci_cumulative_log_mask(i);
+ /* send updated mask to peripherals */
+ diag_send_dci_log_mask(driver->ch_cntl);
+ } else if (*(int *)temp == DCI_EVENT_TYPE) {
+ /* find client id and table */
+ for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+ if (driver->dci_client_tbl[i].client->tgid ==
+ current->tgid) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ pr_err("diag: dci client not registered/found\n");
+ return ret;
+ }
+ /* Extract each log code and put in client table */
+ temp += 4;
+ set_mask = *(int *)temp;
+ temp += 4;
+ num_codes = *(int *)temp;
+ temp += 4;
+
+ event_mask_ptr = driver->dci_client_tbl[i].dci_event_mask;
+ pr_debug("diag: head of dci event mask %p\n", event_mask_ptr);
+ count = 0; /* iterator for extracting log codes */
+ while (count < num_codes) {
+ event_id = *(int *)temp;
+ byte_index = event_id/8;
+ bit_index = event_id % 8;
+ byte_mask = 0x1 << bit_index;
+ /*
+ * Parse through event mask table and set
+ * relevant byte & bit combination
+ */
+ if (set_mask)
+ *(event_mask_ptr + byte_index) |= byte_mask;
+ else
+ *(event_mask_ptr + byte_index) &= ~byte_mask;
+ temp += sizeof(int);
+ count++;
+ ret = DIAG_DCI_NO_ERROR;
+ }
+ /* add to cumulative mask */
+ update_dci_cumulative_event_mask(i);
+ /* send updated mask to peripherals */
+ diag_send_dci_event_mask(driver->ch_cntl);
+ } else {
+ pr_alert("diag: Incorrect DCI transaction\n");
}
return ret;
}
+void update_dci_cumulative_event_mask(int client_index)
+{
+ int i;
+ uint8_t *update_ptr = dci_cumulative_event_mask;
+ uint8_t *event_mask_ptr;
+
+ event_mask_ptr = driver->dci_client_tbl[client_index].dci_event_mask;
+ for (i = 0; i < DCI_EVENT_MASK_SIZE; i++)
+ *(update_ptr+i) |= *(event_mask_ptr+i);
+}
+
+void diag_send_dci_event_mask(smd_channel_t *ch)
+{
+ void *buf = driver->buf_event_mask_update;
+ int header_size = sizeof(struct diag_ctrl_event_mask);
+ int wr_size = -ENOMEM, retry_count = 0, timer;
+
+ mutex_lock(&driver->diag_cntl_mutex);
+ /* send event mask update */
+ driver->event_mask->cmd_type = DIAG_CTRL_MSG_EVENT_MASK;
+ driver->event_mask->data_len = 7 + DCI_EVENT_MASK_SIZE;
+ driver->event_mask->stream_id = DCI_MASK_STREAM;
+ driver->event_mask->status = 3; /* status for valid mask */
+ driver->event_mask->event_config = 1; /* event config */
+ driver->event_mask->event_mask_size = DCI_EVENT_MASK_SIZE;
+ memcpy(buf, driver->event_mask, header_size);
+ memcpy(buf+header_size, dci_cumulative_event_mask, DCI_EVENT_MASK_SIZE);
+ if (ch) {
+ while (retry_count < 3) {
+ wr_size = smd_write(ch, buf,
+ header_size + DCI_EVENT_MASK_SIZE);
+ if (wr_size == -ENOMEM) {
+ retry_count++;
+ for (timer = 0; timer < 5; timer++)
+ udelay(2000);
+ } else {
+ break;
+ }
+ }
+ if (wr_size != header_size + DCI_EVENT_MASK_SIZE)
+ pr_err("diag: error writing dci event mask %d, tried %d\n",
+ wr_size, header_size + DCI_EVENT_MASK_SIZE);
+ } else
+ pr_err("diag: ch not valid for dci event mask update\n");
+ mutex_unlock(&driver->diag_cntl_mutex);
+}
+
+void update_dci_cumulative_log_mask(int client_index)
+{
+ int i, j;
+ uint8_t *update_ptr = dci_cumulative_log_mask;
+ uint8_t *log_mask_ptr =
+ driver->dci_client_tbl[client_index].dci_log_mask;
+
+ *update_ptr = 0; /* add first equip id */
+ /* skip the first equip id */
+ update_ptr++; log_mask_ptr++;
+ for (i = 0; i < 16; i++) {
+ for (j = 0; j < 513; j++) {
+ *update_ptr |= *log_mask_ptr;
+ update_ptr++;
+ log_mask_ptr++;
+ }
+ *update_ptr = i+1;
+ update_ptr++;
+ log_mask_ptr++;
+ }
+}
+
+void diag_send_dci_log_mask(smd_channel_t *ch)
+{
+ void *buf = driver->buf_log_mask_update;
+ int header_size = sizeof(struct diag_ctrl_log_mask);
+ uint8_t *log_mask_ptr = dci_cumulative_log_mask;
+ int i, wr_size = -ENOMEM, retry_count = 0, timer;
+
+ mutex_lock(&driver->diag_cntl_mutex);
+ for (i = 0; i < 16; i++) {
+ driver->log_mask->cmd_type = DIAG_CTRL_MSG_LOG_MASK;
+ driver->log_mask->num_items = 512;
+ driver->log_mask->data_len = 11 + 512;
+ driver->log_mask->stream_id = DCI_MASK_STREAM;
+ driver->log_mask->status = 3; /* status for valid mask */
+ driver->log_mask->equip_id = *log_mask_ptr;
+ driver->log_mask->log_mask_size = 512;
+ memcpy(buf, driver->log_mask, header_size);
+ memcpy(buf+header_size, log_mask_ptr+2, 512);
+ /* if dirty byte is set and channel is valid */
+ if (ch && *(log_mask_ptr+1)) {
+ while (retry_count < 3) {
+ wr_size = smd_write(ch, buf, header_size + 512);
+ if (wr_size == -ENOMEM) {
+ retry_count++;
+ for (timer = 0; timer < 5; timer++)
+ udelay(2000);
+ } else
+ break;
+ }
+ if (wr_size != header_size + 512)
+ pr_err("diag: dci log mask update failed %d, tried %d",
+ wr_size, header_size + 512);
+ else {
+ *(log_mask_ptr+1) = 0; /* clear dirty byte */
+ pr_debug("diag: updated dci log equip ID %d\n",
+ *log_mask_ptr);
+ }
+ }
+ log_mask_ptr += 514;
+ }
+ mutex_unlock(&driver->diag_cntl_mutex);
+}
+
+void create_dci_log_mask_tbl(unsigned char *tbl_buf)
+{
+ uint8_t i; int count = 0;
+
+ /* create hard coded table for log mask with 16 categories */
+ for (i = 0; i < 16; i++) {
+ *(uint8_t *)tbl_buf = i;
+ pr_debug("diag: put value %x at %p\n", i, tbl_buf);
+ memset(tbl_buf+1, 0, 513); /* set dirty bit as 0 */
+ tbl_buf += 514;
+ count += 514;
+ }
+}
+
+void create_dci_event_mask_tbl(unsigned char *tbl_buf)
+{
+ memset(tbl_buf, 0, 512);
+}
+
static int diag_dci_runtime_suspend(struct device *dev)
{
dev_dbg(dev, "pm_runtime: suspending...\n");
@@ -290,10 +728,10 @@
struct platform_driver msm_diag_dci_driver = {
.probe = diag_dci_probe,
.driver = {
- .name = "DIAG_2",
- .owner = THIS_MODULE,
- .pm = &diag_dci_dev_pm_ops,
- },
+ .name = "DIAG_2",
+ .owner = THIS_MODULE,
+ .pm = &diag_dci_dev_pm_ops,
+ },
};
int diag_dci_init(void)
@@ -316,16 +754,10 @@
if (driver->write_ptr_dci == NULL)
goto err;
}
- if (driver->dci_tbl == NULL) {
- driver->dci_tbl = kzalloc(dci_max_reg *
- sizeof(struct diag_dci_tbl), GFP_KERNEL);
- if (driver->dci_tbl == NULL)
- goto err;
- }
- if (driver->dci_notify_tbl == NULL) {
- driver->dci_notify_tbl = kzalloc(MAX_DCI_CLIENT *
- sizeof(struct dci_notification_tbl), GFP_KERNEL);
- if (driver->dci_notify_tbl == NULL)
+ if (driver->req_tracking_tbl == NULL) {
+ driver->req_tracking_tbl = kzalloc(dci_max_reg *
+ sizeof(struct dci_pkt_req_tracking_tbl), GFP_KERNEL);
+ if (driver->req_tracking_tbl == NULL)
goto err;
}
if (driver->apps_dci_buf == NULL) {
@@ -333,6 +765,13 @@
if (driver->apps_dci_buf == NULL)
goto err;
}
+ if (driver->dci_client_tbl == NULL) {
+ driver->dci_client_tbl = kzalloc(MAX_DCI_CLIENTS *
+ sizeof(struct diag_dci_client_tbl), GFP_KERNEL);
+ if (driver->dci_client_tbl == NULL)
+ goto err;
+ }
+ driver->diag_dci_wq = create_singlethread_workqueue("diag_dci_wq");
success = platform_driver_register(&msm_diag_dci_driver);
if (success) {
pr_err("diag: Could not register DCI driver\n");
@@ -341,11 +780,13 @@
return DIAG_DCI_NO_ERROR;
err:
pr_err("diag: Could not initialize diag DCI buffers");
- kfree(driver->dci_tbl);
- kfree(driver->dci_notify_tbl);
+ kfree(driver->req_tracking_tbl);
+ kfree(driver->dci_client_tbl);
kfree(driver->apps_dci_buf);
kfree(driver->buf_in_dci);
kfree(driver->write_ptr_dci);
+ if (driver->diag_dci_wq)
+ destroy_workqueue(driver->diag_dci_wq);
return DIAG_DCI_NO_REG;
}
@@ -354,10 +795,10 @@
smd_close(driver->ch_dci);
driver->ch_dci = 0;
platform_driver_unregister(&msm_diag_dci_driver);
- kfree(driver->dci_tbl);
- kfree(driver->dci_notify_tbl);
+ kfree(driver->req_tracking_tbl);
+ kfree(driver->dci_client_tbl);
kfree(driver->apps_dci_buf);
kfree(driver->buf_in_dci);
kfree(driver->write_ptr_dci);
+ destroy_workqueue(driver->diag_dci_wq);
}
-
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
index b70efe3..97a285c 100644
--- a/drivers/char/diag/diag_dci.h
+++ b/drivers/char/diag/diag_dci.h
@@ -11,21 +11,48 @@
*/
#ifndef DIAG_DCI_H
#define DIAG_DCI_H
-#define MAX_DCI_CLIENT 10
-#define DCI_CMD_CODE 0x93
+
+#define MAX_DCI_CLIENTS 10
+#define DCI_PKT_RSP_CODE 0x93
+#define DCI_DELAYED_RSP_CODE 0x94
+#define LOG_CMD_CODE 0x10
+#define EVENT_CMD_CODE 0x60
+#define DCI_PKT_RSP_TYPE 0
+#define DCI_LOG_TYPE -1
+#define DCI_EVENT_TYPE -2
+#define SET_LOG_MASK 1
+#define DISABLE_LOG_MASK 0
+#define MAX_EVENT_SIZE 100
+
+/* 16 log code categories, each has:
+ * 1 bytes equip id + 1 dirty byte + 512 byte max log mask
+ */
+#define DCI_LOG_MASK_SIZE (16*514)
+#define DCI_EVENT_MASK_SIZE 512
+#define DCI_MASK_STREAM 2
+#define DCI_MAX_LOG_CODES 16
+#define DCI_MAX_ITEMS_PER_LOG_CODE 512
extern unsigned int dci_max_reg;
extern unsigned int dci_max_clients;
-struct diag_dci_tbl {
+
+struct dci_pkt_req_tracking_tbl {
int pid;
int uid;
int tag;
};
-struct dci_notification_tbl {
+struct diag_dci_client_tbl {
struct task_struct *client;
uint16_t list; /* bit mask */
int signal_type;
+ unsigned char dci_log_mask[DCI_LOG_MASK_SIZE];
+ unsigned char dci_event_mask[DCI_EVENT_MASK_SIZE];
+ unsigned char *dci_data;
+ int data_len;
+ int total_capacity;
+ int dropped_logs;
+ int dropped_events;
};
enum {
@@ -41,7 +68,18 @@
int diag_dci_init(void);
void diag_dci_exit(void);
void diag_read_smd_dci_work_fn(struct work_struct *);
-int diag_process_dci_client(unsigned char *buf, int len);
+int diag_process_dci_transaction(unsigned char *buf, int len);
int diag_send_dci_pkt(struct diag_master_table entry, unsigned char *buf,
int len, int index);
+void extract_dci_pkt_rsp(unsigned char *buf);
+/* DCI Log streaming functions */
+void create_dci_log_mask_tbl(unsigned char *tbl_buf);
+void update_dci_cumulative_log_mask(int client_index);
+void diag_send_dci_log_mask(smd_channel_t *ch);
+void extract_dci_log(unsigned char *buf);
+/* DCI event streaming functions */
+void update_dci_cumulative_event_mask(int client_index);
+void diag_send_dci_event_mask(smd_channel_t *ch);
+void extract_dci_events(unsigned char *buf);
+void create_dci_event_mask_tbl(unsigned char *tbl_buf);
#endif
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index a8e33b5..b535d53 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -147,14 +147,15 @@
struct diag_write_device *buf_tbl;
int use_device_tree;
/* DCI related variables */
- struct diag_dci_tbl *dci_tbl;
- struct dci_notification_tbl *dci_notify_tbl;
+ struct dci_pkt_req_tracking_tbl *req_tracking_tbl;
+ struct diag_dci_client_tbl *dci_client_tbl;
int dci_tag;
int dci_client_id;
struct mutex dci_mutex;
int num_dci_client;
unsigned char *apps_dci_buf;
int dci_state;
+ struct workqueue_struct *diag_dci_wq;
/* Memory pool parameters */
unsigned int itemsize;
unsigned int poolsize;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 09da40c..9f7c7ac 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -377,7 +377,7 @@
int success = -1;
void *temp_buf;
uint16_t support_list = 0;
- struct dci_notification_tbl *notify_params;
+ struct diag_dci_client_tbl *notify_params;
int status;
if (iocmd == DIAG_IOCTL_COMMAND_REG) {
@@ -449,20 +449,31 @@
} else if (iocmd == DIAG_IOCTL_DCI_REG) {
if (driver->dci_state == DIAG_DCI_NO_REG)
return DIAG_DCI_NO_REG;
- if (driver->num_dci_client >= MAX_DCI_CLIENT)
+ if (driver->num_dci_client >= MAX_DCI_CLIENTS)
return DIAG_DCI_NO_REG;
- notify_params = (struct dci_notification_tbl *) ioarg;
+ notify_params = (struct diag_dci_client_tbl *) ioarg;
mutex_lock(&driver->dci_mutex);
driver->num_dci_client++;
pr_debug("diag: id = %d\n", driver->dci_client_id);
driver->dci_client_id++;
- for (i = 0; i < MAX_DCI_CLIENT; i++) {
- if (driver->dci_notify_tbl[i].client == NULL) {
- driver->dci_notify_tbl[i].client = current;
- driver->dci_notify_tbl[i].list =
+ 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].list =
notify_params->list;
- driver->dci_notify_tbl[i].signal_type =
+ driver->dci_client_tbl[i].signal_type =
notify_params->signal_type;
+ create_dci_log_mask_tbl(driver->
+ dci_client_tbl[i].dci_log_mask);
+ create_dci_event_mask_tbl(driver->
+ dci_client_tbl[i].dci_event_mask);
+ driver->dci_client_tbl[i].data_len = 0;
+ driver->dci_client_tbl[i].dci_data =
+ kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+ driver->dci_client_tbl[i].total_capacity =
+ IN_BUF_SIZE;
+ driver->dci_client_tbl[i].dropped_logs = 0;
+ driver->dci_client_tbl[i].dropped_events = 0;
break;
}
}
@@ -473,15 +484,15 @@
/* Delete this process from DCI table */
mutex_lock(&driver->dci_mutex);
for (i = 0; i < dci_max_reg; i++) {
- if (driver->dci_tbl[i].pid == current->tgid) {
+ if (driver->req_tracking_tbl[i].pid == current->tgid) {
pr_debug("diag: delete %d\n", current->tgid);
- driver->dci_tbl[i].pid = 0;
+ driver->req_tracking_tbl[i].pid = 0;
success = i;
}
}
- for (i = 0; i < MAX_DCI_CLIENT; i++) {
- if (driver->dci_notify_tbl[i].client == current) {
- driver->dci_notify_tbl[i].client = NULL;
+ for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+ if (driver->dci_client_tbl[i].client == current) {
+ driver->dci_client_tbl[i].client = NULL;
break;
}
}
@@ -491,10 +502,6 @@
driver->num_dci_client--;
driver->num_dci_client--;
mutex_unlock(&driver->dci_mutex);
- for (i = 0; i < dci_max_reg; i++)
- if (driver->dci_tbl[i].pid != 0)
- pr_debug("diag: PID = %d, UID = %d, tag = %d\n",
- driver->dci_tbl[i].pid, driver->dci_tbl[i].uid, driver->dci_tbl[i].tag);
pr_debug("diag: complete deleting registrations\n");
return success;
} else if (iocmd == DIAG_IOCTL_DCI_SUPPORT) {
@@ -653,6 +660,7 @@
static int diagchar_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
+ struct diag_dci_client_tbl *entry;
int index = -1, i = 0, ret = 0;
int num_data = 0, data_type;
#if defined(CONFIG_DIAG_SDIO_PIPE) || defined(CONFIG_DIAG_BRIDGE_CODE)
@@ -956,23 +964,26 @@
}
if (driver->data_ready[index] & DCI_DATA_TYPE) {
- /*Copy the type of data being passed*/
+ /* Copy the type of data being passed */
data_type = driver->data_ready[index] & DCI_DATA_TYPE;
COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
- COPY_USER_SPACE_OR_EXIT(buf+4,
- driver->write_ptr_dci->length, 4);
- /* check delayed vs immediate response */
- if (*(uint8_t *)(driver->buf_in_dci+4) == DCI_CMD_CODE)
- COPY_USER_SPACE_OR_EXIT(buf+8,
- *(driver->buf_in_dci + 5), driver->write_ptr_dci->length);
- else
- COPY_USER_SPACE_OR_EXIT(buf+8,
- *(driver->buf_in_dci + 8), driver->write_ptr_dci->length);
- driver->in_busy_dci = 0;
+ /* check the current client and copy its data */
+ for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+ entry = &(driver->dci_client_tbl[i]);
+ if (entry && (current->tgid == entry->client->tgid)) {
+ COPY_USER_SPACE_OR_EXIT(buf+4,
+ entry->data_len, 4);
+ COPY_USER_SPACE_OR_EXIT(buf+8,
+ *(entry->dci_data), entry->data_len);
+ entry->data_len = 0;
+ break;
+ }
+ }
driver->data_ready[index] ^= DCI_DATA_TYPE;
+ driver->in_busy_dci = 0;
if (driver->ch_dci)
- queue_work(driver->diag_wq,
- &(driver->diag_read_smd_dci_work));
+ queue_work(driver->diag_dci_wq,
+ &(driver->diag_read_smd_dci_work));
goto exit;
}
exit:
@@ -981,7 +992,7 @@
}
static int diagchar_write(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
+ size_t count, loff_t *ppos)
{
int err, ret = 0, pkt_type;
bool mdm_mask = false;
@@ -1011,7 +1022,7 @@
pr_alert("diag: copy failed for DCI data\n");
return DIAG_DCI_SEND_DATA_FAIL;
}
- err = diag_process_dci_client(driver->user_space_data,
+ err = diag_process_dci_transaction(driver->user_space_data,
payload_size);
return err;
}
@@ -1422,9 +1433,9 @@
INIT_WORK(&(driver->diag_clean_wcnss_reg_work),
diag_clean_wcnss_reg_fn);
diag_debugfs_init();
+ diag_masks_init();
diagfwd_init();
diagfwd_cntl_init();
- diag_masks_init();
driver->dci_state = diag_dci_init();
diag_sdio_fn(INIT);
diag_bridge_fn(INIT);
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index a537bb3..e4501f4 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -160,9 +160,8 @@
{
if (driver->use_device_tree)
return 1;
- else if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm8930aa() ||
- cpu_is_msm9615() || cpu_is_apq8064() || cpu_is_msm8627() ||
- cpu_is_msm8960ab() || cpu_is_apq8064ab())
+ else if (soc_class_is_msm8960() || soc_class_is_msm8930() ||
+ soc_class_is_apq8064() || cpu_is_msm9615())
return 1;
else
return 0;
diff --git a/drivers/cpufreq/cpufreq_gov_msm.c b/drivers/cpufreq/cpufreq_gov_msm.c
index 9c49f80..6ddbf4e 100644
--- a/drivers/cpufreq/cpufreq_gov_msm.c
+++ b/drivers/cpufreq/cpufreq_gov_msm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -18,20 +18,114 @@
#include <linux/kobject.h>
#include <linux/cpufreq.h>
#include <linux/platform_device.h>
+#include <linux/cpu_pm.h>
+#include <linux/pm_qos.h>
+#include <linux/hrtimer.h>
+#include <linux/tick.h>
#include <mach/msm_dcvs.h>
+struct cpu_idle_info {
+ int enabled;
+ int dcvs_core_id;
+ struct pm_qos_request pm_qos_req;
+};
+
+static DEFINE_PER_CPU_SHARED_ALIGNED(struct cpu_idle_info, cpu_idle_info);
+static DEFINE_PER_CPU_SHARED_ALIGNED(u64, iowait_on_cpu);
+static uint32_t latency;
+
+static int msm_dcvs_idle_notifier(int core_num,
+ enum msm_core_control_event event)
+{
+ struct cpu_idle_info *info = &per_cpu(cpu_idle_info, core_num);
+
+ switch (event) {
+ case MSM_DCVS_ENABLE_IDLE_PULSE:
+ info->enabled = true;
+ break;
+
+ case MSM_DCVS_DISABLE_IDLE_PULSE:
+ info->enabled = false;
+ break;
+
+ case MSM_DCVS_ENABLE_HIGH_LATENCY_MODES:
+ pm_qos_update_request(&info->pm_qos_req, PM_QOS_DEFAULT_VALUE);
+ break;
+
+ case MSM_DCVS_DISABLE_HIGH_LATENCY_MODES:
+ pm_qos_update_request(&info->pm_qos_req, latency);
+ break;
+ }
+
+ return 0;
+}
+
+static int msm_cpuidle_notifier(struct notifier_block *self, unsigned long cmd,
+ void *v)
+{
+ struct cpu_idle_info *info =
+ &per_cpu(cpu_idle_info, smp_processor_id());
+ u64 io_wait_us = 0;
+ u64 prev_io_wait_us = 0;
+ u64 last_update_time = 0;
+ u64 val = 0;
+ uint32_t iowaited = 0;
+
+ if (!info->enabled)
+ return NOTIFY_OK;
+
+ switch (cmd) {
+ case CPU_PM_ENTER:
+ val = get_cpu_iowait_time_us(smp_processor_id(),
+ &last_update_time);
+ /* val could be -1 when NOHZ is not enabled */
+ if (val == (u64)-1)
+ val = 0;
+ per_cpu(iowait_on_cpu, smp_processor_id()) = val;
+ msm_dcvs_idle(info->dcvs_core_id, MSM_DCVS_IDLE_ENTER, 0);
+ break;
+
+ case CPU_PM_EXIT:
+ prev_io_wait_us = per_cpu(iowait_on_cpu, smp_processor_id());
+ val = get_cpu_iowait_time_us(smp_processor_id(),
+ &last_update_time);
+ if (val == (u64)-1)
+ val = 0;
+ io_wait_us = val;
+ iowaited = (io_wait_us - prev_io_wait_us);
+ msm_dcvs_idle(info->dcvs_core_id, MSM_DCVS_IDLE_EXIT, iowaited);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block idle_nb = {
+ .notifier_call = msm_cpuidle_notifier,
+};
+
+static void msm_gov_idle_source_init(int cpu, int dcvs_core_id)
+{
+ struct cpu_idle_info *info = NULL;
+
+ info = &per_cpu(cpu_idle_info, cpu);
+ info->dcvs_core_id = dcvs_core_id;
+
+ pm_qos_add_request(&info->pm_qos_req, PM_QOS_CPU_DMA_LATENCY,
+ PM_QOS_DEFAULT_VALUE);
+}
+
struct msm_gov {
- int cpu;
- unsigned int cur_freq;
- unsigned int min_freq;
- unsigned int max_freq;
- struct msm_dcvs_freq gov_notifier;
- struct cpufreq_policy *policy;
+ int cpu;
+ unsigned int cur_freq;
+ unsigned int min_freq;
+ unsigned int max_freq;
+ struct cpufreq_policy *policy;
+ int dcvs_core_id;
};
static DEFINE_PER_CPU_SHARED_ALIGNED(struct mutex, gov_mutex);
static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_gov, msm_gov_info);
-static char core_name[NR_CPUS][10];
static void msm_gov_check_limits(struct cpufreq_policy *policy)
{
@@ -40,7 +134,7 @@
if (policy->max < gov->cur_freq)
__cpufreq_driver_target(policy, policy->max,
CPUFREQ_RELATION_H);
- else if (policy->min > gov->min_freq)
+ else if (policy->min > gov->cur_freq)
__cpufreq_driver_target(policy, policy->min,
CPUFREQ_RELATION_L);
else
@@ -50,14 +144,14 @@
gov->cur_freq = policy->cur;
gov->min_freq = policy->min;
gov->max_freq = policy->max;
+ msm_dcvs_update_limits(gov->dcvs_core_id);
}
-static int msm_dcvs_freq_set(struct msm_dcvs_freq *self,
+static int msm_dcvs_freq_set(int core_num,
unsigned int freq)
{
int ret = -EINVAL;
- struct msm_gov *gov =
- container_of(self, struct msm_gov, gov_notifier);
+ struct msm_gov *gov = &per_cpu(msm_gov_info, core_num);
mutex_lock(&per_cpu(gov_mutex, gov->cpu));
@@ -66,23 +160,30 @@
if (freq > gov->max_freq)
freq = gov->max_freq;
- ret = __cpufreq_driver_target(gov->policy, freq, CPUFREQ_RELATION_L);
- gov->cur_freq = gov->policy->cur;
-
mutex_unlock(&per_cpu(gov_mutex, gov->cpu));
- if (!ret)
- return gov->cur_freq;
+ ret = cpufreq_driver_target(gov->policy, freq, CPUFREQ_RELATION_L);
+
+ if (!ret) {
+ gov->cur_freq = cpufreq_quick_get(gov->cpu);
+ if (freq != gov->cur_freq)
+ pr_err("cpu %d freq %u gov->cur_freq %u didn't match",
+ gov->cpu, freq, gov->cur_freq);
+ }
+ ret = gov->cur_freq;
return ret;
}
-static unsigned int msm_dcvs_freq_get(struct msm_dcvs_freq *self)
+static unsigned int msm_dcvs_freq_get(int core_num)
{
- struct msm_gov *gov =
- container_of(self, struct msm_gov, gov_notifier);
-
- return gov->cur_freq;
+ struct msm_gov *gov = &per_cpu(msm_gov_info, core_num);
+ /*
+ * the rw_sem in cpufreq is always held when this is called.
+ * The policy->cur won't be updated in this case - so it is safe to
+ * access policy->cur
+ */
+ return gov->policy->cur;
}
static int cpufreq_governor_msm(struct cpufreq_policy *policy,
@@ -92,8 +193,6 @@
int ret = 0;
int handle = 0;
struct msm_gov *gov = &per_cpu(msm_gov_info, policy->cpu);
- struct msm_dcvs_freq *dcvs_notifier =
- &(per_cpu(msm_gov_info, cpu).gov_notifier);
switch (event) {
case CPUFREQ_GOV_START:
@@ -103,19 +202,14 @@
mutex_lock(&per_cpu(gov_mutex, cpu));
per_cpu(msm_gov_info, cpu).cpu = cpu;
gov->policy = policy;
- dcvs_notifier->core_name = core_name[cpu];
- dcvs_notifier->set_frequency = msm_dcvs_freq_set;
- dcvs_notifier->get_frequency = msm_dcvs_freq_get;
- handle = msm_dcvs_freq_sink_register(dcvs_notifier);
+ handle = msm_dcvs_freq_sink_start(gov->dcvs_core_id);
BUG_ON(handle < 0);
msm_gov_check_limits(policy);
mutex_unlock(&per_cpu(gov_mutex, cpu));
break;
case CPUFREQ_GOV_STOP:
- mutex_lock(&per_cpu(gov_mutex, cpu));
- msm_dcvs_freq_sink_unregister(dcvs_notifier);
- mutex_unlock(&per_cpu(gov_mutex, cpu));
+ msm_dcvs_freq_sink_stop(gov->dcvs_core_id);
break;
case CPUFREQ_GOV_LIMITS:
@@ -136,21 +230,40 @@
static int __devinit msm_gov_probe(struct platform_device *pdev)
{
- int ret = 0;
int cpu;
- uint32_t group_id = 0x43505530; /* CPU0 */
struct msm_dcvs_core_info *core = NULL;
+ struct msm_dcvs_core_info *core_info = NULL;
+ struct msm_gov_platform_data *pdata = pdev->dev.platform_data;
+ int sensor = 0;
core = pdev->dev.platform_data;
+ core_info = pdata->info;
+ latency = pdata->latency;
for_each_possible_cpu(cpu) {
+ struct msm_gov *gov = &per_cpu(msm_gov_info, cpu);
+
mutex_init(&per_cpu(gov_mutex, cpu));
- snprintf(core_name[cpu], 10, "cpu%d", cpu);
- ret = msm_dcvs_register_core(core_name[cpu], group_id, core);
- if (ret)
+ if (cpu < core->num_cores)
+ sensor = core_info->sensors[cpu];
+ gov->dcvs_core_id = msm_dcvs_register_core(
+ MSM_DCVS_CORE_TYPE_CPU,
+ cpu,
+ core_info,
+ msm_dcvs_freq_set,
+ msm_dcvs_freq_get,
+ msm_dcvs_idle_notifier,
+ sensor);
+ if (gov->dcvs_core_id < 0) {
pr_err("Unable to register core for %d\n", cpu);
+ return -EINVAL;
+ }
+
+ msm_gov_idle_source_init(cpu, gov->dcvs_core_id);
}
+ cpu_pm_register_notifier(&idle_nb);
+
return cpufreq_register_governor(&cpufreq_gov_msm);
}
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index 785ba6c..78161b6 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -869,7 +869,11 @@
static void dbs_input_event(struct input_handle *handle, unsigned int type,
unsigned int code, int value)
{
- int i;
+ int i, j;
+ struct cpumask cpus_scheduled;
+ struct cpu_dbs_info_s *dbs_info;
+ cpumask_clear(&cpus_scheduled);
+
if ((dbs_tuners_ins.powersave_bias == POWERSAVE_BIAS_MAXLEVEL) ||
(dbs_tuners_ins.powersave_bias == POWERSAVE_BIAS_MINLEVEL)) {
@@ -878,7 +882,23 @@
}
for_each_online_cpu(i) {
+ dbs_info = &per_cpu(od_cpu_dbs_info, i);
+
+ if (!dbs_info->cur_policy) {
+ pr_err("Dbs policy is NULL\n");
+ continue;
+ }
+
+ for_each_cpu(j, &cpus_scheduled) {
+ if (cpumask_test_cpu(j, dbs_info->cur_policy->cpus))
+ goto skip_schedule;
+ }
+ cpumask_set_cpu(i, &cpus_scheduled);
queue_work_on(i, input_wq, &per_cpu(dbs_refresh_work, i));
+
+ /* This CPU is already running at new frequency */
+skip_schedule:
+ ;
}
}
diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c
index de5f10f..8a9567b 100644
--- a/drivers/crypto/msm/qce50.c
+++ b/drivers/crypto/msm/qce50.c
@@ -77,7 +77,8 @@
int dst_nents;
dma_addr_t phy_iv_in;
-
+ unsigned char dec_iv[16];
+ int dir;
void *areq;
enum qce_cipher_mode_enum mode;
struct ce_sps_data ce_sps;
@@ -713,16 +714,19 @@
{
struct ahash_request *areq;
unsigned char digest[SHA256_DIGEST_SIZE];
+ uint32_t bytecount32[2];
areq = (struct ahash_request *) pce_dev->areq;
dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
DMA_TO_DEVICE);
memcpy(digest, (char *)(&pce_dev->ce_sps.result->auth_iv[0]),
SHA256_DIGEST_SIZE);
+ _byte_stream_to_net_words(bytecount32,
+ (unsigned char *)pce_dev->ce_sps.result->auth_byte_count,
+ 2 * CRYPTO_REG_SIZE);
if (_qce_unlock_other_pipes(pce_dev))
return -EINVAL;
- pce_dev->qce_cb(areq, digest,
- (char *)pce_dev->ce_sps.result->auth_byte_count,
+ pce_dev->qce_cb(areq, digest, (char *)bytecount32,
pce_dev->ce_sps.consumer_status);
return 0;
};
@@ -750,10 +754,16 @@
pce_dev->ce_sps.producer_status);
} else {
if (pce_dev->ce_sps.minor_version == 0) {
- if (pce_dev->mode == QCE_MODE_CBC)
- memcpy(iv, (char *)sg_virt(areq->src),
- sizeof(iv));
-
+ if (pce_dev->mode == QCE_MODE_CBC) {
+ if (pce_dev->dir == QCE_DECRYPT)
+ memcpy(iv, (char *)pce_dev->dec_iv,
+ sizeof(iv));
+ else
+ memcpy(iv, (unsigned char *)
+ (sg_virt(areq->src) +
+ areq->src->length - 16),
+ sizeof(iv));
+ }
if ((pce_dev->mode == QCE_MODE_CTR) ||
(pce_dev->mode == QCE_MODE_XTS)) {
uint32_t num_blk = 0;
@@ -2326,6 +2336,16 @@
} else {
pce_dev->dst_nents = pce_dev->src_nents;
}
+ pce_dev->dir = c_req->dir;
+ if ((pce_dev->ce_sps.minor_version == 0) && (c_req->dir == QCE_DECRYPT)
+ && (c_req->mode == QCE_MODE_CBC)) {
+ struct ablkcipher_request *areq =
+ (struct ablkcipher_request *)pce_dev->areq;
+ memcpy(pce_dev->dec_iv, (unsigned char *)sg_virt(areq->src) +
+ areq->src->length - 16,
+ NUM_OF_CRYPTO_CNTR_IV_REG * CRYPTO_REG_SIZE);
+ }
+
/* set up crypto device */
rc = _ce_setup_cipher(pce_dev, c_req, areq->nbytes, 0, cmdlistinfo);
if (rc < 0)
diff --git a/drivers/crypto/msm/qce50.h b/drivers/crypto/msm/qce50.h
index c9eba82..8533636 100644
--- a/drivers/crypto/msm/qce50.h
+++ b/drivers/crypto/msm/qce50.h
@@ -36,7 +36,7 @@
/* QCE max number of descriptor in a descriptor list */
#define QCE_MAX_NUM_DESC 128
-#define SPS_MAX_PKT_SIZE (64 * 1024 - 1)
+#define SPS_MAX_PKT_SIZE (32 * 1024 - 64)
/* State of consumer/producer Pipe */
enum qce_pipe_st_enum {
diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c
index a48e6b2..b3df752 100644
--- a/drivers/gpu/ion/ion.c
+++ b/drivers/gpu/ion/ion.c
@@ -261,6 +261,12 @@
mutex_unlock(&buffer->lock);
}
+static void ion_delayed_unsecure(struct ion_buffer *buffer)
+{
+ if (buffer->heap->ops->unsecure_buffer)
+ buffer->heap->ops->unsecure_buffer(buffer, 1);
+}
+
static void ion_buffer_destroy(struct kref *kref)
{
struct ion_buffer *buffer = container_of(kref, struct ion_buffer, ref);
@@ -271,6 +277,7 @@
buffer->heap->ops->unmap_dma(buffer->heap, buffer);
+ ion_delayed_unsecure(buffer);
ion_iommu_delayed_unmap(buffer);
buffer->heap->ops->free(buffer);
mutex_lock(&dev->lock);
@@ -1644,6 +1651,73 @@
mutex_unlock(&dev->lock);
}
+int ion_secure_handle(struct ion_client *client, struct ion_handle *handle,
+ int version, void *data, int flags)
+{
+ int ret = -EINVAL;
+ struct ion_heap *heap;
+ struct ion_buffer *buffer;
+
+ mutex_lock(&client->lock);
+ if (!ion_handle_validate(client, handle)) {
+ WARN(1, "%s: invalid handle passed to secure.\n", __func__);
+ goto out_unlock;
+ }
+
+ buffer = handle->buffer;
+ heap = buffer->heap;
+
+ if (heap->type != (enum ion_heap_type) ION_HEAP_TYPE_CP) {
+ pr_err("%s: cannot secure buffer from non secure heap\n",
+ __func__);
+ goto out_unlock;
+ }
+
+ BUG_ON(!buffer->heap->ops->secure_buffer);
+ /*
+ * Protect the handle via the client lock to ensure we aren't
+ * racing with free
+ */
+ ret = buffer->heap->ops->secure_buffer(buffer, version, data, flags);
+
+out_unlock:
+ mutex_unlock(&client->lock);
+ return ret;
+}
+
+int ion_unsecure_handle(struct ion_client *client, struct ion_handle *handle)
+{
+ int ret = -EINVAL;
+ struct ion_heap *heap;
+ struct ion_buffer *buffer;
+
+ mutex_lock(&client->lock);
+ if (!ion_handle_validate(client, handle)) {
+ WARN(1, "%s: invalid handle passed to secure.\n", __func__);
+ goto out_unlock;
+ }
+
+ buffer = handle->buffer;
+ heap = buffer->heap;
+
+ if (heap->type != (enum ion_heap_type) ION_HEAP_TYPE_CP) {
+ pr_err("%s: cannot secure buffer from non secure heap\n",
+ __func__);
+ goto out_unlock;
+ }
+
+ BUG_ON(!buffer->heap->ops->unsecure_buffer);
+ /*
+ * Protect the handle via the client lock to ensure we aren't
+ * racing with free
+ */
+ ret = buffer->heap->ops->unsecure_buffer(buffer, 0);
+
+out_unlock:
+ mutex_unlock(&client->lock);
+ return ret;
+}
+
int ion_secure_heap(struct ion_device *dev, int heap_id, int version,
void *data)
{
diff --git a/drivers/gpu/ion/ion_cp_heap.c b/drivers/gpu/ion/ion_cp_heap.c
index 2070abf..f9a9212 100644
--- a/drivers/gpu/ion/ion_cp_heap.c
+++ b/drivers/gpu/ion/ion_cp_heap.c
@@ -85,8 +85,8 @@
unsigned int heap_protected;
unsigned long allocated_bytes;
unsigned long total_size;
- int (*request_region)(void *);
- int (*release_region)(void *);
+ int (*heap_request_region)(void *);
+ int (*heap_release_region)(void *);
void *bus_id;
unsigned long kmap_cached_count;
unsigned long kmap_uncached_count;
@@ -106,6 +106,26 @@
HEAP_PROTECTED = 1,
};
+struct ion_cp_buffer {
+ phys_addr_t buffer;
+ atomic_t secure_cnt;
+ int is_secure;
+ int want_delayed_unsecure;
+ /*
+ * Currently all user/kernel mapping is protected by the heap lock.
+ * This is sufficient to protect the map count as well. The lock
+ * should be used to protect map_cnt if the whole heap lock is
+ * ever removed.
+ */
+ atomic_t map_cnt;
+ /*
+ * protects secure_cnt for securing.
+ */
+ struct mutex lock;
+ int version;
+ void *data;
+};
+
static int ion_cp_protect_mem(unsigned int phy_base, unsigned int size,
unsigned int permission_type, int version,
void *data);
@@ -124,6 +144,170 @@
return cp_heap->kmap_cached_count + cp_heap->kmap_uncached_count;
}
+static int ion_on_first_alloc(struct ion_heap *heap)
+{
+ struct ion_cp_heap *cp_heap =
+ container_of(heap, struct ion_cp_heap, heap);
+ int ret_value;
+
+ if (cp_heap->reusable) {
+ ret_value = fmem_set_state(FMEM_C_STATE);
+ if (ret_value)
+ return 1;
+ }
+ return 0;
+}
+
+static void ion_on_last_free(struct ion_heap *heap)
+{
+ struct ion_cp_heap *cp_heap =
+ container_of(heap, struct ion_cp_heap, heap);
+
+ if (cp_heap->reusable)
+ if (fmem_set_state(FMEM_T_STATE) != 0)
+ pr_err("%s: unable to transition heap to T-state\n",
+ __func__);
+}
+
+/* Must be protected by ion_cp_buffer lock */
+static int __ion_cp_protect_buffer(struct ion_buffer *buffer, int version,
+ void *data, int flags)
+{
+ struct ion_cp_buffer *buf = buffer->priv_virt;
+ int ret_value = 0;
+
+ if (atomic_inc_return(&buf->secure_cnt) == 1) {
+ ret_value = ion_cp_protect_mem(buf->buffer,
+ buffer->size, 0,
+ version, data);
+
+ if (ret_value) {
+ pr_err("Failed to secure buffer %p, error %d\n",
+ buffer, ret_value);
+ atomic_dec(&buf->secure_cnt);
+ } else {
+ pr_debug("Protected buffer %p from %x-%x\n",
+ buffer, buf->buffer,
+ buf->buffer + buffer->size);
+ buf->want_delayed_unsecure |=
+ flags & ION_UNSECURE_DELAYED ? 1 : 0;
+ buf->data = data;
+ buf->version = version;
+ }
+ }
+ pr_debug("buffer %p protect count %d\n", buffer,
+ atomic_read(&buf->secure_cnt));
+ BUG_ON(atomic_read(&buf->secure_cnt) < 0);
+ return ret_value;
+}
+
+/* Must be protected by ion_cp_buffer lock */
+static int __ion_cp_unprotect_buffer(struct ion_buffer *buffer, int version,
+ void *data, int force_unsecure)
+{
+ struct ion_cp_buffer *buf = buffer->priv_virt;
+ int ret_value = 0;
+
+ if (force_unsecure) {
+ if (!buf->is_secure || atomic_read(&buf->secure_cnt) == 0)
+ return 0;
+
+ if (atomic_read(&buf->secure_cnt) != 1) {
+ WARN(1, "Forcing unsecure of buffer with outstanding secure count %d!\n",
+ atomic_read(&buf->secure_cnt));
+ atomic_set(&buf->secure_cnt, 1);
+ }
+ }
+
+ if (atomic_dec_and_test(&buf->secure_cnt)) {
+ ret_value = ion_cp_unprotect_mem(
+ buf->buffer, buffer->size,
+ 0, version, data);
+
+ if (ret_value) {
+ pr_err("Failed to unsecure buffer %p, error %d\n",
+ buffer, ret_value);
+ /*
+ * If the force unsecure is happening, the buffer
+ * is being destroyed. We failed to unsecure the
+ * buffer even though the memory is given back.
+ * Just die now rather than discovering later what
+ * happens when trying to use the secured memory as
+ * unsecured...
+ */
+ BUG_ON(force_unsecure);
+ /* Bump the count back up one to try again later */
+ atomic_inc(&buf->secure_cnt);
+ } else {
+ buf->version = -1;
+ buf->data = NULL;
+ }
+ }
+ pr_debug("buffer %p unprotect count %d\n", buffer,
+ atomic_read(&buf->secure_cnt));
+ BUG_ON(atomic_read(&buf->secure_cnt) < 0);
+ return ret_value;
+}
+
+int ion_cp_secure_buffer(struct ion_buffer *buffer, int version, void *data,
+ int flags)
+{
+ int ret_value;
+ struct ion_cp_buffer *buf = buffer->priv_virt;
+
+ mutex_lock(&buf->lock);
+ if (!buf->is_secure) {
+ pr_err("%s: buffer %p was not allocated as secure\n",
+ __func__, buffer);
+ ret_value = -EINVAL;
+ goto out_unlock;
+ }
+
+ if (ION_IS_CACHED(buffer->flags)) {
+ pr_err("%s: buffer %p was allocated as cached\n",
+ __func__, buffer);
+ ret_value = -EINVAL;
+ goto out_unlock;
+ }
+
+ if (atomic_read(&buf->map_cnt)) {
+ pr_err("%s: cannot secure buffer %p with outstanding mappings. Total count: %d",
+ __func__, buffer, atomic_read(&buf->map_cnt));
+ ret_value = -EINVAL;
+ goto out_unlock;
+ }
+
+ if (atomic_read(&buf->secure_cnt)) {
+ if (buf->version != version || buf->data != data) {
+ pr_err("%s: Trying to re-secure buffer with different values",
+ __func__);
+ pr_err("Last secured version: %d Currrent %d\n",
+ buf->version, version);
+ pr_err("Last secured data: %p current %p\n",
+ buf->data, data);
+ ret_value = -EINVAL;
+ goto out_unlock;
+ }
+ }
+ ret_value = __ion_cp_protect_buffer(buffer, version, data, flags);
+
+out_unlock:
+ mutex_unlock(&buf->lock);
+ return ret_value;
+}
+
+int ion_cp_unsecure_buffer(struct ion_buffer *buffer, int force_unsecure)
+{
+ int ret_value = 0;
+ struct ion_cp_buffer *buf = buffer->priv_virt;
+
+ mutex_lock(&buf->lock);
+ ret_value = __ion_cp_unprotect_buffer(buffer, buf->version, buf->data,
+ force_unsecure);
+ mutex_unlock(&buf->lock);
+ return ret_value;
+}
+
/**
* Protects memory if heap is unsecured heap. Also ensures that we are in
* the correct FMEM state if this heap is a reusable heap.
@@ -137,11 +321,9 @@
if (atomic_inc_return(&cp_heap->protect_cnt) == 1) {
/* Make sure we are in C state when the heap is protected. */
- if (cp_heap->reusable && !cp_heap->allocated_bytes) {
- ret_value = fmem_set_state(FMEM_C_STATE);
- if (ret_value)
+ if (!cp_heap->allocated_bytes)
+ if (ion_on_first_alloc(heap))
goto out;
- }
ret_value = ion_cp_protect_mem(cp_heap->secure_base,
cp_heap->secure_size, cp_heap->permission_type,
@@ -150,11 +332,9 @@
pr_err("Failed to protect memory for heap %s - "
"error code: %d\n", heap->name, ret_value);
- if (cp_heap->reusable && !cp_heap->allocated_bytes) {
- if (fmem_set_state(FMEM_T_STATE) != 0)
- pr_err("%s: unable to transition heap to T-state\n",
- __func__);
- }
+ if (!cp_heap->allocated_bytes)
+ ion_on_last_free(heap);
+
atomic_dec(&cp_heap->protect_cnt);
} else {
cp_heap->heap_protected = HEAP_PROTECTED;
@@ -191,11 +371,8 @@
pr_debug("Un-protected heap %s @ 0x%x\n", heap->name,
(unsigned int) cp_heap->base);
- if (cp_heap->reusable && !cp_heap->allocated_bytes) {
- if (fmem_set_state(FMEM_T_STATE) != 0)
- pr_err("%s: unable to transition heap to T-state",
- __func__);
- }
+ if (!cp_heap->allocated_bytes)
+ ion_on_last_free(heap);
}
}
pr_debug("%s: protect count is %d\n", __func__,
@@ -236,12 +413,11 @@
* if this is the first reusable allocation, transition
* the heap
*/
- if (cp_heap->reusable && !cp_heap->allocated_bytes) {
- if (fmem_set_state(FMEM_C_STATE) != 0) {
+ if (!cp_heap->allocated_bytes)
+ if (ion_on_first_alloc(heap)) {
mutex_unlock(&cp_heap->lock);
return ION_RESERVED_ALLOCATE_FAIL;
}
- }
cp_heap->allocated_bytes += size;
mutex_unlock(&cp_heap->lock);
@@ -260,13 +436,9 @@
__func__, heap->name,
cp_heap->total_size -
cp_heap->allocated_bytes, size);
-
- if (cp_heap->reusable && !cp_heap->allocated_bytes &&
- cp_heap->heap_protected == HEAP_NOT_PROTECTED) {
- if (fmem_set_state(FMEM_T_STATE) != 0)
- pr_err("%s: unable to transition heap to T-state\n",
- __func__);
- }
+ if (!cp_heap->allocated_bytes &&
+ cp_heap->heap_protected == HEAP_NOT_PROTECTED)
+ ion_on_last_free(heap);
mutex_unlock(&cp_heap->lock);
return ION_CP_ALLOCATE_FAIL;
@@ -311,12 +483,9 @@
mutex_lock(&cp_heap->lock);
cp_heap->allocated_bytes -= size;
- if (cp_heap->reusable && !cp_heap->allocated_bytes &&
- cp_heap->heap_protected == HEAP_NOT_PROTECTED) {
- if (fmem_set_state(FMEM_T_STATE) != 0)
- pr_err("%s: unable to transition heap to T-state\n",
- __func__);
- }
+ if (!cp_heap->allocated_bytes &&
+ cp_heap->heap_protected == HEAP_NOT_PROTECTED)
+ ion_on_last_free(heap);
/* Unmap everything if we previously mapped the whole heap at once. */
if (!cp_heap->allocated_bytes) {
@@ -344,7 +513,9 @@
struct ion_buffer *buffer,
ion_phys_addr_t *addr, size_t *len)
{
- *addr = buffer->priv_phys;
+ struct ion_cp_buffer *buf = buffer->priv_virt;
+
+ *addr = buf->buffer;
*len = buffer->size;
return 0;
}
@@ -354,34 +525,83 @@
unsigned long size, unsigned long align,
unsigned long flags)
{
- buffer->priv_phys = ion_cp_allocate(heap, size, align, flags);
- return buffer->priv_phys == ION_CP_ALLOCATE_FAIL ? -ENOMEM : 0;
+ struct ion_cp_buffer *buf;
+ phys_addr_t addr;
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return ION_CP_ALLOCATE_FAIL;
+
+ addr = ion_cp_allocate(heap, size, align, flags);
+ if (addr == ION_CP_ALLOCATE_FAIL)
+ return -ENOMEM;
+
+ buf->buffer = addr;
+ buf->want_delayed_unsecure = 0;
+ atomic_set(&buf->secure_cnt, 0);
+ mutex_init(&buf->lock);
+ buf->is_secure = flags & ION_SECURE ? 1 : 0;
+ buffer->priv_virt = buf;
+
+ return 0;
}
static void ion_cp_heap_free(struct ion_buffer *buffer)
{
struct ion_heap *heap = buffer->heap;
+ struct ion_cp_buffer *buf = buffer->priv_virt;
- ion_cp_free(heap, buffer->priv_phys, buffer->size);
- buffer->priv_phys = ION_CP_ALLOCATE_FAIL;
+ ion_cp_free(heap, buf->buffer, buffer->size);
+ WARN_ON(atomic_read(&buf->secure_cnt));
+ WARN_ON(atomic_read(&buf->map_cnt));
+ kfree(buf);
+
+ buffer->priv_virt = NULL;
}
struct sg_table *ion_cp_heap_create_sg_table(struct ion_buffer *buffer)
{
struct sg_table *table;
int ret;
+ struct ion_cp_buffer *buf = buffer->priv_virt;
table = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
if (!table)
return ERR_PTR(-ENOMEM);
- ret = sg_alloc_table(table, 1, GFP_KERNEL);
- if (ret)
- goto err0;
+ if (buf->is_secure) {
+ int n_chunks;
+ int i;
+ struct scatterlist *sg;
- table->sgl->length = buffer->size;
- table->sgl->offset = 0;
- table->sgl->dma_address = buffer->priv_phys;
+ if (!IS_ALIGNED(buffer->size, SZ_1M)) {
+ pr_err("%s: buffer is marked as secure but buffer size %x is not aligned to 1MB\n",
+ __func__, buffer->size);
+
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* Count number of 1MB chunks. Alignment is already checked. */
+ n_chunks = buffer->size >> 20;
+
+ ret = sg_alloc_table(table, n_chunks, GFP_KERNEL);
+ if (ret)
+ goto err0;
+
+ for_each_sg(table->sgl, sg, table->nents, i) {
+ sg_dma_address(sg) = buf->buffer + i * SZ_1M;
+ sg->length = SZ_1M;
+ sg->offset = 0;
+ }
+ } else {
+ ret = sg_alloc_table(table, 1, GFP_KERNEL);
+ if (ret)
+ goto err0;
+
+ table->sgl->length = buffer->size;
+ table->sgl->offset = 0;
+ table->sgl->dma_address = buf->buffer;
+ }
return table;
err0:
@@ -411,8 +631,9 @@
{
int ret_value = 0;
if ((cp_heap->umap_count + ion_cp_get_total_kmap_count(cp_heap)) == 0)
- if (cp_heap->request_region)
- ret_value = cp_heap->request_region(cp_heap->bus_id);
+ if (cp_heap->heap_request_region)
+ ret_value = cp_heap->heap_request_region(
+ cp_heap->bus_id);
return ret_value;
}
@@ -423,8 +644,9 @@
{
int ret_value = 0;
if ((cp_heap->umap_count + ion_cp_get_total_kmap_count(cp_heap)) == 0)
- if (cp_heap->release_region)
- ret_value = cp_heap->release_region(cp_heap->bus_id);
+ if (cp_heap->heap_release_region)
+ ret_value = cp_heap->heap_release_region(
+ cp_heap->bus_id);
return ret_value;
}
@@ -432,17 +654,18 @@
void *virt_base, unsigned long flags)
{
int ret;
- unsigned int offset = buffer->priv_phys - phys_base;
+ struct ion_cp_buffer *buf = buffer->priv_virt;
+ unsigned int offset = buf->buffer - phys_base;
unsigned long start = ((unsigned long)virt_base) + offset;
const struct mem_type *type = ION_IS_CACHED(flags) ?
get_mem_type(MT_DEVICE_CACHED) :
get_mem_type(MT_DEVICE);
- if (phys_base > buffer->priv_phys)
+ if (phys_base > buf->buffer)
return NULL;
- ret = ioremap_pages(start, buffer->priv_phys, buffer->size, type);
+ ret = ioremap_pages(start, buf->buffer, buffer->size, type);
if (!ret)
return (void *)start;
@@ -455,6 +678,7 @@
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
void *ret_value = NULL;
+ struct ion_cp_buffer *buf = buffer->priv_virt;
mutex_lock(&cp_heap->lock);
if ((cp_heap->heap_protected == HEAP_NOT_PROTECTED) ||
@@ -472,10 +696,10 @@
} else {
if (ION_IS_CACHED(buffer->flags))
- ret_value = ioremap_cached(buffer->priv_phys,
+ ret_value = ioremap_cached(buf->buffer,
buffer->size);
else
- ret_value = ioremap(buffer->priv_phys,
+ ret_value = ioremap(buf->buffer,
buffer->size);
}
@@ -486,6 +710,7 @@
++cp_heap->kmap_cached_count;
else
++cp_heap->kmap_uncached_count;
+ atomic_inc(&buf->map_cnt);
}
}
mutex_unlock(&cp_heap->lock);
@@ -497,6 +722,7 @@
{
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
+ struct ion_cp_buffer *buf = buffer->priv_virt;
if (cp_heap->reusable)
unmap_kernel_range((unsigned long)buffer->vaddr, buffer->size);
@@ -510,6 +736,8 @@
--cp_heap->kmap_cached_count;
else
--cp_heap->kmap_uncached_count;
+
+ atomic_dec(&buf->map_cnt);
ion_cp_release_region(cp_heap);
mutex_unlock(&cp_heap->lock);
@@ -522,6 +750,7 @@
int ret_value = -EAGAIN;
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
+ struct ion_cp_buffer *buf = buffer->priv_virt;
mutex_lock(&cp_heap->lock);
if (cp_heap->heap_protected == HEAP_NOT_PROTECTED) {
@@ -535,14 +764,17 @@
vma->vm_page_prot);
ret_value = remap_pfn_range(vma, vma->vm_start,
- __phys_to_pfn(buffer->priv_phys) + vma->vm_pgoff,
+ __phys_to_pfn(buf->buffer) + vma->vm_pgoff,
vma->vm_end - vma->vm_start,
vma->vm_page_prot);
- if (ret_value)
+ if (ret_value) {
ion_cp_release_region(cp_heap);
- else
+ } else {
+ atomic_inc(&buf->map_cnt);
++cp_heap->umap_count;
+ }
+
}
mutex_unlock(&cp_heap->lock);
return ret_value;
@@ -553,9 +785,11 @@
{
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
+ struct ion_cp_buffer *buf = buffer->priv_virt;
mutex_lock(&cp_heap->lock);
--cp_heap->umap_count;
+ atomic_dec(&buf->map_cnt);
ion_cp_release_region(cp_heap);
mutex_unlock(&cp_heap->lock);
}
@@ -567,6 +801,7 @@
void (*outer_cache_op)(phys_addr_t, phys_addr_t);
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
+ struct ion_cp_buffer *buf = buffer->priv_virt;
switch (cmd) {
case ION_IOC_CLEAN_CACHES:
@@ -586,7 +821,7 @@
}
if (cp_heap->has_outer_cache) {
- unsigned long pstart = buffer->priv_phys + offset;
+ unsigned long pstart = buf->buffer + offset;
outer_cache_op(pstart, pstart + length);
}
return 0;
@@ -774,25 +1009,26 @@
struct ion_cp_heap *cp_heap =
container_of(buffer->heap, struct ion_cp_heap, heap);
int prot = IOMMU_WRITE | IOMMU_READ;
+ struct ion_cp_buffer *buf = buffer->priv_virt;
prot |= ION_IS_CACHED(flags) ? IOMMU_CACHE : 0;
data->mapped_size = iova_length;
if (!msm_use_iommu()) {
- data->iova_addr = buffer->priv_phys;
+ data->iova_addr = buf->buffer;
return 0;
}
if (cp_heap->iommu_iova[domain_num]) {
/* Already mapped. */
- unsigned long offset = buffer->priv_phys - cp_heap->base;
+ unsigned long offset = buf->buffer - cp_heap->base;
data->iova_addr = cp_heap->iommu_iova[domain_num] + offset;
return 0;
} else if (cp_heap->iommu_map_all) {
ret = iommu_map_all(domain_num, cp_heap, partition_num, prot);
if (!ret) {
unsigned long offset =
- buffer->priv_phys - cp_heap->base;
+ buf->buffer - cp_heap->base;
data->iova_addr =
cp_heap->iommu_iova[domain_num] + offset;
cp_heap->iommu_partition[domain_num] = partition_num;
@@ -902,6 +1138,8 @@
.unsecure_heap = ion_cp_unsecure_heap,
.map_iommu = ion_cp_heap_map_iommu,
.unmap_iommu = ion_cp_heap_unmap_iommu,
+ .secure_buffer = ion_cp_secure_buffer,
+ .unsecure_buffer = ion_cp_unsecure_buffer,
};
struct ion_heap *ion_cp_heap_create(struct ion_platform_heap *heap_data)
@@ -949,9 +1187,11 @@
if (extra_data->setup_region)
cp_heap->bus_id = extra_data->setup_region();
if (extra_data->request_region)
- cp_heap->request_region = extra_data->request_region;
+ cp_heap->heap_request_region =
+ extra_data->request_region;
if (extra_data->release_region)
- cp_heap->release_region = extra_data->release_region;
+ cp_heap->heap_release_region =
+ extra_data->release_region;
cp_heap->iommu_map_all =
extra_data->iommu_map_all;
cp_heap->iommu_2x_map_domain =
diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h
index 273e57e..991a310 100644
--- a/drivers/gpu/ion/ion_priv.h
+++ b/drivers/gpu/ion/ion_priv.h
@@ -147,6 +147,9 @@
const struct rb_root *mem_map);
int (*secure_heap)(struct ion_heap *heap, int version, void *data);
int (*unsecure_heap)(struct ion_heap *heap, int version, void *data);
+ int (*secure_buffer)(struct ion_buffer *buffer, int version,
+ void *data, int flags);
+ int (*unsecure_buffer)(struct ion_buffer *buffer, int force_unsecure);
};
/**
@@ -307,4 +310,10 @@
void ion_mem_map_show(struct ion_heap *heap);
+
+
+int ion_secure_handle(struct ion_client *client, struct ion_handle *handle,
+ int version, void *data, int flags);
+
+int ion_unsecure_handle(struct ion_client *client, struct ion_handle *handle);
#endif /* _ION_PRIV_H */
diff --git a/drivers/gpu/ion/msm/ion_cp_common.c b/drivers/gpu/ion/msm/ion_cp_common.c
index b274ba2..803b04a 100644
--- a/drivers/gpu/ion/msm/ion_cp_common.c
+++ b/drivers/gpu/ion/msm/ion_cp_common.c
@@ -37,6 +37,7 @@
int lock)
{
struct cp2_lock_req request;
+ u32 resp;
request.mem_usage = usage;
request.lock = lock;
@@ -46,7 +47,7 @@
request.chunks.chunk_size = chunk_size;
return scm_call(SCM_SVC_CP, MEM_PROTECT_LOCK_ID,
- &request, sizeof(request), NULL, 0);
+ &request, sizeof(request), &resp, sizeof(resp));
}
diff --git a/drivers/gpu/ion/msm/msm_ion.c b/drivers/gpu/ion/msm/msm_ion.c
index deff514..7fe47ee 100644
--- a/drivers/gpu/ion/msm/msm_ion.c
+++ b/drivers/gpu/ion/msm/msm_ion.c
@@ -130,6 +130,22 @@
}
EXPORT_SYMBOL(msm_ion_unsecure_heap_2_0);
+int msm_ion_secure_buffer(struct ion_client *client, struct ion_handle *handle,
+ enum cp_mem_usage usage,
+ int flags)
+{
+ return ion_secure_handle(client, handle, ION_CP_V2,
+ (void *)usage, flags);
+}
+EXPORT_SYMBOL(msm_ion_secure_buffer);
+
+int msm_ion_unsecure_buffer(struct ion_client *client,
+ struct ion_handle *handle)
+{
+ return ion_unsecure_handle(client, handle);
+}
+EXPORT_SYMBOL(msm_ion_unsecure_buffer);
+
int msm_ion_do_cache_op(struct ion_client *client, struct ion_handle *handle,
void *vaddr, unsigned long len, unsigned int cmd)
{
diff --git a/drivers/gpu/msm/a3xx_reg.h b/drivers/gpu/msm/a3xx_reg.h
index 05c7967..90f14e6 100644
--- a/drivers/gpu/msm/a3xx_reg.h
+++ b/drivers/gpu/msm/a3xx_reg.h
@@ -65,13 +65,17 @@
#define A3XX_RBBM_INT_CLEAR_CMD 0x061
#define A3XX_RBBM_INT_0_MASK 0x063
#define A3XX_RBBM_INT_0_STATUS 0x064
+#define A3XX_RBBM_PERFCTR_CTL 0x80
#define A3XX_RBBM_GPU_BUSY_MASKED 0x88
+#define A3XX_RBBM_PERFCTR_SP_7_LO 0xE0
+#define A3XX_RBBM_PERFCTR_SP_7_HI 0xE1
#define A3XX_RBBM_RBBM_CTL 0x100
#define A3XX_RBBM_RBBM_CTL 0x100
#define A3XX_RBBM_PERFCTR_PWR_1_LO 0x0EC
#define A3XX_RBBM_PERFCTR_PWR_1_HI 0x0ED
#define A3XX_RBBM_DEBUG_BUS_CTL 0x111
#define A3XX_RBBM_DEBUG_BUS_DATA_STATUS 0x112
+
/* Following two are same as on A2XX, just in a different place */
#define A3XX_CP_PFP_UCODE_ADDR 0x1C9
#define A3XX_CP_PFP_UCODE_DATA 0x1CA
@@ -160,6 +164,7 @@
#define A3XX_VPC_VPC_DEBUG_RAM_READ 0xE62
#define A3XX_UCHE_CACHE_MODE_CONTROL_REG 0xE82
#define A3XX_UCHE_CACHE_INVALIDATE0_REG 0xEA0
+#define A3XX_SP_PERFCOUNTER7_SELECT 0xECB
#define A3XX_GRAS_CL_CLIP_CNTL 0x2040
#define A3XX_GRAS_CL_GB_CLIP_ADJ 0x2044
#define A3XX_GRAS_CL_VPORT_XOFFSET 0x2048
@@ -526,6 +531,13 @@
#define RBBM_BLOCK_ID_MARB_3 0x2b
/* RBBM_CLOCK_CTL default value */
-#define A3XX_RBBM_CLOCK_CTL_DEFAULT 0xBFFFFFFF
+#define A305_RBBM_CLOCK_CTL_DEFAULT 0xAAAAAAAA
+#define A320_RBBM_CLOCK_CTL_DEFAULT 0xBFFFFFFF
+#define A330_RBBM_CLOCK_CTL_DEFAULT 0xBFFCFFFF
+
+#define A330_RBBM_GPR0_CTL_DEFAULT 0x00000000
+
+/* COUNTABLE FOR SP PERFCOUNTER */
+#define SP_FS_FULL_ALU_INSTRUCTIONS 0x0E
#endif
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index cf7f3ce..930d233 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2002,2007-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -127,6 +127,8 @@
REG_CP_IB1_BUFSZ,
REG_CP_IB2_BASE,
REG_CP_IB2_BUFSZ,
+ 0,
+ 0
};
const unsigned int hang_detect_regs_count = ARRAY_SIZE(hang_detect_regs);
@@ -847,15 +849,15 @@
for_each_child_of_node(node, child)
count++;
- info->core_param.num_freq = count;
+ info->power_param.num_freq = count;
- info->freq_tbl = kzalloc(info->core_param.num_freq *
+ info->freq_tbl = kzalloc(info->power_param.num_freq *
sizeof(struct msm_dcvs_freq_entry),
GFP_KERNEL);
if (info->freq_tbl == NULL) {
KGSL_CORE_ERR("kzalloc(%d) failed\n",
- info->core_param.num_freq *
+ info->power_param.num_freq *
sizeof(struct msm_dcvs_freq_entry));
ret = -ENOMEM;
goto err;
@@ -867,7 +869,7 @@
if (adreno_of_read_property(child, "reg", &index))
goto err;
- if (index >= info->core_param.num_freq) {
+ if (index >= info->power_param.num_freq) {
KGSL_CORE_ERR("DCVS freq entry %d is out of range\n",
index);
continue;
@@ -877,43 +879,109 @@
&info->freq_tbl[index].freq))
goto err;
- if (adreno_of_read_property(child, "qcom,idle-energy",
- &info->freq_tbl[index].idle_energy))
- info->freq_tbl[index].idle_energy = 0;
+ if (adreno_of_read_property(child, "qcom,voltage",
+ &info->freq_tbl[index].voltage))
+ info->freq_tbl[index].voltage = 0;
- if (adreno_of_read_property(child, "qcom,active-energy",
- &info->freq_tbl[index].active_energy))
- info->freq_tbl[index].active_energy = 0;
+ if (adreno_of_read_property(child, "qcom,is_trans_level",
+ &info->freq_tbl[index].is_trans_level))
+ info->freq_tbl[index].is_trans_level = 0;
+
+ if (adreno_of_read_property(child, "qcom,active-energy-offset",
+ &info->freq_tbl[index].active_energy_offset))
+ info->freq_tbl[index].active_energy_offset = 0;
+
+ if (adreno_of_read_property(child, "qcom,leakage-energy-offset",
+ &info->freq_tbl[index].leakage_energy_offset))
+ info->freq_tbl[index].leakage_energy_offset = 0;
}
- if (adreno_of_read_property(node, "qcom,core-max-time-us",
- &info->core_param.max_time_us))
+ if (adreno_of_read_property(node, "qcom,num-cores", &info->num_cores))
goto err;
- if (adreno_of_read_property(node, "qcom,algo-slack-time-us",
- &info->algo_param.slack_time_us))
+ info->sensors = kzalloc(info->num_cores *
+ sizeof(int),
+ GFP_KERNEL);
+
+ for (count = 0; count < info->num_cores; count++) {
+ if (adreno_of_read_property(node, "qcom,sensors",
+ &(info->sensors[count])))
+ goto err;
+ }
+
+ if (adreno_of_read_property(node, "qcom,core-core-type",
+ &info->core_param.core_type))
goto err;
if (adreno_of_read_property(node, "qcom,algo-disable-pc-threshold",
&info->algo_param.disable_pc_threshold))
goto err;
-
- if (adreno_of_read_property(node, "qcom,algo-ss-window-size",
- &info->algo_param.ss_window_size))
+ if (adreno_of_read_property(node, "qcom,algo-em-win-size-min-us",
+ &info->algo_param.em_win_size_min_us))
goto err;
-
- if (adreno_of_read_property(node, "qcom,algo-ss-util-pct",
- &info->algo_param.ss_util_pct))
+ if (adreno_of_read_property(node, "qcom,algo-em-win-size-max-us",
+ &info->algo_param.em_win_size_max_us))
goto err;
-
if (adreno_of_read_property(node, "qcom,algo-em-max-util-pct",
&info->algo_param.em_max_util_pct))
goto err;
-
+ if (adreno_of_read_property(node, "qcom,algo-group-id",
+ &info->algo_param.group_id))
+ goto err;
+ if (adreno_of_read_property(node, "qcom,algo-max-freq-chg-time-us",
+ &info->algo_param.max_freq_chg_time_us))
+ goto err;
+ if (adreno_of_read_property(node, "qcom,algo-slack-mode-dynamic",
+ &info->algo_param.slack_mode_dynamic))
+ goto err;
+ if (adreno_of_read_property(node, "qcom,algo-slack-weight-thresh-pct",
+ &info->algo_param.slack_weight_thresh_pct))
+ goto err;
+ if (adreno_of_read_property(node, "qcom,algo-slack-time-min-us",
+ &info->algo_param.slack_time_min_us))
+ goto err;
+ if (adreno_of_read_property(node, "qcom,algo-slack-time-max-us",
+ &info->algo_param.slack_time_max_us))
+ goto err;
+ if (adreno_of_read_property(node, "qcom,algo-ss-win-size-min-us",
+ &info->algo_param.ss_win_size_min_us))
+ goto err;
+ if (adreno_of_read_property(node, "qcom,algo-ss-win-size-max-us",
+ &info->algo_param.ss_win_size_max_us))
+ goto err;
+ if (adreno_of_read_property(node, "qcom,algo-ss-util-pct",
+ &info->algo_param.ss_util_pct))
+ goto err;
if (adreno_of_read_property(node, "qcom,algo-ss-iobusy-conv",
&info->algo_param.ss_iobusy_conv))
goto err;
+ if (adreno_of_read_property(node, "qcom,energy-active-coeff-a",
+ &info->energy_coeffs.active_coeff_a))
+ goto err;
+ if (adreno_of_read_property(node, "qcom,energy-active-coeff-b",
+ &info->energy_coeffs.active_coeff_b))
+ goto err;
+ if (adreno_of_read_property(node, "qcom,energy-active-coeff-c",
+ &info->energy_coeffs.active_coeff_c))
+ goto err;
+ if (adreno_of_read_property(node, "qcom,energy-leakage-coeff-a",
+ &info->energy_coeffs.leakage_coeff_a))
+ goto err;
+ if (adreno_of_read_property(node, "qcom,energy-leakage-coeff-b",
+ &info->energy_coeffs.leakage_coeff_b))
+ goto err;
+ if (adreno_of_read_property(node, "qcom,energy-leakage-coeff-c",
+ &info->energy_coeffs.leakage_coeff_c))
+ goto err;
+ if (adreno_of_read_property(node, "qcom,energy-leakage-coeff-d",
+ &info->energy_coeffs.leakage_coeff_d))
+ goto err;
+
+ if (adreno_of_read_property(node, "qcom,power-current-temp",
+ &info->power_param.current_temp))
+ goto err;
+
return info;
err:
@@ -1039,6 +1107,9 @@
&pdata->nap_allowed))
pdata->nap_allowed = 1;
+ pdata->strtstp_sleepwake = of_property_read_bool(pdev->dev.of_node,
+ "qcom,strtstp-sleepwake");
+
if (adreno_of_read_property(pdev->dev.of_node, "qcom,clk-map",
&pdata->clk_map))
goto err;
@@ -1233,6 +1304,12 @@
*/
hang_detect_regs[0] = adreno_dev->gpudev->reg_rbbm_status;
+ /* Add A3XX specific registers for hang detection */
+ if (adreno_is_a3xx(adreno_dev)) {
+ hang_detect_regs[6] = A3XX_RBBM_PERFCTR_SP_7_LO;
+ hang_detect_regs[7] = A3XX_RBBM_PERFCTR_SP_7_HI;
+ }
+
status = kgsl_mmu_start(device);
if (status)
goto error_clk_off;
@@ -1390,6 +1467,7 @@
ret = -ENOMEM;
goto done;
}
+ rec_data->fault = device->mmu.fault;
done:
if (ret) {
@@ -1452,9 +1530,9 @@
goto done;
}
- /* Do not try the bad caommands if recovery has failed bad commands
- * once already */
- if (!try_bad_commands)
+ /* Do not try the bad commands if recovery has failed bad commands
+ * once already or if hang is due to a fault */
+ if (!try_bad_commands || rec_data->fault)
rec_data->bad_rb_size = 0;
if (rec_data->bad_rb_size) {
@@ -2155,7 +2233,14 @@
if (!adreno_dev->fast_hang_detect)
return 0;
+ if (device->ftbl->isidle(device))
+ return 0;
+
for (i = 0; i < hang_detect_regs_count; i++) {
+
+ if (hang_detect_regs[i] == 0)
+ continue;
+
adreno_regread(device, hang_detect_regs[i],
&curr_reg_val[i]);
if (curr_reg_val[i] != prev_reg_val[i]) {
@@ -2179,12 +2264,15 @@
static uint io_cnt;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+ struct adreno_context *adreno_ctx = context ? context->devctxt : NULL;
int retries = 0;
unsigned int ts_issued;
unsigned int context_id = _get_context_id(context);
unsigned int time_elapsed = 0;
unsigned int prev_reg_val[hang_detect_regs_count];
unsigned int wait;
+ unsigned int retry_ts_cmp = 0;
+ unsigned int retry_ts_cmp_msecs = KGSL_SYNCOBJ_SERVER_TIMEOUT;
memset(prev_reg_val, 0, sizeof(prev_reg_val));
@@ -2194,12 +2282,21 @@
if (msecs == KGSL_TIMEOUT_DEFAULT)
msecs = adreno_dev->wait_timeout;
+ /*
+ * With user generated ts, if this check fails perform this check
+ * again after 'retry_ts_cmp_msecs' milliseconds.
+ */
if (timestamp_cmp(timestamp, ts_issued) > 0) {
- KGSL_DRV_ERR(device, "Cannot wait for invalid ts <%d:0x%x>, "
- "last issued ts <%d:0x%x>\n",
- context_id, timestamp, context_id, ts_issued);
- status = -EINVAL;
- goto done;
+ if (adreno_ctx == NULL ||
+ !(adreno_ctx->flags & CTXT_FLAGS_USER_GENERATED_TS)) {
+ KGSL_DRV_ERR(device,
+ "Cannot wait for invalid ts <%d:0x%x>, "
+ "last issued ts <%d:0x%x>\n",
+ context_id, timestamp, context_id, ts_issued);
+ status = -EINVAL;
+ goto done;
+ } else
+ retry_ts_cmp = 1;
}
/*
@@ -2269,7 +2366,22 @@
time_elapsed += wait;
wait = KGSL_TIMEOUT_PART;
- retries++;
+ if (!retry_ts_cmp)
+ retries++;
+ else if (time_elapsed >= retry_ts_cmp_msecs) {
+ ts_issued =
+ adreno_dev->ringbuffer.timestamp[context_id];
+ if (timestamp_cmp(timestamp, ts_issued) > 0) {
+ KGSL_DRV_ERR(device,
+ "Cannot wait for user-generated ts <%d:0x%x>, "
+ "not submitted within server timeout period. "
+ "last issued ts <%d:0x%x>\n",
+ context_id, timestamp, context_id, ts_issued);
+ status = -EINVAL;
+ goto done;
+ }
+ retry_ts_cmp = 0;
+ }
} while (!msecs || time_elapsed < msecs);
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 66402fe..bec19e2 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -33,7 +33,7 @@
/* Flags to control command packet settings */
#define KGSL_CMD_FLAGS_NONE 0x00000000
#define KGSL_CMD_FLAGS_PMODE 0x00000001
-#define KGSL_CMD_FLAGS_DUMMY_INTR_CMD 0x00000002
+#define KGSL_CMD_FLAGS_INTERNAL_ISSUE 0x00000002
/* Command identifiers */
#define KGSL_CONTEXT_TO_MEM_IDENTIFIER 0x2EADBEEF
@@ -138,6 +138,7 @@
* bad_rb_size - Number of valid dwords in bad_rb_buffer
* @last_valid_ctx_id - The last context from which commands were placed in
* ringbuffer before the GPU hung
+ * @fault - Indicates whether the hang was caused due to a pagefault
*/
struct adreno_recovery_data {
unsigned int ib1;
@@ -148,6 +149,7 @@
unsigned int *bad_rb_buffer;
unsigned int bad_rb_size;
unsigned int last_valid_ctx_id;
+ int fault;
};
extern struct adreno_gpudev adreno_a2xx_gpudev;
@@ -179,6 +181,8 @@
unsigned int value);
int adreno_dump(struct kgsl_device *device, int manual);
+unsigned int adreno_a3xx_rbbm_clock_ctl_default(struct adreno_device
+ *adreno_dev);
struct kgsl_memdesc *adreno_find_region(struct kgsl_device *device,
unsigned int pt_base,
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index e5959c1..4c7534c 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -433,6 +433,19 @@
tmp_ctx.cmd = cmd;
}
+unsigned int adreno_a3xx_rbbm_clock_ctl_default(struct adreno_device
+ *adreno_dev)
+{
+ if (adreno_is_a305(adreno_dev))
+ return A305_RBBM_CLOCK_CTL_DEFAULT;
+ else if (adreno_is_a320(adreno_dev))
+ return A320_RBBM_CLOCK_CTL_DEFAULT;
+ else if (adreno_is_a330(adreno_dev))
+ return A330_RBBM_CLOCK_CTL_DEFAULT;
+
+ BUG_ON(1);
+}
+
/* Copy GMEM contents to system memory shadow. */
static unsigned int *build_gmem2sys_cmds(struct adreno_device *adreno_dev,
struct adreno_context *drawctxt,
@@ -442,7 +455,7 @@
unsigned int *start = cmds;
*cmds++ = cp_type0_packet(A3XX_RBBM_CLOCK_CTL, 1);
- *cmds++ = A3XX_RBBM_CLOCK_CTL_DEFAULT;
+ *cmds++ = adreno_a3xx_rbbm_clock_ctl_default(adreno_dev);
*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
*cmds++ = CP_REG(A3XX_RB_MODE_CONTROL);
@@ -1238,7 +1251,7 @@
unsigned int *start = cmds;
*cmds++ = cp_type0_packet(A3XX_RBBM_CLOCK_CTL, 1);
- *cmds++ = A3XX_RBBM_CLOCK_CTL_DEFAULT;
+ *cmds++ = adreno_a3xx_rbbm_clock_ctl_default(adreno_dev);
*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5);
*cmds++ = CP_REG(A3XX_HLSQ_CONTROL_0_REG);
@@ -2826,13 +2839,28 @@
/* Enable Clock gating */
adreno_regwrite(device, A3XX_RBBM_CLOCK_CTL,
- A3XX_RBBM_CLOCK_CTL_DEFAULT);
+ adreno_a3xx_rbbm_clock_ctl_default(adreno_dev));
+
+ if (adreno_is_a330(adreno_dev))
+ adreno_regwrite(device, A3XX_RBBM_GPR0_CTL,
+ A330_RBBM_GPR0_CTL_DEFAULT);
/* Set the OCMEM base address for A330 */
if (adreno_is_a330(adreno_dev)) {
adreno_regwrite(device, A3XX_RB_GMEM_BASE_ADDR,
(unsigned int)(adreno_dev->ocmem_base >> 14));
}
+
+ /* Turn on performance counters */
+ adreno_regwrite(device, A3XX_RBBM_PERFCTR_CTL, 0x01);
+
+ /*
+ * Set SP perfcounter 7 to count SP_FS_FULL_ALU_INSTRUCTIONS
+ * we will use this to augment our hang detection
+ */
+
+ adreno_regwrite(device, A3XX_SP_PERFCOUNTER7_SELECT,
+ SP_FS_FULL_ALU_INSTRUCTIONS);
}
/* Defined in adreno_a3xx_snapshot.c */
diff --git a/drivers/gpu/msm/adreno_a3xx_snapshot.c b/drivers/gpu/msm/adreno_a3xx_snapshot.c
index d49fc23..a410445 100644
--- a/drivers/gpu/msm/adreno_a3xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a3xx_snapshot.c
@@ -383,7 +383,7 @@
/* Enable Clock gating */
adreno_regwrite(device, A3XX_RBBM_CLOCK_CTL,
- A3XX_RBBM_CLOCK_CTL_DEFAULT);
+ adreno_a3xx_rbbm_clock_ctl_default(adreno_dev));
return snapshot;
}
diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c
index 7cbc7a8..a107a27 100644
--- a/drivers/gpu/msm/adreno_drawctxt.c
+++ b/drivers/gpu/msm/adreno_drawctxt.c
@@ -169,6 +169,14 @@
if (flags & KGSL_CONTEXT_PER_CONTEXT_TS)
drawctxt->flags |= CTXT_FLAGS_PER_CONTEXT_TS;
+ if (flags & KGSL_CONTEXT_USER_GENERATED_TS) {
+ if (!(flags & KGSL_CONTEXT_PER_CONTEXT_TS)) {
+ ret = -EINVAL;
+ goto err;
+ }
+ drawctxt->flags |= CTXT_FLAGS_USER_GENERATED_TS;
+ }
+
ret = adreno_dev->gpudev->ctxt_create(adreno_dev, drawctxt);
if (ret)
goto err;
diff --git a/drivers/gpu/msm/adreno_drawctxt.h b/drivers/gpu/msm/adreno_drawctxt.h
index 034d6e9..58e4791 100644
--- a/drivers/gpu/msm/adreno_drawctxt.h
+++ b/drivers/gpu/msm/adreno_drawctxt.h
@@ -48,6 +48,8 @@
#define CTXT_FLAGS_GPU_HANG_RECOVERED BIT(12)
/* Context is being destroyed so dont save it */
#define CTXT_FLAGS_BEING_DESTROYED BIT(13)
+/* User mode generated timestamps enabled */
+#define CTXT_FLAGS_USER_GENERATED_TS BIT(14)
struct kgsl_device;
struct adreno_device;
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index 15ffa9e..da9daf7 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -487,11 +487,10 @@
adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb,
struct adreno_context *context,
unsigned int flags, unsigned int *cmds,
- int sizedwords)
+ int sizedwords, uint32_t timestamp)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(rb->device);
unsigned int *ringcmds;
- unsigned int timestamp;
unsigned int total_sizedwords = sizedwords;
unsigned int i;
unsigned int rcmd_gpu;
@@ -506,19 +505,38 @@
if (context && context->flags & CTXT_FLAGS_PER_CONTEXT_TS)
context_id = context->id;
+ if ((context->flags & CTXT_FLAGS_USER_GENERATED_TS) &&
+ (!(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE))) {
+ if (timestamp_cmp(rb->timestamp[context_id],
+ timestamp) >= 0) {
+ KGSL_DRV_ERR(rb->device,
+ "Invalid user generated ts <%d:0x%x>, "
+ "less than last issued ts <%d:0x%x>\n",
+ context_id, timestamp, context_id,
+ rb->timestamp[context_id]);
+ return -ERANGE;
+ }
+ }
+
/* reserve space to temporarily turn off protected mode
* error checking if needed
*/
total_sizedwords += flags & KGSL_CMD_FLAGS_PMODE ? 4 : 0;
/* 2 dwords to store the start of command sequence */
total_sizedwords += 2;
- total_sizedwords += context ? 7 : 0;
+ /*
+ * Add CP_COND_EXEC commands to generate CP_INTERRUPT only
+ * for submissions from userspace.
+ */
+ total_sizedwords += (context &&
+ !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) ? 7 : 0;
if (adreno_is_a3xx(adreno_dev))
total_sizedwords += 7;
total_sizedwords += 2; /* scratchpad ts for recovery */
- if (context && context->flags & CTXT_FLAGS_PER_CONTEXT_TS) {
+ if (context && context->flags & CTXT_FLAGS_PER_CONTEXT_TS &&
+ !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) {
total_sizedwords += 3; /* sop timestamp */
total_sizedwords += 4; /* eop timestamp */
total_sizedwords += 3; /* global timestamp without cache
@@ -564,10 +582,13 @@
/* always increment the global timestamp. once. */
rb->timestamp[KGSL_MEMSTORE_GLOBAL]++;
- if (context && !(flags & KGSL_CMD_FLAGS_DUMMY_INTR_CMD)) {
+ /* Do not update context's timestamp for internal submissions */
+ if (context && !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) {
if (context_id == KGSL_MEMSTORE_GLOBAL)
rb->timestamp[context->id] =
rb->timestamp[KGSL_MEMSTORE_GLOBAL];
+ else if (context->flags & CTXT_FLAGS_USER_GENERATED_TS)
+ rb->timestamp[context_id] = timestamp;
else
rb->timestamp[context_id]++;
}
@@ -591,7 +612,8 @@
GSL_RB_WRITE(ringcmds, rcmd_gpu, 0x00);
}
- if (context && context->flags & CTXT_FLAGS_PER_CONTEXT_TS) {
+ if (context && context->flags & CTXT_FLAGS_PER_CONTEXT_TS
+ && !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) {
/* start-of-pipeline timestamp */
GSL_RB_WRITE(ringcmds, rcmd_gpu,
cp_type3_packet(CP_MEM_WRITE, 2));
@@ -619,11 +641,13 @@
cp_type3_packet(CP_EVENT_WRITE, 3));
GSL_RB_WRITE(ringcmds, rcmd_gpu, CACHE_FLUSH_TS);
GSL_RB_WRITE(ringcmds, rcmd_gpu, (gpuaddr +
- KGSL_MEMSTORE_OFFSET(context_id, eoptimestamp)));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, rb->timestamp[context_id]);
+ KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
+ eoptimestamp)));
+ GSL_RB_WRITE(ringcmds, rcmd_gpu,
+ rb->timestamp[KGSL_MEMSTORE_GLOBAL]);
}
- if (context) {
+ if (context && !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) {
/* Conditional execution based on memory values */
GSL_RB_WRITE(ringcmds, rcmd_gpu,
cp_type3_packet(CP_COND_EXEC, 4));
@@ -675,8 +699,8 @@
device->state & KGSL_STATE_HUNG)
return;
- adreno_ringbuffer_addcmds(rb, a_ctxt, KGSL_CMD_FLAGS_DUMMY_INTR_CMD,
- cmds, sizedwords);
+ adreno_ringbuffer_addcmds(rb, a_ctxt, KGSL_CMD_FLAGS_INTERNAL_ISSUE,
+ cmds, sizedwords, 0);
}
unsigned int
@@ -692,7 +716,11 @@
if (device->state & KGSL_STATE_HUNG)
return kgsl_readtimestamp(device, KGSL_MEMSTORE_GLOBAL,
KGSL_TIMESTAMP_RETIRED);
- return adreno_ringbuffer_addcmds(rb, drawctxt, flags, cmds, sizedwords);
+
+ flags |= KGSL_CMD_FLAGS_INTERNAL_ISSUE;
+
+ return adreno_ringbuffer_addcmds(rb, drawctxt, flags, cmds,
+ sizedwords, 0);
}
static bool _parse_ibs(struct kgsl_device_private *dev_priv, uint gpuaddr,
@@ -902,6 +930,7 @@
unsigned int i;
struct adreno_context *drawctxt;
unsigned int start_index = 0;
+ int ret;
if (device->state & KGSL_STATE_HUNG)
return -EBUSY;
@@ -948,9 +977,15 @@
if (unlikely(adreno_dev->ib_check_level >= 1 &&
!_parse_ibs(dev_priv, ibdesc[i].gpuaddr,
ibdesc[i].sizedwords))) {
- kfree(link);
- return -EINVAL;
+ ret = -EINVAL;
+ goto done;
}
+
+ if (ibdesc[i].sizedwords == 0) {
+ ret = -EINVAL;
+ goto done;
+ }
+
*cmds++ = CP_HDR_INDIRECT_BUFFER_PFD;
*cmds++ = ibdesc[i].gpuaddr;
*cmds++ = ibdesc[i].sizedwords;
@@ -966,13 +1001,13 @@
adreno_drawctxt_switch(adreno_dev, drawctxt, flags);
*timestamp = adreno_ringbuffer_addcmds(&adreno_dev->ringbuffer,
- drawctxt, 0,
- &link[0], (cmds - link));
+ drawctxt,
+ 0,
+ &link[0], (cmds - link), *timestamp);
KGSL_CMD_INFO(device, "<%d:0x%x> g %08x numibs %d\n",
context->id, *timestamp, (unsigned int)ibdesc, numibs);
- kfree(link);
#ifdef CONFIG_MSM_KGSL_CFF_DUMP
/*
@@ -982,13 +1017,16 @@
*/
adreno_idle(device);
#endif
+
/* If context hung and recovered then return error so that the
* application may handle it */
- if (drawctxt->flags & CTXT_FLAGS_GPU_HANG_RECOVERED)
- return -EDEADLK;
- else
- return 0;
+ ret = (drawctxt->flags & CTXT_FLAGS_GPU_HANG_RECOVERED) ?
+ -EDEADLK : 0;
+
+done:
+ kfree(link);
+ return ret;
}
static int _find_start_of_cmd_seq(struct adreno_ringbuffer *rb,
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 664d519..afe384b 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -722,6 +722,7 @@
list_add(&private->list, &kgsl_driver.process_list);
kgsl_process_init_sysfs(private);
+ kgsl_process_init_debugfs(private);
out:
mutex_unlock(&kgsl_driver.process_mutex);
@@ -744,6 +745,7 @@
goto unlock;
kgsl_process_uninit_sysfs(private);
+ debugfs_remove_recursive(private->debug_root);
list_del(&private->list);
@@ -1080,6 +1082,7 @@
unsigned int cmd, void *data)
{
int result = 0;
+ int i = 0;
struct kgsl_ringbuffer_issueibcmds *param = data;
struct kgsl_ibdesc *ibdesc;
struct kgsl_context *context;
@@ -1141,6 +1144,16 @@
param->numibs = 1;
}
+ for (i = 0; i < param->numibs; i++) {
+ if (!kgsl_mmu_gpuaddr_in_range(ibdesc[i].gpuaddr)) {
+ result = -ERANGE;
+ KGSL_DRV_ERR(dev_priv->device,
+ "invalid ib base GPU virtual addr %x\n",
+ ibdesc[i].gpuaddr);
+ goto free_ibdesc;
+ }
+ }
+
result = dev_priv->device->ftbl->issueibcmds(dev_priv,
context,
ibdesc,
@@ -1468,6 +1481,8 @@
return -ENOMEM;
memdesc->sglen = sglen;
+ memdesc->sglen_alloc = sglen;
+
sg_init_table(memdesc->sg, sglen);
spin_lock(¤t->mm->page_table_lock);
@@ -1632,11 +1647,16 @@
#endif
static int kgsl_setup_ion(struct kgsl_mem_entry *entry,
- struct kgsl_pagetable *pagetable, int fd)
+ struct kgsl_pagetable *pagetable, void *data)
{
struct ion_handle *handle;
struct scatterlist *s;
struct sg_table *sg_table;
+ struct kgsl_map_user_mem *param = data;
+ int fd = param->fd;
+
+ if (!param->len)
+ return -EINVAL;
if (IS_ERR_OR_NULL(kgsl_ion_client))
return -ENODEV;
@@ -1741,8 +1761,7 @@
entry->memtype = KGSL_MEM_ENTRY_ASHMEM;
break;
case KGSL_USER_MEM_TYPE_ION:
- result = kgsl_setup_ion(entry, private->pagetable,
- param->fd);
+ result = kgsl_setup_ion(entry, private->pagetable, data);
break;
default:
KGSL_CORE_ERR("Invalid memory type: %x\n", memtype);
@@ -1752,6 +1771,13 @@
if (result)
goto error;
+ entry->memdesc.priv |= param->flags & KGSL_MEMTYPE_MASK;
+
+ if (entry->memdesc.size >= SZ_1M)
+ entry->memdesc.priv |= ilog2(SZ_1M) << KGSL_MEMALIGN_SHIFT;
+ else if (entry->memdesc.size >= SZ_64K)
+ entry->memdesc.priv |= ilog2(SZ_64K) << KGSL_MEMALIGN_SHIFT;
+
result = kgsl_mmu_map(private->pagetable,
&entry->memdesc,
GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index 416eda9..2861117 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -135,7 +135,8 @@
unsigned int size;
unsigned int priv;
struct scatterlist *sg;
- unsigned int sglen;
+ unsigned int sglen; /* Active entries in the sglist */
+ unsigned int sglen_alloc; /* Allocated entries in the sglist */
struct kgsl_memdesc_ops *ops;
int flags;
};
@@ -182,6 +183,8 @@
struct kgsl_process_private *private, unsigned int gpuaddr,
size_t size);
+void kgsl_get_memory_usage(char *str, size_t len, unsigned int memflags);
+
int kgsl_add_event(struct kgsl_device *device, u32 id, u32 ts,
void (*cb)(struct kgsl_device *, void *, u32, u32), void *priv,
void *owner);
diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c
index 545d2b3..b49c260 100644
--- a/drivers/gpu/msm/kgsl_debugfs.c
+++ b/drivers/gpu/msm/kgsl_debugfs.c
@@ -16,6 +16,7 @@
#include "kgsl.h"
#include "kgsl_device.h"
+#include "kgsl_sharedmem.h"
/*default log levels is error for everything*/
#define KGSL_LOG_LEVEL_DEFAULT 3
@@ -23,6 +24,7 @@
struct dentry *kgsl_debugfs_dir;
static struct dentry *pm_d_debugfs;
+struct dentry *proc_d_debugfs;
static int pm_dump_set(void *data, u64 val)
{
@@ -146,9 +148,90 @@
}
+static const char * const memtype_strings[] = {
+ "gpumem",
+ "pmem",
+ "ashmem",
+ "usermap",
+ "ion",
+};
+
+static const char *memtype_str(int memtype)
+{
+ if (memtype < ARRAY_SIZE(memtype_strings))
+ return memtype_strings[memtype];
+ return "unknown";
+}
+
+static int process_mem_print(struct seq_file *s, void *unused)
+{
+ struct kgsl_mem_entry *entry;
+ struct rb_node *node;
+ struct kgsl_process_private *private = s->private;
+ char flags[4];
+ char usage[16];
+ unsigned int align;
+
+ spin_lock(&private->mem_lock);
+ seq_printf(s, "%8s %8s %5s %10s %16s %5s\n",
+ "gpuaddr", "size", "flags", "type", "usage", "sglen");
+ for (node = rb_first(&private->mem_rb); node; node = rb_next(node)) {
+ struct kgsl_memdesc *m;
+
+ entry = rb_entry(node, struct kgsl_mem_entry, node);
+ m = &entry->memdesc;
+
+ flags[0] = m->priv & KGSL_MEMFLAGS_GLOBAL ? 'g' : '-';
+ flags[1] = m->priv & KGSL_MEMFLAGS_GPUREADONLY ? 'r' : '-';
+
+ align = (m->priv & KGSL_MEMALIGN_MASK) >> KGSL_MEMALIGN_SHIFT;
+ if (align >= ilog2(SZ_1M))
+ flags[2] = 'L';
+ else if (align >= ilog2(SZ_64K))
+ flags[2] = 'l';
+ else
+ flags[2] = '-';
+
+ flags[3] = '\0';
+
+ kgsl_get_memory_usage(usage, sizeof(usage), m->priv);
+
+ seq_printf(s, "%08x %8d %5s %10s %16s %5d\n",
+ m->gpuaddr, m->size, flags,
+ memtype_str(entry->memtype), usage, m->sglen);
+ }
+ spin_unlock(&private->mem_lock);
+ return 0;
+}
+
+static int process_mem_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, process_mem_print, inode->i_private);
+}
+
+static const struct file_operations process_mem_fops = {
+ .open = process_mem_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+void
+kgsl_process_init_debugfs(struct kgsl_process_private *private)
+{
+ unsigned char name[16];
+
+ snprintf(name, sizeof(name), "%d", private->pid);
+
+ private->debug_root = debugfs_create_dir(name, proc_d_debugfs);
+ debugfs_create_file("mem", 0400, private->debug_root, private,
+ &process_mem_fops);
+}
+
void kgsl_core_debugfs_init(void)
{
kgsl_debugfs_dir = debugfs_create_dir("kgsl", 0);
+ proc_d_debugfs = debugfs_create_dir("proc", kgsl_debugfs_dir);
}
void kgsl_core_debugfs_close(void)
diff --git a/drivers/gpu/msm/kgsl_debugfs.h b/drivers/gpu/msm/kgsl_debugfs.h
index 5e10988..898c4e9 100644
--- a/drivers/gpu/msm/kgsl_debugfs.h
+++ b/drivers/gpu/msm/kgsl_debugfs.h
@@ -15,6 +15,7 @@
#define _KGSL_DEBUGFS_H
struct kgsl_device;
+struct kgsl_process_private;
#ifdef CONFIG_DEBUG_FS
void kgsl_core_debugfs_init(void);
@@ -28,11 +29,16 @@
return kgsl_debugfs_dir;
}
+int kgsl_process_init_debugfs(struct kgsl_process_private *);
#else
static inline void kgsl_core_debugfs_init(void) { }
static inline void kgsl_device_debugfs_init(struct kgsl_device *device) { }
static inline void kgsl_core_debugfs_close(void) { }
static inline struct dentry *kgsl_get_debugfs_dir(void) { return NULL; }
+static inline int kgsl_process_init_debugfs(struct kgsl_process_private *)
+{
+ return 0;
+}
#endif
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index dc597f5..4394118 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -251,6 +251,7 @@
struct kgsl_pagetable *pagetable;
struct list_head list;
struct kobject kobj;
+ struct dentry *debug_root;
struct {
unsigned int cur;
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 87e8746..62108f2 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -34,6 +34,7 @@
{ 0x14, 0x0003FFFF, 14 }, /* TTBR1 */
{ 0x20, 0, 0 }, /* FSR */
{ 0x800, 0, 0 }, /* TLBIALL */
+ { 0x820, 0, 0 }, /* RESUME */
};
static struct kgsl_iommu_register_list kgsl_iommuv2_reg[KGSL_IOMMU_REG_MAX] = {
@@ -41,7 +42,8 @@
{ 0x20, 0x00FFFFFF, 14 }, /* TTBR0 */
{ 0x28, 0x00FFFFFF, 14 }, /* TTBR1 */
{ 0x58, 0, 0 }, /* FSR */
- { 0x618, 0, 0 } /* TLBIALL */
+ { 0x618, 0, 0 }, /* TLBIALL */
+ { 0x008, 0, 0 } /* RESUME */
};
static int get_iommu_unit(struct device *dev, struct kgsl_mmu **mmu_out,
@@ -124,9 +126,19 @@
KGSL_MEM_CRIT(iommu_dev->kgsldev, "context = %d FSR = %X\n",
iommu_dev->ctx_id, fsr);
+ mmu->fault = 1;
+ iommu_dev->fault = 1;
+
trace_kgsl_mmu_pagefault(iommu_dev->kgsldev, addr,
kgsl_mmu_get_ptname_from_ptbase(mmu, ptbase), 0);
+ /*
+ * We do not want the h/w to resume fetching data from an iommu unit
+ * that has faulted, this is better for debugging as it will stall
+ * the GPU and trigger a snapshot. To stall the transaction return
+ * EBUSY error.
+ */
+ ret = -EBUSY;
done:
return ret;
}
@@ -859,12 +871,13 @@
*/
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++)
+ for (j = 0; j < iommu_unit->dev_count; j++) {
iommu_unit->dev[j].pt_lsb = KGSL_IOMMMU_PT_LSB(iommu,
KGSL_IOMMU_GET_CTX_REG(iommu,
iommu_unit,
iommu_unit->dev[j].ctx_id,
TTBR0));
+ }
}
kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
@@ -923,19 +936,22 @@
unsigned int iommu_virt_addr;
struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
int size = kgsl_sg_size(memdesc->sg, memdesc->sglen);
+ unsigned int iommu_flags = IOMMU_READ;
BUG_ON(NULL == iommu_pt);
+ if (protflags & GSL_PT_PAGE_WV)
+ iommu_flags |= IOMMU_WRITE;
iommu_virt_addr = memdesc->gpuaddr;
ret = iommu_map_range(iommu_pt->domain, iommu_virt_addr, memdesc->sg,
- size, (IOMMU_READ | IOMMU_WRITE));
+ size, iommu_flags);
if (ret) {
KGSL_CORE_ERR("iommu_map_range(%p, %x, %p, %d, %d) "
"failed with err: %d\n", iommu_pt->domain,
iommu_virt_addr, memdesc->sg, size,
- (IOMMU_READ | IOMMU_WRITE), ret);
+ iommu_flags, ret);
return ret;
}
@@ -945,6 +961,7 @@
static void kgsl_iommu_stop(struct kgsl_mmu *mmu)
{
struct kgsl_iommu *iommu = mmu->priv;
+ int i, j;
/*
* stop device mmu
*
@@ -957,8 +974,25 @@
mmu->hwpagetable = NULL;
mmu->flags &= ~KGSL_FLAGS_STARTED;
- }
+ if (mmu->fault) {
+ 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].fault) {
+ kgsl_iommu_enable_clk(mmu, j);
+ KGSL_IOMMU_SET_CTX_REG(iommu,
+ iommu_unit,
+ iommu_unit->dev[j].ctx_id,
+ RESUME, 1);
+ iommu_unit->dev[j].fault = 0;
+ }
+ }
+ }
+ mmu->fault = 0;
+ }
+ }
/* switch off MMU clocks and cancel any events it has queued */
iommu->clk_event_queued = false;
kgsl_cancel_events(mmu->device, mmu);
diff --git a/drivers/gpu/msm/kgsl_iommu.h b/drivers/gpu/msm/kgsl_iommu.h
index eafba7b..661b4f0 100644
--- a/drivers/gpu/msm/kgsl_iommu.h
+++ b/drivers/gpu/msm/kgsl_iommu.h
@@ -25,6 +25,7 @@
KGSL_IOMMU_CTX_TTBR1,
KGSL_IOMMU_CTX_FSR,
KGSL_IOMMU_CTX_TLBIALL,
+ KGSL_IOMMU_CTX_RESUME,
KGSL_IOMMU_REG_MAX
};
@@ -77,6 +78,8 @@
* @ctx_id: This iommu units context id. It can be either 0 or 1
* @clk_enabled: If set indicates that iommu clocks of this iommu context
* are on, else the clocks are off
+ * fault: Flag when set indicates that this iommu device has caused a page
+ * fault
*/
struct kgsl_iommu_device {
struct device *dev;
@@ -85,6 +88,7 @@
enum kgsl_iommu_context_id ctx_id;
bool clk_enabled;
struct kgsl_device *kgsldev;
+ int fault;
};
/*
diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c
index 0e1e100..dbb88ee 100644
--- a/drivers/gpu/msm/kgsl_mmu.c
+++ b/drivers/gpu/msm/kgsl_mmu.c
@@ -606,6 +606,7 @@
int ret;
struct gen_pool *pool;
int size;
+ int page_align = ilog2(PAGE_SIZE);
if (kgsl_mmu_type == KGSL_MMU_TYPE_NONE) {
if (memdesc->sglen == 1) {
@@ -630,7 +631,17 @@
/* Allocate from kgsl pool if it exists for global mappings */
pool = _get_pool(pagetable, memdesc->priv);
- memdesc->gpuaddr = gen_pool_alloc(pool, size);
+ /* Allocate aligned virtual addresses for iommu. This allows
+ * more efficient pagetable entries if the physical memory
+ * is also aligned. Don't do this for GPUMMU, because
+ * the address space is so small.
+ */
+ if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype() &&
+ (memdesc->priv & KGSL_MEMALIGN_MASK)) {
+ page_align = (memdesc->priv & KGSL_MEMALIGN_MASK)
+ >> KGSL_MEMALIGN_SHIFT;
+ }
+ memdesc->gpuaddr = gen_pool_alloc_aligned(pool, size, page_align);
if (memdesc->gpuaddr == 0) {
KGSL_CORE_ERR("gen_pool_alloc(%d) failed from pool: %s\n",
size,
@@ -729,7 +740,8 @@
return 0;
gpuaddr = memdesc->gpuaddr;
- memdesc->priv |= KGSL_MEMFLAGS_GLOBAL;
+ memdesc->priv |= KGSL_MEMFLAGS_GLOBAL
+ | (KGSL_MEMTYPE_KERNEL << KGSL_MEMTYPE_SHIFT);
result = kgsl_mmu_map(pagetable, memdesc, protflags);
if (result)
diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h
index 234629b..b8b9149 100644
--- a/drivers/gpu/msm/kgsl_mmu.h
+++ b/drivers/gpu/msm/kgsl_mmu.h
@@ -175,6 +175,7 @@
struct kgsl_pagetable *hwpagetable;
const struct kgsl_mmu_ops *mmu_ops;
void *priv;
+ int fault;
};
#include "kgsl_gpummu.h"
diff --git a/drivers/gpu/msm/kgsl_pwrscale_msm.c b/drivers/gpu/msm/kgsl_pwrscale_msm.c
index 879b381..b302bee 100644
--- a/drivers/gpu/msm/kgsl_pwrscale_msm.c
+++ b/drivers/gpu/msm/kgsl_pwrscale_msm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -20,21 +20,21 @@
#include "kgsl_trace.h"
struct msm_priv {
- struct kgsl_device *device;
- int enabled;
- int handle;
- unsigned int cur_freq;
- struct msm_dcvs_idle idle_source;
- struct msm_dcvs_freq freq_sink;
- struct msm_dcvs_core_info *core_info;
- int gpu_busy;
+ struct kgsl_device *device;
+ int enabled;
+ unsigned int cur_freq;
+ struct msm_dcvs_core_info *core_info;
+ int gpu_busy;
+ int dcvs_core_id;
};
-static int msm_idle_enable(struct msm_dcvs_idle *self,
- enum msm_core_control_event event)
+/* reference to be used in idle and freq callbacks */
+static struct msm_priv *the_msm_priv;
+
+static int msm_idle_enable(int type_core_num,
+ enum msm_core_control_event event)
{
- struct msm_priv *priv = container_of(self, struct msm_priv,
- idle_source);
+ struct msm_priv *priv = the_msm_priv;
switch (event) {
case MSM_DCVS_ENABLE_IDLE_PULSE:
@@ -53,12 +53,10 @@
/* Set the requested frequency if it is within 5MHz (delta) of a
* supported frequency.
*/
-static int msm_set_freq(struct msm_dcvs_freq *self,
- unsigned int freq)
+static int msm_set_freq(int core_num, unsigned int freq)
{
int i, delta = 5000000;
- struct msm_priv *priv = container_of(self, struct msm_priv,
- freq_sink);
+ struct msm_priv *priv = the_msm_priv;
struct kgsl_device *device = priv->device;
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
@@ -79,10 +77,10 @@
return priv->cur_freq / 1000;
}
-static unsigned int msm_get_freq(struct msm_dcvs_freq *self)
+static unsigned int msm_get_freq(int core_num)
{
- struct msm_priv *priv = container_of(self, struct msm_priv,
- freq_sink);
+ struct msm_priv *priv = the_msm_priv;
+
/* return current frequency in kHz */
return priv->cur_freq / 1000;
}
@@ -92,7 +90,7 @@
{
struct msm_priv *priv = pwrscale->priv;
if (priv->enabled && !priv->gpu_busy) {
- msm_dcvs_idle(priv->handle, MSM_DCVS_IDLE_EXIT, 0);
+ msm_dcvs_idle(priv->dcvs_core_id, MSM_DCVS_IDLE_EXIT, 0);
trace_kgsl_mpdcvs(device, 1);
priv->gpu_busy = 1;
}
@@ -106,7 +104,8 @@
if (priv->enabled && priv->gpu_busy)
if (device->ftbl->isidle(device)) {
- msm_dcvs_idle(priv->handle, MSM_DCVS_IDLE_ENTER, 0);
+ msm_dcvs_idle(priv->dcvs_core_id,
+ MSM_DCVS_IDLE_ENTER, 0);
trace_kgsl_mpdcvs(device, 0);
priv->gpu_busy = 0;
}
@@ -119,7 +118,7 @@
struct msm_priv *priv = pwrscale->priv;
if (priv->enabled && priv->gpu_busy) {
- msm_dcvs_idle(priv->handle, MSM_DCVS_IDLE_ENTER, 0);
+ msm_dcvs_idle(priv->dcvs_core_id, MSM_DCVS_IDLE_ENTER, 0);
trace_kgsl_mpdcvs(device, 0);
priv->gpu_busy = 0;
}
@@ -127,13 +126,14 @@
return;
}
-static void msm_remove_io_fraction(struct kgsl_device *device)
+static void msm_set_io_fraction(struct kgsl_device *device,
+ unsigned int value)
{
int i;
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
for (i = 0; i < pwr->num_pwrlevels; i++)
- pwr->pwrlevels[i].io_fraction = 100;
+ pwr->pwrlevels[i].io_fraction = value;
}
@@ -154,60 +154,58 @@
{
struct msm_priv *priv;
struct msm_dcvs_freq_entry *tbl;
- int i, ret, low_level;
+ int i, ret = -EINVAL, low_level;
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
struct platform_device *pdev =
container_of(device->parentdev, struct platform_device, dev);
struct kgsl_device_platform_data *pdata = pdev->dev.platform_data;
- priv = pwrscale->priv = kzalloc(sizeof(struct msm_priv),
- GFP_KERNEL);
- if (pwrscale->priv == NULL)
- return -ENOMEM;
+ if (the_msm_priv) {
+ priv = pwrscale->priv = the_msm_priv;
+ } else {
+ priv = pwrscale->priv = kzalloc(sizeof(struct msm_priv),
+ GFP_KERNEL);
+ if (pwrscale->priv == NULL)
+ return -ENOMEM;
- priv->core_info = pdata->core_info;
- tbl = priv->core_info->freq_tbl;
- /* Fill in frequency table from low to high, reversing order. */
- low_level = pwr->num_pwrlevels - KGSL_PWRLEVEL_LAST_OFFSET;
- for (i = 0; i <= low_level; i++)
- tbl[i].freq =
- pwr->pwrlevels[low_level - i].gpu_freq / 1000;
- ret = msm_dcvs_register_core(device->name, 0, priv->core_info);
- if (ret) {
- KGSL_PWR_ERR(device, "msm_dcvs_register_core failed");
- goto err;
+ priv->core_info = pdata->core_info;
+ tbl = priv->core_info->freq_tbl;
+ /* Fill in frequency table from low to high, reversing order. */
+ low_level = pwr->num_pwrlevels - KGSL_PWRLEVEL_LAST_OFFSET;
+ for (i = 0; i <= low_level; i++)
+ tbl[i].freq =
+ pwr->pwrlevels[low_level - i].gpu_freq / 1000;
+ priv->dcvs_core_id =
+ msm_dcvs_register_core(MSM_DCVS_CORE_TYPE_GPU,
+ 0,
+ priv->core_info,
+ msm_set_freq, msm_get_freq, msm_idle_enable,
+ priv->core_info->sensors[0]);
+ if (priv->dcvs_core_id < 0) {
+ KGSL_PWR_ERR(device, "msm_dcvs_register_core failed");
+ goto err;
+ }
+ the_msm_priv = priv;
}
-
priv->device = device;
- priv->idle_source.enable = msm_idle_enable;
- priv->idle_source.core_name = device->name;
- priv->handle = msm_dcvs_idle_source_register(&priv->idle_source);
- if (priv->handle < 0) {
- ret = priv->handle;
- KGSL_PWR_ERR(device, "msm_dcvs_idle_source_register failed\n");
- goto err;
- }
-
- priv->freq_sink.core_name = device->name;
- priv->freq_sink.set_frequency = msm_set_freq;
- priv->freq_sink.get_frequency = msm_get_freq;
- ret = msm_dcvs_freq_sink_register(&priv->freq_sink);
+ ret = msm_dcvs_freq_sink_start(priv->dcvs_core_id);
if (ret >= 0) {
if (device->ftbl->isidle(device)) {
priv->gpu_busy = 0;
- msm_dcvs_idle(priv->handle, MSM_DCVS_IDLE_ENTER, 0);
+ msm_dcvs_idle(priv->dcvs_core_id,
+ MSM_DCVS_IDLE_ENTER, 0);
} else {
priv->gpu_busy = 1;
}
- msm_remove_io_fraction(device);
+ msm_set_io_fraction(device, 0);
return 0;
}
KGSL_PWR_ERR(device, "msm_dcvs_freq_sink_register failed\n");
- msm_dcvs_idle_source_unregister(&priv->idle_source);
err:
- kfree(pwrscale->priv);
+ if (!the_msm_priv)
+ kfree(pwrscale->priv);
pwrscale->priv = NULL;
return ret;
@@ -220,9 +218,7 @@
if (pwrscale->priv == NULL)
return;
- msm_dcvs_idle_source_unregister(&priv->idle_source);
- msm_dcvs_freq_sink_unregister(&priv->freq_sink);
- kfree(pwrscale->priv);
+ msm_dcvs_freq_sink_stop(priv->dcvs_core_id);
pwrscale->priv = NULL;
msm_restore_io_fraction(device);
}
diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c
index c2ce5c7..77617ba 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.c
+++ b/drivers/gpu/msm/kgsl_sharedmem.c
@@ -317,21 +317,42 @@
struct vm_area_struct *vma,
struct vm_fault *vmf)
{
- unsigned long offset;
- struct page *page;
- int i;
+ int i, pgoff;
+ struct scatterlist *s = memdesc->sg;
+ unsigned int offset;
- offset = (unsigned long) vmf->virtual_address - vma->vm_start;
+ offset = ((unsigned long) vmf->virtual_address - vma->vm_start);
- i = offset >> PAGE_SHIFT;
- page = sg_page(&memdesc->sg[i]);
- if (page == NULL)
+ if (offset >= memdesc->size)
return VM_FAULT_SIGBUS;
- get_page(page);
+ pgoff = offset >> PAGE_SHIFT;
- vmf->page = page;
- return 0;
+ /*
+ * The sglist might be comprised of mixed blocks of memory depending
+ * on how many 64K pages were allocated. This means we have to do math
+ * to find the actual 4K page to map in user space
+ */
+
+ for (i = 0; i < memdesc->sglen; i++) {
+ int npages = s->length >> PAGE_SHIFT;
+
+ if (pgoff < npages) {
+ struct page *page = sg_page(s);
+
+ page = nth_page(page, pgoff);
+
+ get_page(page);
+ vmf->page = page;
+
+ return 0;
+ }
+
+ pgoff -= npages;
+ s = sg_next(s);
+ }
+
+ return VM_FAULT_SIGBUS;
}
static int kgsl_page_alloc_vmflags(struct kgsl_memdesc *memdesc)
@@ -357,7 +378,7 @@
}
if (memdesc->sg)
for_each_sg(memdesc->sg, sg, sglen, i)
- __free_page(sg_page(sg));
+ __free_pages(sg_page(sg), get_order(sg->length));
}
static int kgsl_contiguous_vmflags(struct kgsl_memdesc *memdesc)
@@ -379,23 +400,32 @@
pgprot_t page_prot = pgprot_writecombine(PAGE_KERNEL);
struct page **pages = NULL;
struct scatterlist *sg;
+ int npages = PAGE_ALIGN(memdesc->size) >> PAGE_SHIFT;
int sglen = memdesc->sglen;
- int i;
+ int i, count = 0;
/* Don't map the guard page if it exists */
if (memdesc->flags & KGSL_MEMDESC_GUARD_PAGE)
sglen--;
/* create a list of pages to call vmap */
- pages = vmalloc(sglen * sizeof(struct page *));
+ pages = vmalloc(npages * sizeof(struct page *));
if (!pages) {
KGSL_CORE_ERR("vmalloc(%d) failed\n",
- sglen * sizeof(struct page *));
+ npages * sizeof(struct page *));
return -ENOMEM;
}
- for_each_sg(memdesc->sg, sg, sglen, i)
- pages[i] = sg_page(sg);
- memdesc->hostptr = vmap(pages, sglen,
+
+ for_each_sg(memdesc->sg, sg, sglen, i) {
+ struct page *page = sg_page(sg);
+ int j;
+
+ for (j = 0; j < sg->length >> PAGE_SHIFT; j++)
+ pages[count++] = page++;
+ }
+
+
+ memdesc->hostptr = vmap(pages, count,
VM_IOREMAP, page_prot);
KGSL_STATS_ADD(memdesc->size, kgsl_driver.stats.vmalloc,
kgsl_driver.stats.vmalloc_max);
@@ -503,14 +533,15 @@
static int
_kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc,
struct kgsl_pagetable *pagetable,
- size_t size, unsigned int protflags)
+ size_t size, unsigned int flags, unsigned int protflags)
{
- int i, order, ret = 0;
- int sglen = PAGE_ALIGN(size) / PAGE_SIZE;
+ int pcount = 0, order, ret = 0;
+ int j, len, page_size, sglen_alloc, sglen = 0;
struct page **pages = NULL;
pgprot_t page_prot = pgprot_writecombine(PAGE_KERNEL);
void *ptr;
struct sysinfo si;
+ unsigned int align;
/*
* Get the current memory information to be used in deciding if we
@@ -530,24 +561,36 @@
if (size >= ((si.freeram << PAGE_SHIFT) - SZ_32M))
return -ENOMEM;
+ align = (flags & KGSL_MEMALIGN_MASK) >> KGSL_MEMALIGN_SHIFT;
+
+ page_size = (align >= ilog2(SZ_64K) && size >= SZ_64K)
+ ? SZ_64K : PAGE_SIZE;
+
+ /*
+ * There needs to be enough room in the sg structure to be able to
+ * service the allocation entirely with PAGE_SIZE sized chunks
+ */
+
+ sglen_alloc = PAGE_ALIGN(size) >> PAGE_SHIFT;
+
/*
* Add guard page to the end of the allocation when the
* IOMMU is in use.
*/
if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_IOMMU)
- sglen++;
+ sglen_alloc++;
memdesc->size = size;
memdesc->pagetable = pagetable;
- memdesc->priv = KGSL_MEMFLAGS_CACHED;
+ memdesc->priv |= (flags & KGSL_MEMALIGN_MASK);
memdesc->ops = &kgsl_page_alloc_ops;
- memdesc->sg = kgsl_sg_alloc(sglen);
+ memdesc->sg = kgsl_sg_alloc(sglen_alloc);
if (memdesc->sg == NULL) {
KGSL_CORE_ERR("vmalloc(%d) failed\n",
- sglen * sizeof(struct scatterlist));
+ sglen_alloc * sizeof(struct scatterlist));
ret = -ENOMEM;
goto done;
}
@@ -559,38 +602,52 @@
* two pages; well within the acceptable limits for using kmalloc.
*/
- pages = kmalloc(sglen * sizeof(struct page *), GFP_KERNEL);
+ pages = kmalloc(sglen_alloc * sizeof(struct page *), GFP_KERNEL);
if (pages == NULL) {
KGSL_CORE_ERR("kmalloc (%d) failed\n",
- sglen * sizeof(struct page *));
+ sglen_alloc * sizeof(struct page *));
ret = -ENOMEM;
goto done;
}
kmemleak_not_leak(memdesc->sg);
- memdesc->sglen = sglen;
- sg_init_table(memdesc->sg, sglen);
+ memdesc->sglen_alloc = sglen_alloc;
+ sg_init_table(memdesc->sg, sglen_alloc);
- for (i = 0; i < PAGE_ALIGN(size) / PAGE_SIZE; i++) {
+ len = size;
- /*
- * Don't use GFP_ZERO here because it is faster to memset the
- * range ourselves (see below)
- */
+ while (len > 0) {
+ struct page *page;
+ unsigned int gfp_mask = GFP_KERNEL | __GFP_HIGHMEM |
+ __GFP_NOWARN;
+ int j;
- pages[i] = alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
- if (pages[i] == NULL) {
- ret = -ENOMEM;
- memdesc->sglen = i;
- goto done;
+ /* don't waste space at the end of the allocation*/
+ if (len < page_size)
+ page_size = PAGE_SIZE;
+
+ if (page_size != PAGE_SIZE)
+ gfp_mask |= __GFP_COMP;
+
+ page = alloc_pages(gfp_mask, get_order(page_size));
+
+ if (page == NULL) {
+ if (page_size != PAGE_SIZE) {
+ page_size = PAGE_SIZE;
+ continue;
+ }
}
- sg_set_page(&memdesc->sg[i], pages[i], PAGE_SIZE, 0);
+ for (j = 0; j < page_size >> PAGE_SHIFT; j++)
+ pages[pcount++] = nth_page(page, j);
+
+ sg_set_page(&memdesc->sg[sglen++], page, page_size, 0);
+ len -= page_size;
}
- /* ADd the guard page to the end of the sglist */
+ /* Add the guard page to the end of the sglist */
if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_IOMMU) {
/*
@@ -604,13 +661,14 @@
__GFP_HIGHMEM);
if (kgsl_guard_page != NULL) {
- sg_set_page(&memdesc->sg[sglen - 1], kgsl_guard_page,
+ sg_set_page(&memdesc->sg[sglen++], kgsl_guard_page,
PAGE_SIZE, 0);
memdesc->flags |= KGSL_MEMDESC_GUARD_PAGE;
- } else
- memdesc->sglen--;
+ }
}
+ memdesc->sglen = sglen;
+
/*
* All memory that goes to the user has to be zeroed out before it gets
* exposed to userspace. This means that the memory has to be mapped in
@@ -630,18 +688,16 @@
* path
*/
- ptr = vmap(pages, i, VM_IOREMAP, page_prot);
+ ptr = vmap(pages, pcount, VM_IOREMAP, page_prot);
if (ptr != NULL) {
memset(ptr, 0, memdesc->size);
dmac_flush_range(ptr, ptr + memdesc->size);
vunmap(ptr);
} else {
- int j;
-
/* Very, very, very slow path */
- for (j = 0; j < i; j++) {
+ for (j = 0; j < pcount; j++) {
ptr = kmap_atomic(pages[j]);
memset(ptr, 0, PAGE_SIZE);
dmac_flush_range(ptr, ptr + PAGE_SIZE);
@@ -684,7 +740,7 @@
size = ALIGN(size, PAGE_SIZE * 2);
ret = _kgsl_sharedmem_page_alloc(memdesc, pagetable, size,
- GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
+ 0, GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
if (!ret)
ret = kgsl_page_alloc_map_kernel(memdesc);
if (ret)
@@ -708,7 +764,7 @@
protflags |= GSL_PT_PAGE_WV;
return _kgsl_sharedmem_page_alloc(memdesc, pagetable, size,
- protflags);
+ flags, protflags);
}
EXPORT_SYMBOL(kgsl_sharedmem_page_alloc_user);
@@ -758,7 +814,7 @@
if (memdesc->ops && memdesc->ops->free)
memdesc->ops->free(memdesc);
- kgsl_sg_free(memdesc->sg, memdesc->sglen);
+ kgsl_sg_free(memdesc->sg, memdesc->sglen_alloc);
memset(memdesc, 0, sizeof(*memdesc));
}
@@ -920,3 +976,42 @@
return 0;
}
EXPORT_SYMBOL(kgsl_sharedmem_map_vma);
+
+static const char * const memtype_str[] = {
+ [KGSL_MEMTYPE_OBJECTANY] = "any(0)",
+ [KGSL_MEMTYPE_FRAMEBUFFER] = "framebuffer",
+ [KGSL_MEMTYPE_RENDERBUFFER] = "renderbuffer",
+ [KGSL_MEMTYPE_ARRAYBUFFER] = "arraybuffer",
+ [KGSL_MEMTYPE_ELEMENTARRAYBUFFER] = "elementarraybuffer",
+ [KGSL_MEMTYPE_VERTEXARRAYBUFFER] = "vertexarraybuffer",
+ [KGSL_MEMTYPE_TEXTURE] = "texture",
+ [KGSL_MEMTYPE_SURFACE] = "surface",
+ [KGSL_MEMTYPE_EGL_SURFACE] = "egl_surface",
+ [KGSL_MEMTYPE_GL] = "gl",
+ [KGSL_MEMTYPE_CL] = "cl",
+ [KGSL_MEMTYPE_CL_BUFFER_MAP] = "cl_buffer_map",
+ [KGSL_MEMTYPE_CL_BUFFER_NOMAP] = "cl_buffer_nomap",
+ [KGSL_MEMTYPE_CL_IMAGE_MAP] = "cl_image_map",
+ [KGSL_MEMTYPE_CL_IMAGE_NOMAP] = "cl_image_nomap",
+ [KGSL_MEMTYPE_CL_KERNEL_STACK] = "cl_kernel_stack",
+ [KGSL_MEMTYPE_COMMAND] = "command",
+ [KGSL_MEMTYPE_2D] = "2d",
+ [KGSL_MEMTYPE_EGL_IMAGE] = "egl_image",
+ [KGSL_MEMTYPE_EGL_SHADOW] = "egl_shadow",
+ [KGSL_MEMTYPE_MULTISAMPLE] = "egl_multisample",
+ /* KGSL_MEMTYPE_KERNEL handled below, to avoid huge array */
+};
+
+void kgsl_get_memory_usage(char *name, size_t name_size, unsigned int memflags)
+{
+ unsigned char type;
+
+ type = (memflags & KGSL_MEMTYPE_MASK) >> KGSL_MEMTYPE_SHIFT;
+ if (type == KGSL_MEMTYPE_KERNEL)
+ strlcpy(name, "kernel", name_size);
+ else if (type < ARRAY_SIZE(memtype_str) && memtype_str[type] != NULL)
+ strlcpy(name, memtype_str[type], name_size);
+ else
+ snprintf(name, name_size, "unknown(%3d)", type);
+}
+EXPORT_SYMBOL(kgsl_get_memory_usage);
diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h
index de89ac1..5a6c4c2 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.h
+++ b/drivers/gpu/msm/kgsl_sharedmem.h
@@ -27,8 +27,6 @@
#define KGSL_CACHE_OP_FLUSH 0x02
#define KGSL_CACHE_OP_CLEAN 0x03
-/** Set if the memdesc describes cached memory */
-#define KGSL_MEMFLAGS_CACHED 0x00000001
/** Set if the memdesc is mapped into all pagetables */
#define KGSL_MEMFLAGS_GLOBAL 0x00000002
@@ -136,6 +134,7 @@
{
if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_NONE)
return kgsl_sharedmem_ebimem(memdesc, pagetable, size);
+ memdesc->priv |= (KGSL_MEMTYPE_KERNEL << KGSL_MEMTYPE_SHIFT);
return kgsl_sharedmem_page_alloc(memdesc, pagetable, size);
}
@@ -144,10 +143,17 @@
struct kgsl_pagetable *pagetable,
size_t size, unsigned int flags)
{
+ int ret;
+ unsigned int mask = (KGSL_MEMTYPE_MASK | KGSL_MEMFLAGS_GPUREADONLY);
if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_NONE)
- return kgsl_sharedmem_ebimem_user(memdesc, pagetable, size,
+ ret = kgsl_sharedmem_ebimem_user(memdesc, pagetable, size,
flags);
- return kgsl_sharedmem_page_alloc_user(memdesc, pagetable, size, flags);
+ else
+ ret = kgsl_sharedmem_page_alloc_user(memdesc, pagetable, size,
+ flags);
+ if (ret == 0)
+ memdesc->priv |= flags & mask;
+ return ret;
}
static inline int
diff --git a/drivers/gpu/msm/kgsl_trace.h b/drivers/gpu/msm/kgsl_trace.h
index 81ab3fb..81cb34f 100644
--- a/drivers/gpu/msm/kgsl_trace.h
+++ b/drivers/gpu/msm/kgsl_trace.h
@@ -315,16 +315,22 @@
TP_STRUCT__entry(
__field(unsigned int, gpuaddr)
__field(unsigned int, size)
+ __field(unsigned int, tgid)
+ __array(char, usage, 16)
),
TP_fast_assign(
__entry->gpuaddr = mem_entry->memdesc.gpuaddr;
__entry->size = mem_entry->memdesc.size;
+ __entry->tgid = mem_entry->priv->pid;
+ kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage),
+ mem_entry->memdesc.priv);
),
TP_printk(
- "gpuaddr=0x%08x size=%d",
- __entry->gpuaddr, __entry->size
+ "gpuaddr=0x%08x size=%d tgid=%d usage=%s",
+ __entry->gpuaddr, __entry->size, __entry->tgid,
+ __entry->usage
)
);
@@ -339,6 +345,8 @@
__field(unsigned int, size)
__field(int, fd)
__field(int, type)
+ __field(unsigned int, tgid)
+ __array(char, usage, 16)
),
TP_fast_assign(
@@ -346,12 +354,16 @@
__entry->size = mem_entry->memdesc.size;
__entry->fd = fd;
__entry->type = mem_entry->memtype;
+ __entry->tgid = mem_entry->priv->pid;
+ kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage),
+ mem_entry->memdesc.priv);
),
TP_printk(
- "gpuaddr=0x%08x size=%d type=%d fd=%d",
+ "gpuaddr=0x%08x size=%d type=%d fd=%d tgid=%d usage %s",
__entry->gpuaddr, __entry->size,
- __entry->type, __entry->fd
+ __entry->type, __entry->fd, __entry->tgid,
+ __entry->usage
)
);
@@ -366,17 +378,23 @@
__field(unsigned int, size)
__field(int, type)
__field(int, fd)
+ __field(unsigned int, tgid)
+ __array(char, usage, 16)
),
TP_fast_assign(
__entry->gpuaddr = mem_entry->memdesc.gpuaddr;
__entry->size = mem_entry->memdesc.size;
__entry->type = mem_entry->memtype;
+ __entry->tgid = mem_entry->priv->pid;
+ kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage),
+ mem_entry->memdesc.priv);
),
TP_printk(
- "gpuaddr=0x%08x size=%d type=%d",
- __entry->gpuaddr, __entry->size, __entry->type
+ "gpuaddr=0x%08x size=%d type=%d tgid=%d usage=%s",
+ __entry->gpuaddr, __entry->size, __entry->type,
+ __entry->tgid, __entry->usage
)
);
@@ -392,6 +410,7 @@
__field(unsigned int, gpuaddr)
__field(unsigned int, size)
__field(int, type)
+ __array(char, usage, 16)
__field(unsigned int, drawctxt_id)
__field(unsigned int, curr_ts)
__field(unsigned int, free_ts)
@@ -401,6 +420,8 @@
__assign_str(device_name, device->name);
__entry->gpuaddr = mem_entry->memdesc.gpuaddr;
__entry->size = mem_entry->memdesc.size;
+ kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage),
+ mem_entry->memdesc.priv);
__entry->drawctxt_id = id;
__entry->type = mem_entry->memtype;
__entry->curr_ts = curr_ts;
@@ -408,12 +429,13 @@
),
TP_printk(
- "d_name=%s gpuaddr=0x%08x size=%d type=%d ctx=%u"
+ "d_name=%s gpuaddr=0x%08x size=%d type=%d usage=%s ctx=%u"
" curr_ts=0x%x free_ts=0x%x",
__get_str(device_name),
__entry->gpuaddr,
__entry->size,
__entry->type,
+ __entry->usage,
__entry->drawctxt_id,
__entry->curr_ts,
__entry->free_ts
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index 62bfce4..ce1a18e 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -557,6 +557,7 @@
memset(pdata, 0, sizeof *pdata);
pdata->rep = !!of_get_property(node, "autorepeat", NULL);
+ pdata->name = of_get_property(node, "input-name", NULL);
/* First count the subnodes */
pdata->nbuttons = 0;
diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c
index 9f64cec..abd66f4 100644
--- a/drivers/input/misc/pmic8xxx-pwrkey.c
+++ b/drivers/input/misc/pmic8xxx-pwrkey.c
@@ -104,14 +104,75 @@
static SIMPLE_DEV_PM_OPS(pm8xxx_pwr_key_pm_ops,
pmic8xxx_pwrkey_suspend, pmic8xxx_pwrkey_resume);
+static int pmic8xxx_set_pon1(struct device *dev, u32 debounce_us, bool pull_up)
+{
+ int err;
+ u32 delay;
+ u8 pon_cntl;
+
+ /* Valid range of pwr key trigger delay is 1/64 sec to 2 seconds. */
+ if (debounce_us > USEC_PER_SEC * 2 ||
+ debounce_us < USEC_PER_SEC / 64) {
+ dev_err(dev, "invalid power key trigger delay\n");
+ return -EINVAL;
+ }
+
+ delay = (debounce_us << 6) / USEC_PER_SEC;
+ delay = ilog2(delay);
+
+ err = pm8xxx_readb(dev->parent, PON_CNTL_1, &pon_cntl);
+ if (err < 0) {
+ dev_err(dev, "failed reading PON_CNTL_1 err=%d\n", err);
+ return err;
+ }
+
+ pon_cntl &= ~PON_CNTL_TRIG_DELAY_MASK;
+ pon_cntl |= (delay & PON_CNTL_TRIG_DELAY_MASK);
+
+ if (pull_up)
+ pon_cntl |= PON_CNTL_PULL_UP;
+ else
+ pon_cntl &= ~PON_CNTL_PULL_UP;
+
+ err = pm8xxx_writeb(dev->parent, PON_CNTL_1, pon_cntl);
+ if (err < 0) {
+ dev_err(dev, "failed writing PON_CNTL_1 err=%d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static ssize_t pmic8xxx_debounce_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct pmic8xxx_pwrkey *pwrkey = dev_get_drvdata(dev);
+ int err;
+ unsigned long val;
+
+ if (size > 8)
+ return -EINVAL;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ err = pmic8xxx_set_pon1(dev, val, pwrkey->pdata->pull_up);
+ if (err < 0)
+ return err;
+
+ return size;
+}
+
+static DEVICE_ATTR(debounce_us, 0664, NULL, pmic8xxx_debounce_store);
+
static int __devinit pmic8xxx_pwrkey_probe(struct platform_device *pdev)
{
struct input_dev *pwr;
int key_release_irq = platform_get_irq(pdev, 0);
int key_press_irq = platform_get_irq(pdev, 1);
int err;
- unsigned int delay;
- u8 pon_cntl;
struct pmic8xxx_pwrkey *pwrkey;
const struct pm8xxx_pwrkey_platform_data *pdata =
dev_get_platdata(&pdev->dev);
@@ -121,13 +182,6 @@
return -EINVAL;
}
- /* Valid range of pwr key trigger delay is 1/64 sec to 2 seconds. */
- if (pdata->kpd_trigger_delay_us > USEC_PER_SEC * 2 ||
- pdata->kpd_trigger_delay_us < USEC_PER_SEC / 64) {
- dev_err(&pdev->dev, "invalid power key trigger delay\n");
- return -EINVAL;
- }
-
pwrkey = kzalloc(sizeof(*pwrkey), GFP_KERNEL);
if (!pwrkey)
return -ENOMEM;
@@ -147,25 +201,10 @@
pwr->phys = "pmic8xxx_pwrkey/input0";
pwr->dev.parent = &pdev->dev;
- delay = (pdata->kpd_trigger_delay_us << 6) / USEC_PER_SEC;
- delay = ilog2(delay);
-
- err = pm8xxx_readb(pdev->dev.parent, PON_CNTL_1, &pon_cntl);
- if (err < 0) {
- dev_err(&pdev->dev, "failed reading PON_CNTL_1 err=%d\n", err);
- goto free_input_dev;
- }
-
- pon_cntl &= ~PON_CNTL_TRIG_DELAY_MASK;
- pon_cntl |= (delay & PON_CNTL_TRIG_DELAY_MASK);
- if (pdata->pull_up)
- pon_cntl |= PON_CNTL_PULL_UP;
- else
- pon_cntl &= ~PON_CNTL_PULL_UP;
-
- err = pm8xxx_writeb(pdev->dev.parent, PON_CNTL_1, pon_cntl);
- if (err < 0) {
- dev_err(&pdev->dev, "failed writing PON_CNTL_1 err=%d\n", err);
+ err = pmic8xxx_set_pon1(&pdev->dev,
+ pdata->kpd_trigger_delay_us, pdata->pull_up);
+ if (err) {
+ dev_dbg(&pdev->dev, "Can't set PON CTRL1 register: %d\n", err);
goto free_input_dev;
}
@@ -178,10 +217,22 @@
pwrkey->key_press_irq = key_press_irq;
pwrkey->key_release_irq = key_release_irq;
pwrkey->pwr = pwr;
- pwrkey->press = false;
platform_set_drvdata(pdev, pwrkey);
+ /* check power key status during boot */
+ err = pm8xxx_read_irq_stat(pdev->dev.parent, key_press_irq);
+ if (err < 0) {
+ dev_err(&pdev->dev, "reading irq status failed\n");
+ goto unreg_input_dev;
+ }
+ pwrkey->press = !!err;
+
+ if (pwrkey->press) {
+ input_report_key(pwrkey->pwr, KEY_POWER, 1);
+ input_sync(pwrkey->pwr);
+ }
+
err = request_any_context_irq(key_press_irq, pwrkey_press_irq,
IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_press", pwrkey);
if (err < 0) {
@@ -199,12 +250,22 @@
goto free_press_irq;
}
+ err = device_create_file(&pdev->dev, &dev_attr_debounce_us);
+ if (err < 0) {
+ dev_err(&pdev->dev,
+ "dev file creation for debounce failed: %d\n",
+ err);
+ goto free_rel_irq;
+ }
+
device_init_wakeup(&pdev->dev, pdata->wakeup);
return 0;
+free_rel_irq:
+ free_irq(key_release_irq, pwrkey);
free_press_irq:
- free_irq(key_press_irq, NULL);
+ free_irq(key_press_irq, pwrkey);
unreg_input_dev:
platform_set_drvdata(pdev, NULL);
input_unregister_device(pwr);
@@ -224,6 +285,7 @@
device_init_wakeup(&pdev->dev, 0);
+ device_remove_file(&pdev->dev, &dev_attr_debounce_us);
free_irq(key_press_irq, pwrkey);
free_irq(key_release_irq, pwrkey);
input_unregister_device(pwrkey->pwr);
diff --git a/drivers/input/touchscreen/cyttsp-i2c-qc.c b/drivers/input/touchscreen/cyttsp-i2c-qc.c
index e54a24a..f96348e 100644
--- a/drivers/input/touchscreen/cyttsp-i2c-qc.c
+++ b/drivers/input/touchscreen/cyttsp-i2c-qc.c
@@ -3,7 +3,6 @@
* drivers/input/touchscreen/cyttsp-i2c.c
*
* Copyright (C) 2009, 2010 Cypress Semiconductor, Inc.
- * Copyright (c) 2012, Code Aurora Forum. 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
@@ -43,7 +42,6 @@
#include <linux/pm_runtime.h>
#include <linux/firmware.h>
#include <linux/mutex.h>
-#include <linux/completion.h>
#include <linux/regulator/consumer.h>
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
@@ -59,30 +57,6 @@
#define FW_FNAME_LEN 40
#define TTSP_BUFF_SIZE 50
-enum cyttsp_powerstate {
- CY_IDLE = 0, /* IC cannot be reached */
- CY_READY, /* pre-operational; ready to go to ACTIVE */
- CY_ACTIVE, /* app is running, IC is scanning */
- CY_LOW_PWR, /* not currently used */
- CY_SLEEP, /* app is running, IC is idle */
- CY_BL, /* bootloader is running */
- CY_LDR, /* loader is running */
- CY_SYSINFO, /* switching to sysinfo mode */
- CY_INVALID, /* always last in the list */
-};
-static char *cyttsp_powerstate_string[] = {
- /* Order must match enum cyttsp_powerstate above */
- "IDLE",
- "READY",
- "ACTIVE",
- "LOW_PWR",
- "SLEEP",
- "BOOTLOADER",
- "LOADER",
- "SYSINFO",
- "INVALID",
-};
-
/* CY TTSP I2C Driver private data */
struct cyttsp {
struct i2c_client *client;
@@ -92,15 +66,12 @@
char phys[32];
struct cyttsp_platform_data *platform_data;
u8 num_prv_st_tch;
- u8 power_settings[3];
u16 fw_start_addr;
- enum cyttsp_powerstate power_state;
u16 act_trk[CY_NUM_TRK_ID];
u16 prv_st_tch[CY_NUM_ST_TCH_ID];
u16 prv_mt_tch[CY_NUM_MT_TCH_ID];
u16 prv_mt_pos[CY_NUM_TRK_ID][2];
atomic_t irq_enabled;
- struct completion si_int_running;
bool cyttsp_update_fw;
bool cyttsp_fwloader_mode;
bool is_suspended;
@@ -188,67 +159,6 @@
atomic_read(&ts->irq_enabled));
}
-static int cyttsp_hndshk(struct cyttsp *ts, u8 hst_mode)
-{
- int retval = 0;
- u8 mode = 0;
-
- mode = hst_mode & CY_HNDSHK_BIT ?
- hst_mode & ~CY_HNDSHK_BIT :
- hst_mode | CY_HNDSHK_BIT;
-
- retval = i2c_smbus_write_i2c_block_data(ts->client,
- CY_REG_BASE, sizeof(mode), &mode);
-
- if (retval < 0) {
- pr_err("%s: bus write fail on handshake r=%d\n",
- __func__, retval);
- }
-
- return retval;
-}
-
-static void cyttsp_change_state(struct cyttsp *ts,
- enum cyttsp_powerstate new_state)
-{
- ts->power_state = new_state;
- pr_info("%s: %s\n", __func__,
- (ts->power_state < CY_INVALID) ?
- cyttsp_powerstate_string[ts->power_state] :
- "INVALID");
-}
-
-static int cyttsp_wait_ready(struct cyttsp *ts, struct completion *complete,
- u8 *cmd, size_t cmd_size, unsigned long timeout_ms)
-{
- unsigned long timeout = 0;
- unsigned long uretval = 0;
- int retval = 0;
-
- timeout = msecs_to_jiffies(timeout_ms);
- INIT_COMPLETION(*complete);
- if ((cmd != NULL) && (cmd_size != 0)) {
- retval = i2c_smbus_write_i2c_block_data(ts->client,
- CY_REG_BASE, cmd_size, cmd);
- if (retval < 0) {
- pr_err("%s: bus write fail switch mode r=%d\n",
- __func__, retval);
- cyttsp_change_state(ts, CY_IDLE);
- goto _cyttsp_wait_ready_exit;
- }
- }
- uretval = wait_for_completion_interruptible_timeout(complete, timeout);
- if (uretval == 0) {
- pr_err("%s: Switch Mode Timeout waiting " \
- "for ready interrupt - try reading regs\n", __func__);
- /* continue anyway */
- retval = 0;
- }
-
-_cyttsp_wait_ready_exit:
- return retval;
-}
-
static ssize_t cyttsp_irq_enable(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
@@ -420,77 +330,39 @@
} while (tries++ < 10 && (retval < 0));
}
-static int cyttsp_set_sysinfo_mode(struct cyttsp *ts, u8 sleep)
-{
- int retval;
- u8 mode = CY_SYSINFO_MODE | sleep;
-
- cyttsp_change_state(ts, CY_SYSINFO);
-
- retval = cyttsp_wait_ready(ts, &ts->si_int_running,
- &mode, sizeof(mode), CY_HALF_SEC_TMO_MS);
-
- if (retval < 0) {
- pr_err("%s: fail wait ready r=%d\n", __func__, retval);
- goto cyttsp_set_sysinfo_mode_exit;
- }
-
- if (GET_HSTMODE(g_sysinfo_data.hst_mode) !=
- GET_HSTMODE(CY_SYSINFO_MODE)) {
- pr_err("%s: Fail enter Sysinfo mode hst_mode=0x%02X\n",
- __func__, g_sysinfo_data.hst_mode);
- retval = -EIO;
- } else {
- cyttsp_debug("%s: Enter Sysinfo mode hst_mode=0x%02X\n",
- __func__, g_sysinfo_data.hst_mode);
- }
-
-cyttsp_set_sysinfo_mode_exit:
- return retval;
-}
-
-static int cyttsp_set_opmode(struct cyttsp *ts, u8 sleep)
+static void cyttsp_set_sysinfo_mode(struct cyttsp *ts)
{
int retval, tries = 0;
- u8 host_reg = CY_OP_MODE | sleep;
+ u8 host_reg = CY_SYSINFO_MODE;
- cyttsp_change_state(ts, CY_ACTIVE);
+ do {
+ retval = i2c_smbus_write_i2c_block_data(ts->client,
+ CY_REG_BASE, sizeof(host_reg), &host_reg);
+ if (retval < 0)
+ msleep(20);
+ } while (tries++ < 10 && (retval < 0));
+
+ /* wait for TTSP Device to complete switch to SysInfo mode */
+ if (!(retval < 0)) {
+ retval = i2c_smbus_read_i2c_block_data(ts->client,
+ CY_REG_BASE,
+ sizeof(struct cyttsp_sysinfo_data_t),
+ (u8 *)&g_sysinfo_data);
+ } else
+ pr_err("%s: failed\n", __func__);
+}
+
+static void cyttsp_set_opmode(struct cyttsp *ts)
+{
+ int retval, tries = 0;
+ u8 host_reg = CY_OP_MODE;
+
do {
retval = i2c_smbus_write_i2c_block_data(ts->client,
CY_REG_BASE, sizeof(host_reg), &host_reg);
if (retval < 0)
msleep(20);
} while (tries++ < 10 && (retval < 0));
-
- return retval;
-}
-
-static int cyttsp_set_lp_mode(struct cyttsp *ts)
-{
- int retval = 0, tries = 0;
-
- retval = cyttsp_set_sysinfo_mode(ts, CY_LOW_PWR_MODE);
- if (retval < 0) {
- pr_err("%s: failed to enter sysinfo mode, retval =%x\n",
- __func__, retval);
- goto exit_low_power_mode;
- }
- do {
- retval = i2c_smbus_write_i2c_block_data(
- ts->client,
- CY_REG_ACT_INTRVL,
- sizeof(ts->power_settings), ts->power_settings);
- if (retval < 0)
- msleep(20);
- } while ((retval < 0) && (tries++ < 5));
- if (retval < 0)
- pr_err("%s: failed to write power_settings, retval =%x\n",
- __func__, retval);
- msleep(CY_DLY_SYSINFO);
-exit_low_power_mode:
- cyttsp_set_opmode(ts, CY_LOW_PWR_MODE);
- return retval;
-
}
static int str2uc(char *str, u8 *val)
@@ -869,20 +741,21 @@
pr_info("%s: firmware upgrade success\n", __func__);
}
- /* enable interrupts */
- if (ts->client->irq == 0)
- mod_timer(&ts->timer, jiffies + TOUCHSCREEN_TIMEOUT);
- else
- enable_irq(ts->client->irq);
-
/* enter bootloader idle mode */
cyttsp_soft_reset(ts);
/* exit bootloader mode */
cyttsp_exit_bl_mode(ts);
msleep(100);
- /* set low power mode and enter application mode*/
- if (ts->platform_data->use_sleep & CY_LOW_PWR_MODE)
- cyttsp_set_lp_mode(ts);
+ /* set sysinfo details */
+ cyttsp_set_sysinfo_mode(ts);
+ /* enter application mode */
+ cyttsp_set_opmode(ts);
+
+ /* enable interrupts */
+ if (ts->client->irq == 0)
+ mod_timer(&ts->timer, jiffies + TOUCHSCREEN_TIMEOUT);
+ else
+ enable_irq(ts->client->irq);
}
static void cyttspfw_upgrade_start(struct cyttsp *ts, const u8 *data,
@@ -1086,14 +959,15 @@
/* compare own irq counter with the device irq counter */
if (ts->client->irq) {
+ u8 host_reg;
u8 cur_cnt;
if (ts->platform_data->use_hndshk) {
- retval = cyttsp_hndshk(ts, g_xy_data.hst_mode);
- if (retval < 0) {
- pr_err("%s: Fail write handshake r=%d\n",
- __func__, retval);
- retval = 0;
- }
+
+ host_reg = g_xy_data.hst_mode & CY_HNDSHK_BIT ?
+ g_xy_data.hst_mode & ~CY_HNDSHK_BIT :
+ g_xy_data.hst_mode | CY_HNDSHK_BIT;
+ retval = i2c_smbus_write_i2c_block_data(ts->client,
+ CY_REG_BASE, sizeof(host_reg), &host_reg);
}
cur_cnt = g_xy_data.tt_undef[CY_IRQ_CNT_REG];
irq_cnt_total++;
@@ -1158,9 +1032,6 @@
tries++ < 100);
cyttsp_putbl(ts, 2, true, false, false);
}
- if (ts->platform_data->use_sleep & CY_LOW_PWR_MODE)
- cyttsp_set_lp_mode(ts);
-
goto exit_xy_handler;
} else {
cur_tch = GET_NUM_TOUCHES(g_xy_data.tt_stat);
@@ -2001,40 +1872,10 @@
static irqreturn_t cyttsp_irq(int irq, void *handle)
{
struct cyttsp *ts = (struct cyttsp *) handle;
- int retval = 0;
cyttsp_xdebug("%s: Got IRQ\n", CY_I2C_NAME);
- switch (ts->power_state) {
- case CY_SYSINFO:
- retval = i2c_smbus_read_i2c_block_data(ts->client,
- CY_REG_BASE,
- sizeof(struct cyttsp_sysinfo_data_t),
- (u8 *)&g_sysinfo_data);
- if (retval < 0) {
- pr_err("%s: Fail read status and version regs r=%d\n",
- __func__, retval);
- goto cyttsp_irq_sysinfo_exit;
- }
- if (ts->platform_data->use_hndshk) {
- retval = cyttsp_hndshk(ts, g_sysinfo_data.hst_mode);
- if (retval < 0) {
- pr_err("%s: Fail write handshake r=%d\n",
- __func__, retval);
- retval = 0;
- }
- }
- udelay(100); /* irq pulse: sysinfo mode switch=50us */
- complete(&ts->si_int_running);
-cyttsp_irq_sysinfo_exit:
- break;
- case CY_ACTIVE:
- cyttsp_xy_handler(ts);
- break;
- default:
- pr_err("%s: Unexpected power state with interrupt ps=%d\n",
- __func__, ts->power_state);
- break;
- }
+
+ cyttsp_xy_handler(ts);
return IRQ_HANDLED;
}
@@ -2400,6 +2241,106 @@
}
bypass:
+ /* switch to System Information mode to read versions
+ * and set interval registers */
+ if (!(retval < CY_OK)) {
+ cyttsp_debug("switch to sysinfo mode\n");
+ host_reg = CY_SYSINFO_MODE;
+ retval = i2c_smbus_write_i2c_block_data(ts->client,
+ CY_REG_BASE, sizeof(host_reg), &host_reg);
+ /* wait for TTSP Device to complete switch to SysInfo mode */
+ msleep(100);
+ if (!(retval < CY_OK)) {
+ retval = i2c_smbus_read_i2c_block_data(ts->client,
+ CY_REG_BASE,
+ sizeof(struct cyttsp_sysinfo_data_t),
+ (u8 *)&g_sysinfo_data);
+ cyttsp_debug("SI2: hst_mode=0x%02X mfg_cmd=0x%02X"\
+ "mfg_stat=0x%02X\n",
+ g_sysinfo_data.hst_mode,
+ g_sysinfo_data.mfg_cmd,
+ g_sysinfo_data.mfg_stat);
+ cyttsp_debug("SI2: bl_ver=0x%02X%02X\n",
+ g_sysinfo_data.bl_verh,
+ g_sysinfo_data.bl_verl);
+ pr_debug("SI2: sysinfo act_int=0x%02X tch_tmout=0x%02X lp_int=0x%02X\n",
+ g_sysinfo_data.act_intrvl,
+ g_sysinfo_data.tch_tmout,
+ g_sysinfo_data.lp_intrvl);
+ pr_info("SI%d: tver=%02X%02X a_id=%02X%02X aver=%02X%02X\n",
+ 102,
+ g_sysinfo_data.tts_verh,
+ g_sysinfo_data.tts_verl,
+ g_sysinfo_data.app_idh,
+ g_sysinfo_data.app_idl,
+ g_sysinfo_data.app_verh,
+ g_sysinfo_data.app_verl);
+ cyttsp_info("SI%d: c_id=%02X%02X%02X\n",
+ 103,
+ g_sysinfo_data.cid[0],
+ g_sysinfo_data.cid[1],
+ g_sysinfo_data.cid[2]);
+ if (!(retval < CY_OK) &&
+ (CY_DIFF(ts->platform_data->act_intrvl,
+ CY_ACT_INTRVL_DFLT) ||
+ CY_DIFF(ts->platform_data->tch_tmout,
+ CY_TCH_TMOUT_DFLT) ||
+ CY_DIFF(ts->platform_data->lp_intrvl,
+ CY_LP_INTRVL_DFLT))) {
+ if (!(retval < CY_OK)) {
+ u8 intrvl_ray[sizeof(\
+ ts->platform_data->act_intrvl) +
+ sizeof(\
+ ts->platform_data->tch_tmout) +
+ sizeof(\
+ ts->platform_data->lp_intrvl)];
+ u8 i = 0;
+
+ intrvl_ray[i++] =
+ ts->platform_data->act_intrvl;
+ intrvl_ray[i++] =
+ ts->platform_data->tch_tmout;
+ intrvl_ray[i++] =
+ ts->platform_data->lp_intrvl;
+
+ pr_debug("SI2: platinfo act_intrvl=0x%02X tch_tmout=0x%02X lp_intrvl=0x%02X\n",
+ ts->platform_data->act_intrvl,
+ ts->platform_data->tch_tmout,
+ ts->platform_data->lp_intrvl);
+ /* set intrvl registers */
+ retval = i2c_smbus_write_i2c_block_data(
+ ts->client,
+ CY_REG_ACT_INTRVL,
+ sizeof(intrvl_ray), intrvl_ray);
+ msleep(CY_DLY_SYSINFO);
+ }
+ }
+ }
+ /* switch back to Operational mode */
+ cyttsp_debug("switch back to operational mode\n");
+ if (!(retval < CY_OK)) {
+ host_reg = CY_OP_MODE/* + CY_LOW_PWR_MODE*/;
+ retval = i2c_smbus_write_i2c_block_data(ts->client,
+ CY_REG_BASE,
+ sizeof(host_reg), &host_reg);
+ /* wait for TTSP Device to complete
+ * switch to Operational mode */
+ msleep(100);
+ }
+ }
+ /* init gesture setup;
+ * this is required even if not using gestures
+ * in order to set the active distance */
+ if (!(retval < CY_OK)) {
+ u8 gesture_setup;
+ cyttsp_debug("init gesture setup\n");
+ gesture_setup = ts->platform_data->gest_set;
+ retval = i2c_smbus_write_i2c_block_data(ts->client,
+ CY_REG_GEST_SET,
+ sizeof(gesture_setup), &gesture_setup);
+ msleep(CY_DLY_DFLT);
+ }
+
if (!(retval < CY_OK))
ts->platform_data->power_state = CY_ACTIVE_STATE;
else
@@ -2498,98 +2439,6 @@
return rc;
}
-static void sysinfo_debug_msg(struct cyttsp *ts)
-{
- cyttsp_debug("SI2: hst_mode=0x%02X mfg_cmd=0x%02X " \
- "mfg_stat=0x%02X\n", \
- g_sysinfo_data.hst_mode, \
- g_sysinfo_data.mfg_cmd, \
- g_sysinfo_data.mfg_stat);
- cyttsp_debug("SI2: bl_ver=0x%02X%02X\n", \
- g_sysinfo_data.bl_verh, \
- g_sysinfo_data.bl_verl);
- cyttsp_debug("SI2: sysinfo act_int=0x%02X " \
- "tch_tmout=0x%02X lp_int=0x%02X\n", \
- g_sysinfo_data.act_intrvl, \
- g_sysinfo_data.tch_tmout, \
- g_sysinfo_data.lp_intrvl);
- cyttsp_info("SI%d: tver=%02X%02X a_id=%02X%02X " \
- "aver=%02X%02X\n", \
- 102, \
- g_sysinfo_data.tts_verh, \
- g_sysinfo_data.tts_verl, \
- g_sysinfo_data.app_idh, \
- g_sysinfo_data.app_idl, \
- g_sysinfo_data.app_verh, \
- g_sysinfo_data.app_verl);
- cyttsp_info("SI%d: c_id=%02X%02X%02X\n", \
- 103, \
- g_sysinfo_data.cid[0], \
- g_sysinfo_data.cid[1], \
- g_sysinfo_data.cid[2]);
- cyttsp_debug("SI2: platinfo " \
- "act_intrvl=0x%02X tch_tmout=0x%02X " \
- "lp_intrvl=0x%02X\n", \
- ts->platform_data->act_intrvl, \
- ts->platform_data->tch_tmout, \
- ts->platform_data->lp_intrvl);
-}
-
-static int set_bypass_modes(struct cyttsp *ts)
-{
- int retval = 0, tries = 0;
-
- /* switch to System Information mode to read versions
- * and set interval registers */
- if (ts->platform_data->use_sleep & CY_LOW_PWR_MODE)
- retval = cyttsp_set_sysinfo_mode(ts, CY_LOW_PWR_MODE);
- else
- retval = cyttsp_set_opmode(ts, CY_OP_MODE);
-
- if (!(retval < CY_OK)) {
- retval = i2c_smbus_read_i2c_block_data(ts->client,
- CY_REG_BASE,
- sizeof(struct cyttsp_sysinfo_data_t),
- (u8 *)&g_sysinfo_data);
- sysinfo_debug_msg(ts);
- /* set power settings registers */
- do {
- retval = i2c_smbus_write_i2c_block_data(ts->client,
- CY_REG_ACT_INTRVL, sizeof(ts->power_settings),
- ts->power_settings);
- if (retval < 0)
- msleep(20);
- } while ((retval < 0) && (tries++ < 5));
- if (retval < 0)
- pr_err("%s: failed to write power_settings, " \
- "retval =%x\n", __func__, retval);
- }
- /* switch back to Operational mode */
- cyttsp_debug("switch back to operational mode\n");
- if (!(retval < CY_OK)) {
- if (ts->platform_data->use_sleep & CY_LOW_PWR_MODE)
- cyttsp_set_opmode(ts, CY_LOW_PWR_MODE);
- else
- cyttsp_set_opmode(ts, CY_OP_MODE);
- /* wait for TTSP Device to complete
- * switch to Operational mode */
- msleep(100);
- }
- /* init gesture setup;
- * this is required even if not using gestures
- * in order to set the active distance */
- if (!(retval < CY_OK)) {
- u8 gesture_setup;
- cyttsp_debug("init gesture setup\n");
- gesture_setup = ts->platform_data->gest_set;
- retval = i2c_smbus_write_i2c_block_data(ts->client,
- CY_REG_GEST_SET,
- sizeof(gesture_setup), &gesture_setup);
- msleep(CY_DLY_DFLT);
- }
- return retval;
-}
-
/* cyttsp_initialize: Driver Initialization. This function takes
* care of the following tasks:
* 1. Create and register an input device with input layer
@@ -2627,21 +2476,6 @@
input_device->phys = ts->phys;
input_device->dev.parent = &client->dev;
- ts->power_state = CY_ACTIVE;
-
- if (ts->platform_data->act_intrvl)
- ts->power_settings[0] = ts->platform_data->act_intrvl;
- else
- ts->power_settings[0] = CY_ACT_INTRVL_DFLT;
- if (ts->platform_data->tch_tmout)
- ts->power_settings[1] = ts->platform_data->tch_tmout;
- else
- ts->power_settings[1] = CY_TCH_TMOUT_DFLT;
- if (ts->platform_data->lp_intrvl)
- ts->power_settings[2] = ts->platform_data->lp_intrvl;
- else
- ts->power_settings[2] = CY_LP_INTRVL_DFLT;
-
/* init the touch structures */
ts->num_prv_st_tch = CY_NTCH;
for (id = 0; id < CY_NUM_TRK_ID; id++) {
@@ -2859,7 +2693,6 @@
goto error_rm_dev_file_fupdate_fw;
}
- set_bypass_modes(ts);
cyttsp_info("%s: Successful registration\n", CY_I2C_NAME);
goto success;
@@ -2947,8 +2780,6 @@
i2c_set_clientdata(client, ts);
- init_completion(&ts->si_int_running);
-
error = cyttsp_initialize(client, ts);
if (error) {
cyttsp_xdebug1("err cyttsp_initialize\n");
@@ -2971,8 +2802,6 @@
#endif /* CONFIG_HAS_EARLYSUSPEND */
device_init_wakeup(&client->dev, ts->platform_data->wakeup);
mutex_init(&ts->mutex);
- if (ts->platform_data->use_sleep & CY_LOW_PWR_MODE)
- retval = cyttsp_set_lp_mode(ts);
cyttsp_info("Start Probe %s\n", \
(retval < CY_OK) ? "FAIL" : "PASS");
@@ -3109,9 +2938,6 @@
cyttsp_debug("Wake Up %s\n", \
(retval < CY_OK) ? "FAIL" : "PASS");
- if (ts->platform_data->use_sleep & CY_LOW_PWR_MODE)
- retval = cyttsp_set_lp_mode(ts);
-
return retval;
}
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 066bc3e..11d50c1 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -1,7 +1,7 @@
obj-$(CONFIG_IOMMU_API) += iommu.o
obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o
ifdef CONFIG_OF
-obj-$(CONFIG_MSM_IOMMU) += msm_iommu-v2.o msm_iommu_dev-v2.o msm_iommu_pagetable.o
+obj-$(CONFIG_MSM_IOMMU) += msm_iommu-v2.o msm_iommu_dev-v2.o msm_iommu_pagetable.o msm_iommu_sec.o
endif
obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o
diff --git a/drivers/iommu/msm_iommu-v2.c b/drivers/iommu/msm_iommu-v2.c
index 06ff85c..9d88fdd 100644
--- a/drivers/iommu/msm_iommu-v2.c
+++ b/drivers/iommu/msm_iommu-v2.c
@@ -148,9 +148,9 @@
return ret;
}
-static void __reset_iommu(void __iomem *base, int smt_size)
+static void __reset_iommu(void __iomem *base)
{
- int i;
+ int i, smt_size;
SET_ACR(base, 0);
SET_NSACR(base, 0);
@@ -162,6 +162,7 @@
SET_PMCR(base, 0);
SET_SCR1(base, 0);
SET_SSDR_N(base, 0, 0);
+ smt_size = GET_IDR0_NUMSMRG(base);
for (i = 0; i < smt_size; i++)
SET_SMR_VALID(base, i, 0);
@@ -169,11 +170,11 @@
mb();
}
-static void __program_iommu(void __iomem *base, int smt_size,
+static void __program_iommu(void __iomem *base,
struct msm_iommu_bfb_settings *bfb_settings)
{
int i;
- __reset_iommu(base, smt_size);
+ __reset_iommu(base);
SET_CR0_SMCFCFG(base, 1);
SET_CR0_USFCFG(base, 1);
@@ -208,16 +209,28 @@
mb();
}
+static void __release_smg(void __iomem *base, int ctx)
+{
+ int i, smt_size;
+ smt_size = GET_IDR0_NUMSMRG(base);
+
+ /* Invalidate any SMGs associated with this context */
+ for (i = 0; i < smt_size; i++)
+ if (GET_SMR_VALID(base, i) &&
+ GET_S2CR_CBNDX(base, i) == ctx)
+ SET_SMR_VALID(base, i, 0);
+}
+
static void __program_context(void __iomem *base, int ctx, int ncb,
phys_addr_t pgtable, int redirect,
- u32 *sids, int len, int smt_size)
+ u32 *sids, int len)
{
unsigned int prrr, nmrr;
unsigned int pn;
- int i, j, found, num = 0;
+ int i, j, found, num = 0, smt_size;
__reset_context(base, ctx);
-
+ smt_size = GET_IDR0_NUMSMRG(base);
pn = pgtable >> CB_TTBR0_ADDR_SHIFT;
SET_TTBCR(base, ctx, 0);
SET_CB_TTBR0_ADDR(base, ctx, pn);
@@ -424,13 +437,12 @@
}
if (!msm_iommu_ctx_attached(dev->parent))
- __program_iommu(iommu_drvdata->base, iommu_drvdata->nsmr,
+ __program_iommu(iommu_drvdata->base,
iommu_drvdata->bfb_settings);
__program_context(iommu_drvdata->base, ctx_drvdata->num,
iommu_drvdata->ncb, __pa(priv->pt.fl_table),
- priv->pt.redirect, ctx_drvdata->sids, ctx_drvdata->nsid,
- iommu_drvdata->nsmr);
+ priv->pt.redirect, ctx_drvdata->sids, ctx_drvdata->nsid);
__disable_clocks(iommu_drvdata);
list_add(&(ctx_drvdata->attached_elm), &priv->list_attached);
@@ -467,6 +479,8 @@
GET_CB_CONTEXTIDR_ASID(iommu_drvdata->base, ctx_drvdata->num));
__reset_context(iommu_drvdata->base, ctx_drvdata->num);
+ __release_smg(iommu_drvdata->base, ctx_drvdata->num);
+
__disable_clocks(iommu_drvdata);
regulator_disable(iommu_drvdata->gdsc);
diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
index e17e1f8..bf173b3 100644
--- a/drivers/iommu/msm_iommu.c
+++ b/drivers/iommu/msm_iommu.c
@@ -48,6 +48,9 @@
#define MSM_IOMMU_ATTR_CACHED_WB_NWA 0x2
#define MSM_IOMMU_ATTR_CACHED_WT 0x3
+struct bus_type msm_iommu_sec_bus_type = {
+ .name = "msm_iommu_sec_bus",
+};
static inline void clean_pte(unsigned long *start, unsigned long *end,
int redirect)
@@ -467,6 +470,92 @@
return pgprot;
}
+static unsigned long *make_second_level(struct msm_priv *priv,
+ unsigned long *fl_pte)
+{
+ unsigned long *sl;
+ sl = (unsigned long *) __get_free_pages(GFP_KERNEL,
+ get_order(SZ_4K));
+
+ if (!sl) {
+ pr_debug("Could not allocate second level table\n");
+ goto fail;
+ }
+ memset(sl, 0, SZ_4K);
+ clean_pte(sl, sl + NUM_SL_PTE, priv->redirect);
+
+ *fl_pte = ((((int)__pa(sl)) & FL_BASE_MASK) | \
+ FL_TYPE_TABLE);
+
+ clean_pte(fl_pte, fl_pte + 1, priv->redirect);
+fail:
+ return sl;
+}
+
+static int sl_4k(unsigned long *sl_pte, phys_addr_t pa, unsigned int pgprot)
+{
+ int ret = 0;
+
+ if (*sl_pte) {
+ ret = -EBUSY;
+ goto fail;
+ }
+
+ *sl_pte = (pa & SL_BASE_MASK_SMALL) | SL_NG | SL_SHARED
+ | SL_TYPE_SMALL | pgprot;
+fail:
+ return ret;
+}
+
+static int sl_64k(unsigned long *sl_pte, phys_addr_t pa, unsigned int pgprot)
+{
+ int ret = 0;
+
+ int i;
+
+ for (i = 0; i < 16; i++)
+ if (*(sl_pte+i)) {
+ ret = -EBUSY;
+ goto fail;
+ }
+
+ for (i = 0; i < 16; i++)
+ *(sl_pte+i) = (pa & SL_BASE_MASK_LARGE) | SL_NG
+ | SL_SHARED | SL_TYPE_LARGE | pgprot;
+
+fail:
+ return ret;
+}
+
+
+static inline int fl_1m(unsigned long *fl_pte, phys_addr_t pa, int pgprot)
+{
+ if (*fl_pte)
+ return -EBUSY;
+
+ *fl_pte = (pa & 0xFFF00000) | FL_NG | FL_TYPE_SECT | FL_SHARED
+ | pgprot;
+
+ return 0;
+}
+
+
+static inline int fl_16m(unsigned long *fl_pte, phys_addr_t pa, int pgprot)
+{
+ int i;
+ int ret = 0;
+ for (i = 0; i < 16; i++)
+ if (*(fl_pte+i)) {
+ ret = -EBUSY;
+ goto fail;
+ }
+ for (i = 0; i < 16; i++)
+ *(fl_pte+i) = (pa & 0xFF000000) | FL_SUPERSECTION
+ | FL_TYPE_SECT | FL_SHARED | FL_NG | pgprot;
+fail:
+ return ret;
+}
+
static int msm_iommu_map(struct iommu_domain *domain, unsigned long va,
phys_addr_t pa, size_t len, int prot)
{
@@ -514,28 +603,16 @@
fl_pte = fl_table + fl_offset; /* int pointers, 4 bytes */
if (len == SZ_16M) {
- int i = 0;
-
- for (i = 0; i < 16; i++)
- if (*(fl_pte+i)) {
- ret = -EBUSY;
- goto fail;
- }
-
- for (i = 0; i < 16; i++)
- *(fl_pte+i) = (pa & 0xFF000000) | FL_SUPERSECTION
- | FL_TYPE_SECT | FL_SHARED | FL_NG | pgprot;
+ ret = fl_16m(fl_pte, pa, pgprot);
+ if (ret)
+ goto fail;
clean_pte(fl_pte, fl_pte + 16, priv->redirect);
}
if (len == SZ_1M) {
- if (*fl_pte) {
- ret = -EBUSY;
+ ret = fl_1m(fl_pte, pa, pgprot);
+ if (ret)
goto fail;
- }
-
- *fl_pte = (pa & 0xFFF00000) | FL_NG | FL_TYPE_SECT | FL_SHARED
- | pgprot;
clean_pte(fl_pte, fl_pte + 1, priv->redirect);
}
@@ -543,22 +620,10 @@
if (len == SZ_4K || len == SZ_64K) {
if (*fl_pte == 0) {
- unsigned long *sl;
- sl = (unsigned long *) __get_free_pages(GFP_KERNEL,
- get_order(SZ_4K));
-
- if (!sl) {
- pr_debug("Could not allocate second level table\n");
+ if (make_second_level(priv, fl_pte) == NULL) {
ret = -ENOMEM;
goto fail;
}
- memset(sl, 0, SZ_4K);
- clean_pte(sl, sl + NUM_SL_PTE, priv->redirect);
-
- *fl_pte = ((((int)__pa(sl)) & FL_BASE_MASK) | \
- FL_TYPE_TABLE);
-
- clean_pte(fl_pte, fl_pte + 1, priv->redirect);
}
if (!(*fl_pte & FL_TYPE_TABLE)) {
@@ -572,29 +637,17 @@
sl_pte = sl_table + sl_offset;
if (len == SZ_4K) {
- if (*sl_pte) {
- ret = -EBUSY;
+ ret = sl_4k(sl_pte, pa, pgprot);
+ if (ret)
goto fail;
- }
- *sl_pte = (pa & SL_BASE_MASK_SMALL) | SL_NG | SL_SHARED
- | SL_TYPE_SMALL | pgprot;
clean_pte(sl_pte, sl_pte + 1, priv->redirect);
}
if (len == SZ_64K) {
- int i;
-
- for (i = 0; i < 16; i++)
- if (*(sl_pte+i)) {
- ret = -EBUSY;
- goto fail;
- }
-
- for (i = 0; i < 16; i++)
- *(sl_pte+i) = (pa & SL_BASE_MASK_LARGE) | SL_NG
- | SL_SHARED | SL_TYPE_LARGE | pgprot;
-
+ ret = sl_64k(sl_pte, pa, pgprot);
+ if (ret)
+ goto fail;
clean_pte(sl_pte, sl_pte + 16, priv->redirect);
}
@@ -712,22 +765,28 @@
return pa;
}
+static inline int is_fully_aligned(unsigned int va, phys_addr_t pa, size_t len,
+ int align)
+{
+ return IS_ALIGNED(va, align) && IS_ALIGNED(pa, align)
+ && (len >= align);
+}
+
static int msm_iommu_map_range(struct iommu_domain *domain, unsigned int va,
struct scatterlist *sg, unsigned int len,
int prot)
{
unsigned int pa;
unsigned int offset = 0;
- unsigned int pgprot;
unsigned long *fl_table;
unsigned long *fl_pte;
unsigned long fl_offset;
- unsigned long *sl_table;
+ unsigned long *sl_table = NULL;
unsigned long sl_offset, sl_start;
- unsigned int chunk_offset = 0;
- unsigned int chunk_pa;
+ unsigned int chunk_size, chunk_offset = 0;
int ret = 0;
struct msm_priv *priv;
+ unsigned int pgprot4k, pgprot64k, pgprot1m, pgprot16m;
mutex_lock(&msm_iommu_lock);
@@ -736,49 +795,78 @@
priv = domain->priv;
fl_table = priv->pgtable;
- pgprot = __get_pgprot(prot, SZ_4K);
+ pgprot4k = __get_pgprot(prot, SZ_4K);
+ pgprot64k = __get_pgprot(prot, SZ_64K);
+ pgprot1m = __get_pgprot(prot, SZ_1M);
+ pgprot16m = __get_pgprot(prot, SZ_16M);
- if (!pgprot) {
+ if (!pgprot4k || !pgprot64k || !pgprot1m || !pgprot16m) {
ret = -EINVAL;
goto fail;
}
fl_offset = FL_OFFSET(va); /* Upper 12 bits */
fl_pte = fl_table + fl_offset; /* int pointers, 4 bytes */
-
- sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
- sl_offset = SL_OFFSET(va);
-
- chunk_pa = get_phys_addr(sg);
- if (chunk_pa == 0) {
- pr_debug("No dma address for sg %p\n", sg);
- ret = -EINVAL;
- goto fail;
- }
+ pa = get_phys_addr(sg);
while (offset < len) {
- /* Set up a 2nd level page table if one doesn't exist */
- if (*fl_pte == 0) {
- sl_table = (unsigned long *)
- __get_free_pages(GFP_KERNEL, get_order(SZ_4K));
+ chunk_size = SZ_4K;
- if (!sl_table) {
- pr_debug("Could not allocate second level table\n");
+ if (is_fully_aligned(va, pa, sg->length - chunk_offset,
+ SZ_16M))
+ chunk_size = SZ_16M;
+ else if (is_fully_aligned(va, pa, sg->length - chunk_offset,
+ SZ_1M))
+ chunk_size = SZ_1M;
+ /* 64k or 4k determined later */
+
+ /* for 1M and 16M, only first level entries are required */
+ if (chunk_size >= SZ_1M) {
+ if (chunk_size == SZ_16M) {
+ ret = fl_16m(fl_pte, pa, pgprot16m);
+ if (ret)
+ goto fail;
+ clean_pte(fl_pte, fl_pte + 16, priv->redirect);
+ fl_pte += 16;
+ } else if (chunk_size == SZ_1M) {
+ ret = fl_1m(fl_pte, pa, pgprot1m);
+ if (ret)
+ goto fail;
+ clean_pte(fl_pte, fl_pte + 1, priv->redirect);
+ fl_pte++;
+ }
+
+ offset += chunk_size;
+ chunk_offset += chunk_size;
+ va += chunk_size;
+ pa += chunk_size;
+
+ if (chunk_offset >= sg->length && offset < len) {
+ chunk_offset = 0;
+ sg = sg_next(sg);
+ pa = get_phys_addr(sg);
+ if (pa == 0) {
+ pr_debug("No dma address for sg %p\n",
+ sg);
+ ret = -EINVAL;
+ goto fail;
+ }
+ }
+ continue;
+ }
+ /* for 4K or 64K, make sure there is a second level table */
+ if (*fl_pte == 0) {
+ if (!make_second_level(priv, fl_pte)) {
ret = -ENOMEM;
goto fail;
}
-
- memset(sl_table, 0, SZ_4K);
- clean_pte(sl_table, sl_table + NUM_SL_PTE,
- priv->redirect);
-
- *fl_pte = ((((int)__pa(sl_table)) & FL_BASE_MASK) |
- FL_TYPE_TABLE);
- clean_pte(fl_pte, fl_pte + 1, priv->redirect);
- } else
- sl_table = (unsigned long *)
- __va(((*fl_pte) & FL_BASE_MASK));
-
+ }
+ if (!(*fl_pte & FL_TYPE_TABLE)) {
+ ret = -EBUSY;
+ goto fail;
+ }
+ sl_table = __va(((*fl_pte) & FL_BASE_MASK));
+ sl_offset = SL_OFFSET(va);
/* Keep track of initial position so we
* don't clean more than we have to
*/
@@ -786,21 +874,39 @@
/* Build the 2nd level page table */
while (offset < len && sl_offset < NUM_SL_PTE) {
- pa = chunk_pa + chunk_offset;
- sl_table[sl_offset] = (pa & SL_BASE_MASK_SMALL) |
- pgprot | SL_NG | SL_SHARED | SL_TYPE_SMALL;
- sl_offset++;
- offset += SZ_4K;
- chunk_offset += SZ_4K;
+ /* Map a large 64K page if the chunk is large enough and
+ * the pa and va are aligned
+ */
+
+ if (is_fully_aligned(va, pa, sg->length - chunk_offset,
+ SZ_64K))
+ chunk_size = SZ_64K;
+ else
+ chunk_size = SZ_4K;
+
+ if (chunk_size == SZ_4K) {
+ sl_4k(&sl_table[sl_offset], pa, pgprot4k);
+ sl_offset++;
+ } else {
+ BUG_ON(sl_offset + 16 > NUM_SL_PTE);
+ sl_64k(&sl_table[sl_offset], pa, pgprot64k);
+ sl_offset += 16;
+ }
+
+
+ offset += chunk_size;
+ chunk_offset += chunk_size;
+ va += chunk_size;
+ pa += chunk_size;
if (chunk_offset >= sg->length && offset < len) {
chunk_offset = 0;
sg = sg_next(sg);
- chunk_pa = get_phys_addr(sg);
- if (chunk_pa == 0) {
+ pa = get_phys_addr(sg);
+ if (pa == 0) {
pr_debug("No dma address for sg %p\n",
- sg);
+ sg);
ret = -EINVAL;
goto fail;
}
@@ -808,7 +914,7 @@
}
clean_pte(sl_table + sl_start, sl_table + sl_offset,
- priv->redirect);
+ priv->redirect);
fl_pte++;
sl_offset = 0;
@@ -842,45 +948,53 @@
fl_offset = FL_OFFSET(va); /* Upper 12 bits */
fl_pte = fl_table + fl_offset; /* int pointers, 4 bytes */
- sl_start = SL_OFFSET(va);
-
while (offset < len) {
- sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
- sl_end = ((len - offset) / SZ_4K) + sl_start;
+ if (*fl_pte & FL_TYPE_TABLE) {
+ sl_start = SL_OFFSET(va);
+ sl_table = __va(((*fl_pte) & FL_BASE_MASK));
+ sl_end = ((len - offset) / SZ_4K) + sl_start;
- if (sl_end > NUM_SL_PTE)
- sl_end = NUM_SL_PTE;
+ if (sl_end > NUM_SL_PTE)
+ sl_end = NUM_SL_PTE;
- memset(sl_table + sl_start, 0, (sl_end - sl_start) * 4);
- clean_pte(sl_table + sl_start, sl_table + sl_end,
- priv->redirect);
+ memset(sl_table + sl_start, 0, (sl_end - sl_start) * 4);
+ clean_pte(sl_table + sl_start, sl_table + sl_end,
+ priv->redirect);
- offset += (sl_end - sl_start) * SZ_4K;
+ offset += (sl_end - sl_start) * SZ_4K;
+ va += (sl_end - sl_start) * SZ_4K;
- /* Unmap and free the 2nd level table if all mappings in it
- * were removed. This saves memory, but the table will need
- * to be re-allocated the next time someone tries to map these
- * VAs.
- */
- used = 0;
+ /* Unmap and free the 2nd level table if all mappings
+ * in it were removed. This saves memory, but the table
+ * will need to be re-allocated the next time someone
+ * tries to map these VAs.
+ */
+ used = 0;
- /* If we just unmapped the whole table, don't bother
- * seeing if there are still used entries left.
- */
- if (sl_end - sl_start != NUM_SL_PTE)
- for (i = 0; i < NUM_SL_PTE; i++)
- if (sl_table[i]) {
- used = 1;
- break;
- }
- if (!used) {
- free_page((unsigned long)sl_table);
+ /* If we just unmapped the whole table, don't bother
+ * seeing if there are still used entries left.
+ */
+ if (sl_end - sl_start != NUM_SL_PTE)
+ for (i = 0; i < NUM_SL_PTE; i++)
+ if (sl_table[i]) {
+ used = 1;
+ break;
+ }
+ if (!used) {
+ free_page((unsigned long)sl_table);
+ *fl_pte = 0;
+
+ clean_pte(fl_pte, fl_pte + 1, priv->redirect);
+ }
+
+ sl_start = 0;
+ } else {
*fl_pte = 0;
-
clean_pte(fl_pte, fl_pte + 1, priv->redirect);
+ va += SZ_1M;
+ offset += SZ_1M;
+ sl_start = 0;
}
-
- sl_start = 0;
fl_pte++;
}
@@ -1011,7 +1125,12 @@
}
SET_FSR(base, num, fsr);
- SET_RESUME(base, num, 1);
+ /*
+ * Only resume fetches if the registered fault handler
+ * allows it
+ */
+ if (ret != -EBUSY)
+ SET_RESUME(base, num, 1);
ret = IRQ_HANDLED;
} else
diff --git a/drivers/iommu/msm_iommu_dev-v2.c b/drivers/iommu/msm_iommu_dev-v2.c
index 237d601..ea6c87c 100644
--- a/drivers/iommu/msm_iommu_dev-v2.c
+++ b/drivers/iommu/msm_iommu_dev-v2.c
@@ -94,7 +94,6 @@
{
struct device_node *child;
int ret = 0;
- u32 nsmr;
ret = device_move(&pdev->dev, &msm_iommu_root_dev->dev, DPM_ORDER_NONE);
if (ret)
@@ -104,18 +103,6 @@
if (ret)
goto fail;
- ret = of_property_read_u32(pdev->dev.of_node, "qcom,iommu-smt-size",
- &nsmr);
- if (ret)
- goto fail;
-
- if (nsmr > MAX_NUM_SMR) {
- pr_err("Invalid SMT size: %d\n", nsmr);
- ret = -EINVAL;
- goto fail;
- }
-
- drvdata->nsmr = nsmr;
for_each_child_of_node(pdev->dev.of_node, child) {
drvdata->ncb++;
if (!of_platform_device_create(child, NULL, &pdev->dev))
@@ -123,6 +110,10 @@
}
drvdata->name = dev_name(&pdev->dev);
+ drvdata->sec_id = -1;
+ of_property_read_u32(pdev->dev.of_node, "qcom,iommu-secure-id",
+ &drvdata->sec_id);
+ return 0;
fail:
return ret;
}
diff --git a/drivers/iommu/msm_iommu_sec.c b/drivers/iommu/msm_iommu_sec.c
new file mode 100644
index 0000000..a89c4a8
--- /dev/null
+++ b/drivers/iommu/msm_iommu_sec.c
@@ -0,0 +1,556 @@
+/* Copyright (c) 2012 Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/iommu.h>
+#include <linux/clk.h>
+#include <linux/scatterlist.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <asm/sizes.h>
+
+#include <mach/iommu_hw-v2.h>
+#include <mach/iommu.h>
+#include <mach/scm.h>
+
+/* bitmap of the page sizes currently supported */
+#define MSM_IOMMU_PGSIZES (SZ_4K | SZ_64K | SZ_1M | SZ_16M)
+
+#define IOMMU_SECURE_CFG 2
+#define IOMMU_SECURE_PTBL_SIZE 3
+#define IOMMU_SECURE_PTBL_INIT 4
+#define IOMMU_SECURE_MAP 6
+#define IOMMU_SECURE_UNMAP 7
+
+static DEFINE_MUTEX(msm_iommu_lock);
+
+struct msm_priv {
+ struct list_head list_attached;
+};
+
+struct msm_scm_paddr_list {
+ unsigned int list;
+ unsigned int list_size;
+ unsigned int size;
+};
+
+struct msm_scm_mapping_info {
+ unsigned int id;
+ unsigned int ctx_id;
+ unsigned int va;
+ unsigned int size;
+};
+
+struct msm_scm_map_req {
+ struct msm_scm_paddr_list plist;
+ struct msm_scm_mapping_info info;
+};
+
+static int msm_iommu_sec_ptbl_init(void)
+{
+ struct device_node *np;
+ struct msm_scm_ptbl_init {
+ unsigned int paddr;
+ unsigned int size;
+ unsigned int spare;
+ } pinit;
+ unsigned int *buf;
+ int psize[2] = {0};
+ unsigned int spare;
+ int ret, ptbl_ret;
+
+ for_each_compatible_node(np, NULL, "qcom,msm-smmu-v2")
+ if (of_find_property(np, "qcom,iommu-secure-id", NULL))
+ break;
+
+ if (!np)
+ return 0;
+
+ of_node_put(np);
+ ret = scm_call(SCM_SVC_CP, IOMMU_SECURE_PTBL_SIZE, &spare,
+ sizeof(spare), psize, sizeof(psize));
+ if (ret) {
+ pr_err("scm call IOMMU_SECURE_PTBL_SIZE failed\n");
+ goto fail;
+ }
+
+ if (psize[1]) {
+ pr_err("scm call IOMMU_SECURE_PTBL_SIZE failed\n");
+ goto fail;
+ }
+
+ buf = kmalloc(psize[0], GFP_KERNEL);
+ if (!buf) {
+ pr_err("%s: Failed to allocate %d bytes for PTBL\n",
+ __func__, psize[0]);
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ pinit.paddr = virt_to_phys(buf);
+ pinit.size = psize[0];
+
+ ret = scm_call(SCM_SVC_CP, IOMMU_SECURE_PTBL_INIT, &pinit,
+ sizeof(pinit), &ptbl_ret, sizeof(ptbl_ret));
+ if (ret) {
+ pr_err("scm call IOMMU_SECURE_PTBL_INIT failed\n");
+ goto fail_mem;
+ }
+ if (ptbl_ret) {
+ pr_err("scm call IOMMU_SECURE_PTBL_INIT extended ret fail\n");
+ goto fail_mem;
+ }
+
+ return 0;
+
+fail_mem:
+ kfree(buf);
+fail:
+ return ret;
+}
+
+static int msm_iommu_sec_program_iommu(int sec_id)
+{
+ struct msm_scm_sec_cfg {
+ unsigned int id;
+ unsigned int spare;
+ } cfg;
+ int ret, scm_ret;
+
+ cfg.id = sec_id;
+
+ ret = scm_call(SCM_SVC_CP, IOMMU_SECURE_CFG, &cfg, sizeof(cfg),
+ &scm_ret, sizeof(scm_ret));
+ if (ret || scm_ret) {
+ pr_err("scm call IOMMU_SECURE_CFG failed\n");
+ return ret ? ret : -EINVAL;
+ }
+
+ return ret;
+}
+
+static int msm_iommu_sec_ptbl_map(struct msm_iommu_drvdata *iommu_drvdata,
+ struct msm_iommu_ctx_drvdata *ctx_drvdata,
+ unsigned long va, phys_addr_t pa, size_t len)
+{
+ struct msm_scm_map_req map;
+ int ret = 0;
+
+ map.plist.list = virt_to_phys(&pa);
+ map.plist.list_size = 1;
+ map.plist.size = len;
+ map.info.id = iommu_drvdata->sec_id;
+ map.info.ctx_id = ctx_drvdata->num;
+ map.info.va = va;
+ map.info.size = len;
+
+ if (scm_call(SCM_SVC_CP, IOMMU_SECURE_MAP, &map, sizeof(map), &ret,
+ sizeof(ret)))
+ return -EINVAL;
+ if (ret)
+ return -EINVAL;
+
+ return 0;
+}
+
+static unsigned int get_phys_addr(struct scatterlist *sg)
+{
+ /*
+ * Try sg_dma_address first so that we can
+ * map carveout regions that do not have a
+ * struct page associated with them.
+ */
+ unsigned int pa = sg_dma_address(sg);
+ if (pa == 0)
+ pa = sg_phys(sg);
+ return pa;
+}
+
+static int msm_iommu_sec_ptbl_map_range(struct msm_iommu_drvdata *iommu_drvdata,
+ struct msm_iommu_ctx_drvdata *ctx_drvdata,
+ unsigned long va, struct scatterlist *sg, size_t len)
+{
+ struct scatterlist *sgiter;
+ struct msm_scm_map_req map;
+ unsigned int *pa_list = 0;
+ unsigned int pa, cnt;
+ unsigned int offset = 0, chunk_offset = 0;
+ int ret, scm_ret;
+
+ map.info.id = iommu_drvdata->sec_id;
+ map.info.ctx_id = ctx_drvdata->num;
+ map.info.va = va;
+ map.info.size = len;
+
+ if (sg->length == len) {
+ pa = get_phys_addr(sg);
+ map.plist.list = virt_to_phys(&pa);
+ map.plist.list_size = 1;
+ map.plist.size = len;
+ } else {
+ sgiter = sg;
+ cnt = sg->length / SZ_1M;
+ while ((sgiter = sg_next(sgiter)))
+ cnt += sgiter->length / SZ_1M;
+
+ pa_list = kmalloc(cnt * sizeof(*pa_list), GFP_KERNEL);
+ if (!pa_list)
+ return -ENOMEM;
+
+ sgiter = sg;
+ cnt = 0;
+ pa = get_phys_addr(sgiter);
+ while (offset < len) {
+ pa += chunk_offset;
+ pa_list[cnt] = pa;
+ chunk_offset += SZ_1M;
+ offset += SZ_1M;
+ cnt++;
+
+ if (chunk_offset >= sgiter->length && offset < len) {
+ chunk_offset = 0;
+ sgiter = sg_next(sgiter);
+ pa = get_phys_addr(sgiter);
+ }
+ }
+
+ map.plist.list = virt_to_phys(pa_list);
+ map.plist.list_size = cnt;
+ map.plist.size = SZ_1M;
+ }
+
+ ret = scm_call(SCM_SVC_CP, IOMMU_SECURE_MAP, &map, sizeof(map),
+ &scm_ret, sizeof(scm_ret));
+ kfree(pa_list);
+ return ret;
+}
+
+static int msm_iommu_sec_ptbl_unmap(struct msm_iommu_drvdata *iommu_drvdata,
+ struct msm_iommu_ctx_drvdata *ctx_drvdata,
+ unsigned long va, size_t len)
+{
+ struct msm_scm_mapping_info mi;
+ int ret, scm_ret;
+
+ mi.id = iommu_drvdata->sec_id;
+ mi.ctx_id = ctx_drvdata->num;
+ mi.va = va;
+ mi.size = len;
+
+ ret = scm_call(SCM_SVC_CP, IOMMU_SECURE_UNMAP, &mi, sizeof(mi),
+ &scm_ret, sizeof(scm_ret));
+ return ret;
+}
+
+static int __enable_clocks(struct msm_iommu_drvdata *drvdata)
+{
+ int ret;
+
+ ret = clk_prepare_enable(drvdata->pclk);
+ if (ret)
+ goto fail;
+
+ ret = clk_prepare_enable(drvdata->clk);
+ if (ret)
+ clk_disable_unprepare(drvdata->pclk);
+
+ if (drvdata->aclk) {
+ ret = clk_prepare_enable(drvdata->aclk);
+ if (ret) {
+ clk_disable_unprepare(drvdata->clk);
+ clk_disable_unprepare(drvdata->pclk);
+ }
+ }
+fail:
+ return ret;
+}
+
+static void __disable_clocks(struct msm_iommu_drvdata *drvdata)
+{
+ if (drvdata->aclk)
+ clk_disable_unprepare(drvdata->aclk);
+ clk_disable_unprepare(drvdata->clk);
+ clk_disable_unprepare(drvdata->pclk);
+}
+
+static int msm_iommu_domain_init(struct iommu_domain *domain, int flags)
+{
+ struct msm_priv *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&priv->list_attached);
+ domain->priv = priv;
+ return 0;
+}
+
+static void msm_iommu_domain_destroy(struct iommu_domain *domain)
+{
+ struct msm_priv *priv;
+
+ mutex_lock(&msm_iommu_lock);
+ priv = domain->priv;
+ domain->priv = NULL;
+
+ kfree(priv);
+ mutex_unlock(&msm_iommu_lock);
+}
+
+static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
+{
+ struct msm_priv *priv;
+ struct msm_iommu_drvdata *iommu_drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata;
+ struct msm_iommu_ctx_drvdata *tmp_drvdata;
+ int ret = 0;
+
+ mutex_lock(&msm_iommu_lock);
+
+ priv = domain->priv;
+ if (!priv || !dev) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ iommu_drvdata = dev_get_drvdata(dev->parent);
+ ctx_drvdata = dev_get_drvdata(dev);
+ if (!iommu_drvdata || !ctx_drvdata) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (!list_empty(&ctx_drvdata->attached_elm)) {
+ ret = -EBUSY;
+ goto fail;
+ }
+
+ list_for_each_entry(tmp_drvdata, &priv->list_attached, attached_elm)
+ if (tmp_drvdata == ctx_drvdata) {
+ ret = -EBUSY;
+ goto fail;
+ }
+
+ ret = regulator_enable(iommu_drvdata->gdsc);
+ if (ret)
+ goto fail;
+
+ ret = __enable_clocks(iommu_drvdata);
+ if (ret) {
+ regulator_disable(iommu_drvdata->gdsc);
+ goto fail;
+ }
+
+ ret = msm_iommu_sec_program_iommu(iommu_drvdata->sec_id);
+ __disable_clocks(iommu_drvdata);
+ if (ret) {
+ regulator_disable(iommu_drvdata->gdsc);
+ goto fail;
+ }
+
+ list_add(&(ctx_drvdata->attached_elm), &priv->list_attached);
+ ctx_drvdata->attached_domain = domain;
+
+fail:
+ mutex_unlock(&msm_iommu_lock);
+ return ret;
+}
+
+static void msm_iommu_detach_dev(struct iommu_domain *domain,
+ struct device *dev)
+{
+ struct msm_iommu_drvdata *iommu_drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata;
+
+ mutex_lock(&msm_iommu_lock);
+ if (!dev)
+ goto fail;
+
+ iommu_drvdata = dev_get_drvdata(dev->parent);
+ ctx_drvdata = dev_get_drvdata(dev);
+ if (!iommu_drvdata || !ctx_drvdata || !ctx_drvdata->attached_domain)
+ goto fail;
+
+ list_del_init(&ctx_drvdata->attached_elm);
+ ctx_drvdata->attached_domain = NULL;
+
+ regulator_disable(iommu_drvdata->gdsc);
+
+fail:
+ mutex_unlock(&msm_iommu_lock);
+}
+
+static int get_drvdata(struct iommu_domain *domain,
+ struct msm_iommu_drvdata **iommu_drvdata,
+ struct msm_iommu_ctx_drvdata **ctx_drvdata)
+{
+ struct msm_priv *priv = domain->priv;
+ struct msm_iommu_ctx_drvdata *ctx;
+
+ list_for_each_entry(ctx, &priv->list_attached, attached_elm) {
+ if (ctx->attached_domain == domain)
+ break;
+ }
+
+ if (ctx->attached_domain != domain)
+ return -EINVAL;
+
+ *ctx_drvdata = ctx;
+ *iommu_drvdata = dev_get_drvdata(ctx->pdev->dev.parent);
+ return 0;
+}
+
+static int msm_iommu_map(struct iommu_domain *domain, unsigned long va,
+ phys_addr_t pa, size_t len, int prot)
+{
+ struct msm_iommu_drvdata *iommu_drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata;
+ int ret = 0;
+
+ mutex_lock(&msm_iommu_lock);
+
+ ret = get_drvdata(domain, &iommu_drvdata, &ctx_drvdata);
+ if (ret)
+ goto fail;
+
+ ret = msm_iommu_sec_ptbl_map(iommu_drvdata, ctx_drvdata,
+ va, pa, len);
+fail:
+ mutex_unlock(&msm_iommu_lock);
+ return ret;
+}
+
+static size_t msm_iommu_unmap(struct iommu_domain *domain, unsigned long va,
+ size_t len)
+{
+ struct msm_iommu_drvdata *iommu_drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata;
+ int ret = -ENODEV;
+
+ mutex_lock(&msm_iommu_lock);
+
+ ret = get_drvdata(domain, &iommu_drvdata, &ctx_drvdata);
+ if (ret)
+ goto fail;
+
+ ret = msm_iommu_sec_ptbl_unmap(iommu_drvdata, ctx_drvdata,
+ va, len);
+fail:
+ mutex_unlock(&msm_iommu_lock);
+
+ /* the IOMMU API requires us to return how many bytes were unmapped */
+ len = ret ? 0 : len;
+ return len;
+}
+
+static int msm_iommu_map_range(struct iommu_domain *domain, unsigned int va,
+ struct scatterlist *sg, unsigned int len,
+ int prot)
+{
+ int ret;
+ struct msm_iommu_drvdata *iommu_drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata;
+
+ mutex_lock(&msm_iommu_lock);
+
+ ret = get_drvdata(domain, &iommu_drvdata, &ctx_drvdata);
+ if (ret)
+ goto fail;
+ ret = msm_iommu_sec_ptbl_map_range(iommu_drvdata, ctx_drvdata,
+ va, sg, len);
+fail:
+ mutex_unlock(&msm_iommu_lock);
+ return ret;
+}
+
+
+static int msm_iommu_unmap_range(struct iommu_domain *domain, unsigned int va,
+ unsigned int len)
+{
+ struct msm_iommu_drvdata *iommu_drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata;
+ int ret;
+
+ mutex_lock(&msm_iommu_lock);
+
+ ret = get_drvdata(domain, &iommu_drvdata, &ctx_drvdata);
+ if (ret)
+ goto fail;
+
+ ret = msm_iommu_sec_ptbl_unmap(iommu_drvdata, ctx_drvdata, va, len);
+
+fail:
+ mutex_unlock(&msm_iommu_lock);
+ return 0;
+}
+
+static phys_addr_t msm_iommu_iova_to_phys(struct iommu_domain *domain,
+ unsigned long va)
+{
+ return 0;
+}
+
+static int msm_iommu_domain_has_cap(struct iommu_domain *domain,
+ unsigned long cap)
+{
+ return 0;
+}
+
+static phys_addr_t msm_iommu_get_pt_base_addr(struct iommu_domain *domain)
+{
+ return 0;
+}
+
+static struct iommu_ops msm_iommu_ops = {
+ .domain_init = msm_iommu_domain_init,
+ .domain_destroy = msm_iommu_domain_destroy,
+ .attach_dev = msm_iommu_attach_dev,
+ .detach_dev = msm_iommu_detach_dev,
+ .map = msm_iommu_map,
+ .unmap = msm_iommu_unmap,
+ .map_range = msm_iommu_map_range,
+ .unmap_range = msm_iommu_unmap_range,
+ .iova_to_phys = msm_iommu_iova_to_phys,
+ .domain_has_cap = msm_iommu_domain_has_cap,
+ .get_pt_base_addr = msm_iommu_get_pt_base_addr,
+ .pgsize_bitmap = MSM_IOMMU_PGSIZES,
+};
+
+static int __init msm_iommu_sec_init(void)
+{
+ int ret;
+
+ ret = bus_register(&msm_iommu_sec_bus_type);
+ if (ret)
+ goto fail;
+
+ bus_set_iommu(&msm_iommu_sec_bus_type, &msm_iommu_ops);
+ ret = msm_iommu_sec_ptbl_init();
+fail:
+ return ret;
+}
+
+subsys_initcall(msm_iommu_sec_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM SMMU Secure Driver");
diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c
index d7dc67d..8a99968 100644
--- a/drivers/media/rc/gpio-ir-recv.c
+++ b/drivers/media/rc/gpio-ir-recv.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -18,17 +18,35 @@
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
+#include <linux/pm_qos.h>
+#include <linux/timer.h>
#include <media/rc-core.h>
#include <media/gpio-ir-recv.h>
#define GPIO_IR_DRIVER_NAME "gpio-rc-recv"
#define GPIO_IR_DEVICE_NAME "gpio_ir_recv"
+static int gpio_ir_timeout = 200;
+module_param_named(gpio_ir_timeout, gpio_ir_timeout, int, 0664);
+
+static int __init gpio_ir_timeout_setup(char *p)
+{
+ gpio_ir_timeout = memparse(p, NULL);
+ return 0;
+}
+
+early_param("gpio_ir_timeout", gpio_ir_timeout_setup);
+
struct gpio_rc_dev {
struct rc_dev *rcdev;
+ struct pm_qos_request pm_qos_req;
+ struct timer_list gpio_ir_timer;
unsigned int gpio_nr;
bool active_low;
int can_sleep;
+ bool can_wakeup;
+ bool pm_qos_vote;
+ int gpio_irq_latency;
};
static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id)
@@ -38,6 +56,12 @@
int rc = 0;
enum raw_event_type type = IR_SPACE;
+ if (!gpio_dev->pm_qos_vote && gpio_dev->can_wakeup) {
+ gpio_dev->pm_qos_vote = 1;
+ pm_qos_update_request(&gpio_dev->pm_qos_req,
+ gpio_dev->gpio_irq_latency);
+ }
+
if (gpio_dev->can_sleep)
gval = gpio_get_value_cansleep(gpio_dev->gpio_nr);
else
@@ -58,10 +82,22 @@
ir_raw_event_handle(gpio_dev->rcdev);
+ if (gpio_dev->can_wakeup)
+ mod_timer(&gpio_dev->gpio_ir_timer,
+ jiffies + msecs_to_jiffies(gpio_ir_timeout));
err_get_value:
return IRQ_HANDLED;
}
+static void gpio_ir_timer(unsigned long data)
+{
+ struct gpio_rc_dev *gpio_dev = (struct gpio_rc_dev *)data;
+
+ pm_qos_update_request(&gpio_dev->pm_qos_req, PM_QOS_DEFAULT_VALUE);
+ pm_qos_request_active(&gpio_dev->pm_qos_req);
+ gpio_dev->pm_qos_vote = 0;
+}
+
static int __devinit gpio_ir_recv_probe(struct platform_device *pdev)
{
struct gpio_rc_dev *gpio_dev;
@@ -96,6 +132,9 @@
gpio_dev->rcdev = rcdev;
gpio_dev->gpio_nr = pdata->gpio_nr;
gpio_dev->active_low = pdata->active_low;
+ gpio_dev->can_wakeup = pdata->can_wakeup;
+ gpio_dev->gpio_irq_latency = pdata->swfi_latency + 1;
+ gpio_dev->pm_qos_vote = 0;
rc = gpio_request(pdata->gpio_nr, "gpio-ir-recv");
if (rc < 0)
@@ -122,7 +161,14 @@
if (rc < 0)
goto err_request_irq;
- device_init_wakeup(&pdev->dev, pdata->can_wakeup);
+ if (gpio_dev->can_wakeup) {
+ pm_qos_add_request(&gpio_dev->pm_qos_req,
+ PM_QOS_CPU_DMA_LATENCY,
+ PM_QOS_DEFAULT_VALUE);
+ device_init_wakeup(&pdev->dev, pdata->can_wakeup);
+ setup_timer(&gpio_dev->gpio_ir_timer, gpio_ir_timer,
+ (unsigned long)gpio_dev);
+ }
return 0;
@@ -144,6 +190,10 @@
{
struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
+ if (gpio_dev->can_wakeup) {
+ del_timer_sync(&gpio_dev->gpio_ir_timer);
+ pm_qos_remove_request(&gpio_dev->pm_qos_req);
+ }
free_irq(gpio_to_irq(gpio_dev->gpio_nr), gpio_dev);
platform_set_drvdata(pdev, NULL);
rc_unregister_device(gpio_dev->rcdev);
diff --git a/drivers/media/video/msm/Kconfig b/drivers/media/video/msm/Kconfig
index e9b4e2b..be9c43c 100644
--- a/drivers/media/video/msm/Kconfig
+++ b/drivers/media/video/msm/Kconfig
@@ -208,6 +208,15 @@
two mipi lanes, required for msm8625 platform.
Say Y here if this is msm8625 variant platform.
+config IMX135
+ bool "Sensor imx135 (Sony 13MP)"
+ depends on MSM_CAMERA
+ ---help---
+ Support for IMX135 sensor driver.
+ This is a Sony 13MP Bayer Sensor with autofocus and video HDR
+ support.
+ Say Y if the platform uses IMX135 sensor.
+
config VB6801
bool "Sensor vb6801"
depends on MSM_CAMERA && !ARCH_MSM8X60 && !MSM_CAMERA_V4L2
diff --git a/drivers/media/video/msm/jpeg_10/msm_jpeg_core.c b/drivers/media/video/msm/jpeg_10/msm_jpeg_core.c
index b67245c..a2fc813 100644
--- a/drivers/media/video/msm/jpeg_10/msm_jpeg_core.c
+++ b/drivers/media/video/msm/jpeg_10/msm_jpeg_core.c
@@ -159,7 +159,7 @@
void *msm_jpeg_core_err_irq(int jpeg_irq_status,
struct msm_jpeg_device *pgmn_dev)
{
- JPEG_PR_ERR("%s:%d]\n", __func__, jpeg_irq_status);
+ JPEG_PR_ERR("%s: Error %x\n", __func__, jpeg_irq_status);
return NULL;
}
@@ -211,6 +211,7 @@
if (msm_jpeg_hw_irq_is_frame_done(jpeg_irq_status)) {
/* send fe ping pong irq */
+ JPEG_DBG_HIGH("%s:%d] Session done\n", __func__, __LINE__);
data = msm_jpeg_core_fe_pingpong_irq(jpeg_irq_status,
pgmn_dev);
if (msm_jpeg_irq_handler)
diff --git a/drivers/media/video/msm/jpeg_10/msm_jpeg_hw.c b/drivers/media/video/msm/jpeg_10/msm_jpeg_hw.c
index e311e4c..c38771b 100644
--- a/drivers/media/video/msm/jpeg_10/msm_jpeg_hw.c
+++ b/drivers/media/video/msm/jpeg_10/msm_jpeg_hw.c
@@ -132,6 +132,10 @@
JPEG_PLN1_RD_OFFSET_BMSK, {0} },
{MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_PLN1_RD_PNTR_ADDR,
JPEG_PLN1_RD_PNTR_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_PLN2_RD_OFFSET_ADDR,
+ JPEG_PLN1_RD_OFFSET_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_PLN2_RD_PNTR_ADDR,
+ JPEG_PLN2_RD_PNTR_BMSK, {0} },
};
void msm_jpeg_hw_fe_buffer_update(struct msm_jpeg_hw_buf *p_input,
@@ -156,7 +160,11 @@
hw_cmd_p->data = p_input->cbcr_buffer_addr;
msm_jpeg_hw_write(hw_cmd_p++, base);
wmb();
-
+ msm_jpeg_hw_write(hw_cmd_p++, base);
+ wmb();
+ hw_cmd_p->data = p_input->pln2_addr;
+ msm_jpeg_hw_write(hw_cmd_p++, base);
+ wmb();
}
return;
}
@@ -215,6 +223,7 @@
JPEG_PR_ERR("%s Output pln1 buffer address is %x\n", __func__,
p_input->cbcr_buffer_addr);
msm_jpeg_hw_write(hw_cmd_p++, base);
+ hw_cmd_p->data = p_input->pln2_addr;
JPEG_PR_ERR("%s Output pln2 buffer address is %x\n", __func__,
p_input->pln2_addr);
msm_jpeg_hw_write(hw_cmd_p++, base);
diff --git a/drivers/media/video/msm/jpeg_10/msm_jpeg_hw.h b/drivers/media/video/msm/jpeg_10/msm_jpeg_hw.h
index e90b941..084e36b 100644
--- a/drivers/media/video/msm/jpeg_10/msm_jpeg_hw.h
+++ b/drivers/media/video/msm/jpeg_10/msm_jpeg_hw.h
@@ -29,7 +29,7 @@
uint32_t num_of_mcu_rows;
struct ion_handle *handle;
uint32_t pln2_addr;
- uint32_t pln2_offset;
+ uint32_t pln2_len;
};
struct msm_jpeg_hw_pingpong {
diff --git a/drivers/media/video/msm/jpeg_10/msm_jpeg_hw_reg.h b/drivers/media/video/msm/jpeg_10/msm_jpeg_hw_reg.h
index 928d59e..ff99aa3 100644
--- a/drivers/media/video/msm/jpeg_10/msm_jpeg_hw_reg.h
+++ b/drivers/media/video/msm/jpeg_10/msm_jpeg_hw_reg.h
@@ -72,6 +72,12 @@
#define JPEG_PLN1_RD_OFFSET_ADDR 0x00000048
#define JPEG_PLN1_RD_OFFSET_BMSK 0x1FFFFFFF
+#define JPEG_PLN2_RD_PNTR_ADDR (JPEG_REG_BASE + 0x00000050)
+#define JPEG_PLN2_RD_PNTR_BMSK 0xFFFFFFFF
+
+#define JPEG_PLN2_RD_OFFSET_ADDR 0x00000054
+#define JPEG_PLN2_RD_OFFSET_BMSK 0x1FFFFFFF
+
#define JPEG_CMD_ADDR (JPEG_REG_BASE + 0x00000010)
#define JPEG_CMD_BMSK 0x00000FFF
#define JPEG_CMD_CLEAR_WRITE_PLN_QUEUES 0x700
diff --git a/drivers/media/video/msm/jpeg_10/msm_jpeg_sync.c b/drivers/media/video/msm/jpeg_10/msm_jpeg_sync.c
index a0aaf03..a7a9d70 100644
--- a/drivers/media/video/msm/jpeg_10/msm_jpeg_sync.c
+++ b/drivers/media/video/msm/jpeg_10/msm_jpeg_sync.c
@@ -368,8 +368,8 @@
buf_cmd.fd);
buf_p->y_buffer_addr = msm_jpeg_platform_v2p(pgmn_dev, buf_cmd.fd,
- buf_cmd.y_len, &buf_p->file, &buf_p->handle,
- pgmn_dev->domain_num);
+ buf_cmd.y_len + buf_cmd.cbcr_len + buf_cmd.pln2_len,
+ &buf_p->file, &buf_p->handle, pgmn_dev->domain_num);
if (!buf_p->y_buffer_addr) {
JPEG_PR_ERR("%s:%d] v2p wrong\n", __func__, __LINE__);
kfree(buf_p);
@@ -382,11 +382,23 @@
else
buf_p->cbcr_buffer_addr = 0x0;
- JPEG_DBG("%s:%d] After v2p pln0_addr =0x%x,pln0_len %d pl1_len %d",
+ if (buf_cmd.pln2_len)
+ buf_p->pln2_addr = buf_p->cbcr_buffer_addr +
+ buf_cmd.cbcr_len;
+ else
+ buf_p->pln2_addr = 0x0;
+
+ JPEG_DBG("%s:%d]After v2p pln0_addr %x pln0_len %d",
__func__, __LINE__, buf_p->y_buffer_addr,
- buf_cmd.y_len, buf_cmd.cbcr_len);
+ buf_cmd.y_len);
+
+ JPEG_DBG("pl1_len %d, pln1_addr %x, pln2_adrr %x,pln2_len %d",
+ buf_cmd.cbcr_len, buf_p->cbcr_buffer_addr,
+ buf_p->pln2_addr, buf_cmd.pln2_len);
+
buf_p->y_len = buf_cmd.y_len;
buf_p->cbcr_len = buf_cmd.cbcr_len;
+ buf_p->pln2_len = buf_cmd.pln2_len;
buf_p->vbuf = buf_cmd;
msm_jpeg_q_in(&pgmn_dev->output_buf_q, buf_p);
@@ -489,23 +501,31 @@
(int) buf_cmd.vaddr, buf_cmd.y_len);
buf_p->y_buffer_addr = msm_jpeg_platform_v2p(pgmn_dev, buf_cmd.fd,
- buf_cmd.y_len + buf_cmd.cbcr_len, &buf_p->file,
- &buf_p->handle, pgmn_dev->domain_num) + buf_cmd.offset
- + buf_cmd.y_off;
+ buf_cmd.y_len + buf_cmd.cbcr_len + buf_cmd.pln2_len,
+ &buf_p->file, &buf_p->handle, pgmn_dev->domain_num) +
+ buf_cmd.offset + buf_cmd.y_off;
buf_p->y_len = buf_cmd.y_len;
buf_p->cbcr_len = buf_cmd.cbcr_len;
+ buf_p->pln2_len = buf_cmd.pln2_len;
buf_p->num_of_mcu_rows = buf_cmd.num_of_mcu_rows;
- buf_p->y_len = buf_cmd.y_len;
- buf_p->cbcr_len = buf_cmd.cbcr_len;
+
if (buf_cmd.cbcr_len)
- buf_p->cbcr_buffer_addr = buf_p->y_buffer_addr + buf_cmd.y_len
- + buf_cmd.cbcr_off;
+ buf_p->cbcr_buffer_addr = buf_p->y_buffer_addr +
+ buf_cmd.y_len + buf_cmd.cbcr_off;
else
buf_p->cbcr_buffer_addr = 0x0;
- JPEG_DBG("%s: y_addr=%x, y_len=%x, cbcr_addr=%x, cbcr_len=%x, fd =%d\n",
+ if (buf_cmd.pln2_len)
+ buf_p->pln2_addr = buf_p->cbcr_buffer_addr +
+ buf_cmd.cbcr_len + buf_cmd.pln2_off;
+ else
+ buf_p->pln2_addr = 0x0;
+
+ JPEG_DBG("%s: y_addr=%x, y_len=%x, cbcr_addr=%x, cbcr_len=%d",
__func__, buf_p->y_buffer_addr, buf_p->y_len,
- buf_p->cbcr_buffer_addr, buf_p->cbcr_len, buf_cmd.fd);
+ buf_p->cbcr_buffer_addr, buf_p->cbcr_len);
+ JPEG_DBG("pln2_addr = %x, pln2_len = %d, fd =%d\n",
+ buf_p->pln2_addr, buf_p->pln2_len, buf_cmd.fd);
if (!buf_p->y_buffer_addr) {
JPEG_PR_ERR("%s:%d] v2p wrong\n", __func__, __LINE__);
@@ -733,9 +753,11 @@
for (i = 0; i < 2; i++)
kfree(buf_out_free[i]);
- msm_jpeg_io_dump(pgmn_dev->base, JPEG_REG_SIZE);
+ JPEG_DBG_HIGH("%s:%d] START\n", __func__, __LINE__);
+ wmb();
rc = msm_jpeg_ioctl_hw_cmds(pgmn_dev, arg);
- JPEG_DBG("%s:%d]\n", __func__, __LINE__);
+ wmb();
+ JPEG_DBG("%s:%d]", __func__, __LINE__);
return rc;
}
diff --git a/drivers/media/video/msm/msm.h b/drivers/media/video/msm/msm.h
index 2d9296c..17303dd 100644
--- a/drivers/media/video/msm/msm.h
+++ b/drivers/media/video/msm/msm.h
@@ -210,11 +210,13 @@
int dirty;
int node_type;
struct timeval timestamp;
+ uint32_t frame_id;
};
struct msm_cam_timestamp {
uint8_t present;
struct timeval timestamp;
+ uint32_t frame_id;
};
struct msm_cam_buf_map_info {
diff --git a/drivers/media/video/msm/msm_isp.c b/drivers/media/video/msm/msm_isp.c
index 2c95ef5..77922e2 100644
--- a/drivers/media/video/msm/msm_isp.c
+++ b/drivers/media/video/msm/msm_isp.c
@@ -711,8 +711,12 @@
int rc = -EINVAL;
void __user *argp = (void __user *)arg;
- struct v4l2_subdev *sd = pmctl->vfe_sdev;
-
+ struct v4l2_subdev *sd;
+ if (!pmctl->vfe_sdev) {
+ pr_err("%s vfe subdev is NULL\n", __func__);
+ return -ENXIO;
+ }
+ sd = pmctl->vfe_sdev;
D("%s: cmd %d\n", __func__, _IOC_NR(cmd));
switch (cmd) {
case MSM_CAM_IOCTL_CONFIG_VFE:
diff --git a/drivers/media/video/msm/msm_mctl.c b/drivers/media/video/msm/msm_mctl.c
index 3b678c4..36fb849 100644
--- a/drivers/media/video/msm/msm_mctl.c
+++ b/drivers/media/video/msm/msm_mctl.c
@@ -222,7 +222,7 @@
pr_err("%s Copy from user failed ", __func__);
rc = -EFAULT;
} else {
- pr_info("%s: mctl=0x%p, vfe output mode =0x%x",
+ pr_debug("%s: mctl=0x%p, vfe output mode =0x%x\n",
__func__, p_mctl, p_mctl->vfe_output_mode);
}
return rc;
diff --git a/drivers/media/video/msm/msm_mctl_buf.c b/drivers/media/video/msm/msm_mctl_buf.c
index a3c7243..2919d23 100644
--- a/drivers/media/video/msm/msm_mctl_buf.c
+++ b/drivers/media/video/msm/msm_mctl_buf.c
@@ -434,7 +434,11 @@
D("%s Copying timestamp as %ld.%ld", __func__,
cam_ts->timestamp.tv_sec, cam_ts->timestamp.tv_usec);
buf->vidbuf.v4l2_buf.timestamp = cam_ts->timestamp;
+ buf->vidbuf.v4l2_buf.sequence = cam_ts->frame_id;
}
+ D("%s Notify user about buffer %d image_mode %d frame_id %d", __func__,
+ buf->vidbuf.v4l2_buf.index, pcam_inst->image_mode,
+ buf->vidbuf.v4l2_buf.sequence);
vb2_buffer_done(&buf->vidbuf, VB2_BUF_STATE_DONE);
return 0;
}
@@ -821,6 +825,7 @@
__func__, pcam_inst, frame->ch_paddr[0], ret_frame->dirty);
cam_ts.present = 1;
cam_ts.timestamp = ret_frame->timestamp;
+ cam_ts.frame_id = ret_frame->frame_id;
if (ret_frame->dirty)
/* the frame is dirty, not going to disptach to app */
rc = msm_mctl_release_free_buf(pmctl, pcam_inst, frame);
diff --git a/drivers/media/video/msm/msm_mctl_pp.c b/drivers/media/video/msm/msm_mctl_pp.c
index a114b37..7155d4c 100644
--- a/drivers/media/video/msm/msm_mctl_pp.c
+++ b/drivers/media/video/msm/msm_mctl_pp.c
@@ -636,6 +636,9 @@
ret_frame.dirty = dirty;
ret_frame.node_type = 0;
ret_frame.timestamp = frame.timestamp;
+ ret_frame.frame_id = frame.frame_id;
+ D("%s frame_id: %d buffer idx %d\n", __func__,
+ frame.frame_id, frame.buf_idx);
rc = msm_mctl_buf_done_pp(p_mctl, &buf_handle, &buf, &ret_frame);
return rc;
}
@@ -678,6 +681,7 @@
ret_frame.dirty = 0;
ret_frame.node_type = frame.node_type;
ret_frame.timestamp = frame.timestamp;
+ ret_frame.frame_id = frame.frame_id;
D("%s Frame done id: %d\n", __func__, frame.frame_id);
rc = msm_mctl_buf_done_pp(p_mctl, &buf_handle, &buf, &ret_frame);
return rc;
diff --git a/drivers/media/video/msm/sensors/Makefile b/drivers/media/video/msm/sensors/Makefile
index cd228a1..a70a632 100644
--- a/drivers/media/video/msm/sensors/Makefile
+++ b/drivers/media/video/msm/sensors/Makefile
@@ -8,6 +8,7 @@
obj-$(CONFIG_OV8825) += ov8825_v4l2.o
obj-$(CONFIG_IMX074) += imx074_v4l2.o
obj-$(CONFIG_S5K3L1YX) += s5k3l1yx.o
+obj-$(CONFIG_IMX135) += imx135_v4l2.o
obj-$(CONFIG_OV2720) += ov2720.o
obj-$(CONFIG_MT9M114) += mt9m114_v4l2.o
obj-$(CONFIG_S5K4E1) += s5k4e1_v4l2.o
diff --git a/drivers/media/video/msm/sensors/imx135_v4l2.c b/drivers/media/video/msm/sensors/imx135_v4l2.c
new file mode 100644
index 0000000..f480923
--- /dev/null
+++ b/drivers/media/video/msm/sensors/imx135_v4l2.c
@@ -0,0 +1,553 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_sensor.h"
+#define SENSOR_NAME "imx135"
+#define PLATFORM_DRIVER_NAME "msm_camera_imx135"
+#define imx135_obj imx135_##obj
+
+DEFINE_MUTEX(imx135_mut);
+static struct msm_sensor_ctrl_t imx135_s_ctrl;
+
+static struct msm_camera_i2c_reg_conf imx135_start_settings[] = {
+ {0x0100, 0x01},
+};
+
+static struct msm_camera_i2c_reg_conf imx135_stop_settings[] = {
+ {0x0100, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf imx135_groupon_settings[] = {
+ {0x104, 0x01},
+};
+
+static struct msm_camera_i2c_reg_conf imx135_groupoff_settings[] = {
+ {0x104, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf imx135_recommend_settings[] = {
+/* Recommended global settings */
+ {0x0220, 0x01},
+ {0x3008, 0xB0},
+ {0x320A, 0x01},
+ {0x320D, 0x10},
+ {0x3216, 0x2E},
+ {0x3230, 0x0A},
+ {0x3228, 0x05},
+ {0x3229, 0x02},
+ {0x322C, 0x02},
+ {0x3302, 0x10},
+ {0x3390, 0x45},
+ {0x3409, 0x0C},
+ {0x340B, 0xF5},
+ {0x340C, 0x2D},
+ {0x3412, 0x41},
+ {0x3413, 0xAD},
+ {0x3414, 0x1E},
+ {0x3427, 0x04},
+ {0x3480, 0x1E},
+ {0x3484, 0x1E},
+ {0x3488, 0x1E},
+ {0x348C, 0x1E},
+ {0x3490, 0x1E},
+ {0x3494, 0x1E},
+ {0x349C, 0x38},
+ {0x34A3, 0x38},
+ {0x3511, 0x8F},
+ {0x3518, 0x00},
+ {0x3519, 0x94},
+ {0x3833, 0x20},
+ {0x3893, 0x01},
+ {0x38C2, 0x08},
+ {0x3C09, 0x01},
+ {0x4300, 0x00},
+ {0x4316, 0x12},
+ {0x4317, 0x22},
+ {0x431A, 0x00},
+ {0x4324, 0x03},
+ {0x4325, 0x20},
+ {0x4326, 0x03},
+ {0x4327, 0x84},
+ {0x4328, 0x03},
+ {0x4329, 0x20},
+ {0x432A, 0x03},
+ {0x432B, 0x84},
+ {0x4401, 0x3F},
+ {0x4412, 0x3F},
+ {0x4413, 0xFF},
+ {0x4446, 0x3F},
+ {0x4447, 0xFF},
+ {0x4452, 0x00},
+ {0x4453, 0xA0},
+ {0x4454, 0x08},
+ {0x4455, 0x00},
+ {0x4458, 0x18},
+ {0x4459, 0x18},
+ {0x445A, 0x3F},
+ {0x445B, 0x3A},
+ {0x4463, 0x00},
+ {0x4465, 0x00},
+ {0x446E, 0x01},
+/* Image Quality Settings */
+/* Bypass Settings */
+ {0x4203, 0x48},
+/* Defect Correction Recommended Setting */
+ {0x4100, 0xE0},
+ {0x4102, 0x0B},
+/* RGB Filter Recommended Setting */
+ {0x4281, 0x22},
+ {0x4282, 0x82},
+ {0x4284, 0x00},
+ {0x4287, 0x18},
+ {0x4288, 0x00},
+ {0x428B, 0x1E},
+ {0x428C, 0x00},
+ {0x428F, 0x08},
+/* DLC/ADP Recommended Setting */
+ {0x4207, 0x00},
+ {0x4218, 0x02},
+ {0x421B, 0x00},
+ {0x4222, 0x04},
+ {0x4223, 0x44},
+ {0x4224, 0x46},
+ {0x4225, 0xFF},
+ {0x4226, 0x14},
+ {0x4227, 0xF2},
+ {0x4228, 0xFC},
+ {0x4229, 0x60},
+ {0x422A, 0xFA},
+ {0x422B, 0xFE},
+ {0x422C, 0xFE},
+/* Color Artifact Recommended Setting */
+ {0x4243, 0xAA}
+};
+
+/* IMX135 mode 1/2 HV at 24MHz */
+static struct msm_camera_i2c_reg_conf imx135_prev_settings[] = {
+/* Clock Setting */
+ {0x011E, 0x18},
+ {0x011F, 0x00},
+ {0x0301, 0x05},
+ {0x0303, 0x01},
+ {0x0304, 0x0B},
+ {0x0305, 0x03},
+ {0x0306, 0x01},
+ {0x0307, 0x5E},
+ {0x0309, 0x05},
+ {0x030B, 0x02},
+ {0x030C, 0x00},
+ {0x030D, 0x71},
+ {0x030E, 0x01},
+ {0x3A06, 0x12},
+/* Mode setting */
+ {0x0101, 0x00},
+ {0x0105, 0x00},
+ {0x0108, 0x03},
+ {0x0109, 0x30},
+ {0x010B, 0x32},
+ {0x0112, 0x0A},
+ {0x0113, 0x0A},
+ {0x0381, 0x01},
+ {0x0383, 0x01},
+ {0x0385, 0x01},
+ {0x0387, 0x01},
+ {0x0390, 0x01}, /* binning_en = 1 */
+ {0x0391, 0x22}, /* binning_type */
+ {0x0392, 0x00}, /* binning_mode = 0 (average) */
+ {0x0401, 0x00},
+ {0x0404, 0x00},
+ {0x0405, 0x10},
+ {0x4083, 0x01},
+/* Size setting*/
+ {0x0340, 0x0A}, /* frame_length_lines = 2680*/
+ {0x0341, 0x78},
+ {0x034C, 0x08},
+ {0x034D, 0x38},
+ {0x034E, 0x06},
+ {0x034F, 0x18},
+ {0x0354, 0x08},
+ {0x0355, 0x38},
+ {0x0356, 0x06},
+ {0x0357, 0x18},
+ {0x3310, 0x08},
+ {0x3311, 0x38},
+ {0x3312, 0x06},
+ {0x3313, 0x18},
+ {0x331C, 0x02},
+ {0x331D, 0xC0},
+ {0x33B0, 0x04},
+ {0x33B1, 0x00},
+ {0x33B3, 0x00},
+ {0x7006, 0x04},
+/* Global Timing Setting */
+ {0x0830, 0x67},
+ {0x0831, 0x27},
+ {0x0832, 0x47},
+ {0x0833, 0x27},
+ {0x0834, 0x27},
+ {0x0835, 0x1F},
+ {0x0836, 0x87},
+ {0x0837, 0x2F},
+ {0x0839, 0x1F},
+ {0x083A, 0x17},
+ {0x083B, 0x02},
+/* Integration Time Setting */
+ {0x0254, 0x00},
+/* Gain Setting */
+ {0x0205, 0x33}
+};
+
+/* IMX135 Mode Fullsize at 24MHz */
+static struct msm_camera_i2c_reg_conf imx135_snap_settings[] = {
+/* Clock Setting */
+ {0x011E, 0x18},
+ {0x011F, 0x00},
+ {0x0301, 0x05},
+ {0x0303, 0x01},
+ {0x0304, 0x0B},
+ {0x0305, 0x03},
+ {0x0306, 0x01},
+ {0x0307, 0x5E},
+ {0x0309, 0x05},
+ {0x030B, 0x01},
+ {0x030C, 0x00},
+ {0x030D, 0x60}, /* pll_multiplier = 96 */
+ {0x030E, 0x01},
+ {0x3A06, 0x11},
+/* Mode setting */
+ {0x0101, 0x00},
+ {0x0105, 0x00},
+ {0x0108, 0x03},
+ {0x0109, 0x30},
+ {0x010B, 0x32},
+ {0x0112, 0x0A},
+ {0x0113, 0x0A},
+ {0x0381, 0x01},
+ {0x0383, 0x01},
+ {0x0385, 0x01},
+ {0x0387, 0x01},
+ {0x0390, 0x00},
+ {0x0391, 0x11},
+ {0x0392, 0x00},
+ {0x0401, 0x00},
+ {0x0404, 0x00},
+ {0x0405, 0x10},
+ {0x4083, 0x01},
+/* Size setting */
+ {0x0340, 0x0C},
+ {0x0341, 0x46},
+ {0x034C, 0x10},
+ {0x034D, 0x70},
+ {0x034E, 0x0C},
+ {0x034F, 0x30},
+ {0x0354, 0x10},
+ {0x0355, 0x70},
+ {0x0356, 0x0C},
+ {0x0357, 0x30},
+ {0x3310, 0x10},
+ {0x3311, 0x70},
+ {0x3312, 0x0C},
+ {0x3313, 0x30},
+ {0x331C, 0x06},
+ {0x331D, 0x00},
+ {0x33B0, 0x04},
+ {0x33B1, 0x00},
+ {0x33B3, 0x00},
+ {0x7006, 0x04},
+/* Global Timing Setting */
+ {0x0830, 0x7F},
+ {0x0831, 0x37},
+ {0x0832, 0x5F},
+ {0x0833, 0x37},
+ {0x0834, 0x37},
+ {0x0835, 0x3F},
+ {0x0836, 0xC7},
+ {0x0837, 0x3F},
+ {0x0839, 0x1F},
+ {0x083A, 0x17},
+ {0x083B, 0x02},
+/* Integration Time Setting */
+ {0x0250, 0x0B},
+/* Gain Setting */
+ {0x0205, 0x33}
+};
+
+
+static struct msm_camera_i2c_reg_conf imx135_hdr_settings[] = {
+/* Clock Setting */
+ {0x011E, 0x18},
+ {0x011F, 0x00},
+ {0x0301, 0x05},
+ {0x0303, 0x01},
+ {0x0304, 0x0B},
+ {0x0305, 0x03},
+ {0x0306, 0x01},
+ {0x0307, 0x5E},
+ {0x0309, 0x05},
+ {0x030B, 0x02},
+ {0x030C, 0x00},
+ {0x030D, 0x71},
+ {0x030E, 0x01},
+ {0x3A06, 0x12},
+/* Mode setting */
+ {0x0101, 0x00},
+ {0x0105, 0x00},
+ {0x0108, 0x03},
+ {0x0109, 0x30},
+ {0x010B, 0x32},
+ {0x0112, 0x0E},
+ {0x0113, 0x0A},
+ {0x0381, 0x01},
+ {0x0383, 0x01},
+ {0x0385, 0x01},
+ {0x0387, 0x01},
+ {0x0390, 0x00},
+ {0x0391, 0x11},
+ {0x0392, 0x00},
+ {0x0401, 0x00},
+ {0x0404, 0x00},
+ {0x0405, 0x10},
+ {0x4083, 0x01},
+/* Size setting */
+ {0x0340, 0x0C},
+ {0x0341, 0x48},
+ {0x034C, 0x08},
+ {0x034D, 0x38},
+ {0x034E, 0x06},
+ {0x034F, 0x18},
+ {0x0354, 0x08},
+ {0x0355, 0x38},
+ {0x0356, 0x06},
+ {0x0357, 0x18},
+ {0x3310, 0x08},
+ {0x3311, 0x38},
+ {0x3312, 0x06},
+ {0x3313, 0x18},
+ {0x331C, 0x02},
+ {0x331D, 0xA0},
+ {0x33B0, 0x08},
+ {0x33B1, 0x38},
+ {0x33B3, 0x01},
+ {0x7006, 0x04},
+/* Global Timing Setting */
+ {0x0830, 0x67},
+ {0x0831, 0x27},
+ {0x0832, 0x47},
+ {0x0833, 0x27},
+ {0x0834, 0x27},
+ {0x0835, 0x1F},
+ {0x0836, 0x87},
+ {0x0837, 0x2F},
+ {0x0839, 0x1F},
+ {0x083A, 0x17},
+ {0x083B, 0x02},
+/* Integration Time Setting */
+ {0x0250, 0x0B},
+/* Gain Setting */
+ {0x0205, 0x33}
+};
+
+static struct v4l2_subdev_info imx135_subdev_info[] = {
+ {
+ .code = V4L2_MBUS_FMT_SBGGR10_1X10,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 0,
+ },
+ /* more can be supported, to be added later */
+};
+
+static struct msm_camera_i2c_conf_array imx135_init_conf[] = {
+ {&imx135_recommend_settings[0],
+ ARRAY_SIZE(imx135_recommend_settings), 0, MSM_CAMERA_I2C_BYTE_DATA}
+};
+
+static struct msm_camera_i2c_conf_array imx135_confs[] = {
+ {&imx135_snap_settings[0],
+ ARRAY_SIZE(imx135_snap_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+ {&imx135_prev_settings[0],
+ ARRAY_SIZE(imx135_prev_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+ {&imx135_hdr_settings[0],
+ ARRAY_SIZE(imx135_hdr_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+};
+
+static struct msm_sensor_output_info_t imx135_dimensions[] = {
+ /* RES0 snapshot(FULL SIZE) */
+ {
+ .x_output = 4208,
+ .y_output = 3120,
+ .line_length_pclk = 4572,
+ .frame_length_lines = 3142,
+ .vt_pixel_clk = 307200000,
+ .op_pixel_clk = 307200000,
+ .binning_factor = 1,
+ },
+ /* RES1 4:3 preview(1/2HV QTR SIZE) */
+ {
+ .x_output = 2104,
+ .y_output = 1560,
+ .line_length_pclk = 4572,
+ .frame_length_lines = 2680,
+ .vt_pixel_clk = 361600000,
+ .op_pixel_clk = 180800000,
+ .binning_factor = 1,
+ },
+ /* RES2 4:3 HDR movie mode */
+ {
+ .x_output = 2104,
+ .y_output = 1560,
+ .line_length_pclk = 4572,
+ .frame_length_lines = 3144,
+ .vt_pixel_clk = 361600000,
+ .op_pixel_clk = 180800000,
+ .binning_factor = 1,
+ },
+};
+
+static struct msm_sensor_output_reg_addr_t imx135_reg_addr = {
+ .x_output = 0x34C,
+ .y_output = 0x34E,
+ .line_length_pclk = 0x342,
+ .frame_length_lines = 0x340,
+};
+
+static struct msm_sensor_id_info_t imx135_id_info = {
+ .sensor_id_reg_addr = 0x0000,
+ .sensor_id = 0x0087,
+};
+
+static struct msm_sensor_exp_gain_info_t imx135_exp_gain_info = {
+ .coarse_int_time_addr = 0x202,
+ .global_gain_addr = 0x205,
+ .vert_offset = 4,
+};
+
+static const struct i2c_device_id imx135_i2c_id[] = {
+ {SENSOR_NAME, (kernel_ulong_t)&imx135_s_ctrl},
+ { }
+};
+
+static struct i2c_driver imx135_i2c_driver = {
+ .id_table = imx135_i2c_id,
+ .probe = msm_sensor_i2c_probe,
+ .driver = {
+ .name = SENSOR_NAME,
+ },
+};
+
+static struct msm_camera_i2c_client imx135_sensor_i2c_client = {
+ .addr_type = MSM_CAMERA_I2C_WORD_ADDR,
+};
+
+static int __init msm_sensor_init_module(void)
+{
+ return i2c_add_driver(&imx135_i2c_driver);
+}
+
+static struct v4l2_subdev_core_ops imx135_subdev_core_ops = {
+ .ioctl = msm_sensor_subdev_ioctl,
+ .s_power = msm_sensor_power,
+};
+
+static struct v4l2_subdev_video_ops imx135_subdev_video_ops = {
+ .enum_mbus_fmt = msm_sensor_v4l2_enum_fmt,
+};
+
+static struct v4l2_subdev_ops imx135_subdev_ops = {
+ .core = &imx135_subdev_core_ops,
+ .video = &imx135_subdev_video_ops,
+};
+
+int32_t imx135_write_exp_gain(struct msm_sensor_ctrl_t *s_ctrl,
+ uint16_t gain, uint32_t line)
+{
+ uint32_t fl_lines;
+ uint8_t offset;
+ fl_lines = s_ctrl->curr_frame_length_lines;
+ fl_lines = (fl_lines * s_ctrl->fps_divider) / Q10;
+ offset = s_ctrl->sensor_exp_gain_info->vert_offset;
+ if (line > (fl_lines - offset))
+ fl_lines = line + offset;
+
+ s_ctrl->func_tbl->sensor_group_hold_on(s_ctrl);
+ msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+ s_ctrl->sensor_output_reg_addr->frame_length_lines, fl_lines,
+ MSM_CAMERA_I2C_WORD_DATA);
+ msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+ s_ctrl->sensor_exp_gain_info->coarse_int_time_addr, line,
+ MSM_CAMERA_I2C_WORD_DATA);
+ msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+ s_ctrl->sensor_exp_gain_info->global_gain_addr, gain,
+ MSM_CAMERA_I2C_BYTE_DATA);
+ s_ctrl->func_tbl->sensor_group_hold_off(s_ctrl);
+ return 0;
+}
+
+static struct msm_sensor_fn_t imx135_func_tbl = {
+ .sensor_start_stream = msm_sensor_start_stream,
+ .sensor_stop_stream = msm_sensor_stop_stream,
+ .sensor_group_hold_on = msm_sensor_group_hold_on,
+ .sensor_group_hold_off = msm_sensor_group_hold_off,
+ .sensor_set_fps = msm_sensor_set_fps,
+ .sensor_write_exp_gain = imx135_write_exp_gain,
+ .sensor_write_snapshot_exp_gain = imx135_write_exp_gain,
+ .sensor_setting = msm_sensor_setting,
+ .sensor_csi_setting = msm_sensor_setting1,
+ .sensor_set_sensor_mode = msm_sensor_set_sensor_mode,
+ .sensor_mode_init = msm_sensor_mode_init,
+ .sensor_get_output_info = msm_sensor_get_output_info,
+ .sensor_config = msm_sensor_config,
+ .sensor_power_up = msm_sensor_power_up,
+ .sensor_power_down = msm_sensor_power_down,
+ .sensor_adjust_frame_lines = msm_sensor_adjust_frame_lines1,
+ .sensor_get_csi_params = msm_sensor_get_csi_params,
+};
+
+static struct msm_sensor_reg_t imx135_regs = {
+ .default_data_type = MSM_CAMERA_I2C_BYTE_DATA,
+ .start_stream_conf = imx135_start_settings,
+ .start_stream_conf_size = ARRAY_SIZE(imx135_start_settings),
+ .stop_stream_conf = imx135_stop_settings,
+ .stop_stream_conf_size = ARRAY_SIZE(imx135_stop_settings),
+ .group_hold_on_conf = imx135_groupon_settings,
+ .group_hold_on_conf_size = ARRAY_SIZE(imx135_groupon_settings),
+ .group_hold_off_conf = imx135_groupoff_settings,
+ .group_hold_off_conf_size =
+ ARRAY_SIZE(imx135_groupoff_settings),
+ .init_settings = &imx135_init_conf[0],
+ .init_size = ARRAY_SIZE(imx135_init_conf),
+ .mode_settings = &imx135_confs[0],
+ .output_settings = &imx135_dimensions[0],
+ .num_conf = ARRAY_SIZE(imx135_confs),
+};
+
+static struct msm_sensor_ctrl_t imx135_s_ctrl = {
+ .msm_sensor_reg = &imx135_regs,
+ .sensor_i2c_client = &imx135_sensor_i2c_client,
+ .sensor_i2c_addr = 0x20,
+ .sensor_output_reg_addr = &imx135_reg_addr,
+ .sensor_id_info = &imx135_id_info,
+ .sensor_exp_gain_info = &imx135_exp_gain_info,
+ .cam_mode = MSM_SENSOR_MODE_INVALID,
+ .msm_sensor_mutex = &imx135_mut,
+ .sensor_i2c_driver = &imx135_i2c_driver,
+ .sensor_v4l2_subdev_info = imx135_subdev_info,
+ .sensor_v4l2_subdev_info_size = ARRAY_SIZE(imx135_subdev_info),
+ .sensor_v4l2_subdev_ops = &imx135_subdev_ops,
+ .func_tbl = &imx135_func_tbl,
+ .clk_rate = MSM_SENSOR_MCLK_24HZ,
+};
+
+module_init(msm_sensor_init_module);
+MODULE_DESCRIPTION("Sony 13MP Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/server/msm_cam_server.c b/drivers/media/video/msm/server/msm_cam_server.c
index 84aaa69..b8b1d51 100644
--- a/drivers/media/video/msm/server/msm_cam_server.c
+++ b/drivers/media/video/msm/server/msm_cam_server.c
@@ -152,6 +152,27 @@
mctl_handle = 0;
}
}
+
+ if (!is_bayer_sensor && interface == PIX_0) {
+ if (g_server_dev.
+ interface_map_table[i].mctl_handle &&
+ g_server_dev.interface_map_table[i].
+ is_bayer_sensor) {
+ /* In case of simultaneous camera,
+ * the YUV sensor could use PIX
+ * interface to only queue the preview
+ * or video buffers, but does not
+ * expect any notifications directly.
+ * (preview/video data is updated from
+ * postprocessing in such scenario).
+ * In such case, there is no need to
+ * update the mctl_handle in the intf
+ * map table, since the notification
+ * will not be sent directly. */
+ break;
+ }
+ }
+
old_handle =
g_server_dev.interface_map_table[i].mctl_handle;
if (old_handle == 0) {
diff --git a/drivers/media/video/msm/vfe/msm_vfe32.c b/drivers/media/video/msm/vfe/msm_vfe32.c
index 6e22388..db0db36 100644
--- a/drivers/media/video/msm/vfe/msm_vfe32.c
+++ b/drivers/media/video/msm/vfe/msm_vfe32.c
@@ -587,6 +587,25 @@
atomic_set(&share_ctrl->handle_common_irq, 1);
}
+static void axi_clear_all_interrupts(struct vfe_share_ctrl_t *share_ctrl)
+{
+ atomic_set(&share_ctrl->handle_common_irq, 0);
+ msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
+ share_ctrl->vfebase + VFE_IRQ_MASK_0);
+ msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
+ share_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+ /* clear all pending interrupts*/
+ msm_camera_io_w(VFE_CLEAR_ALL_IRQS,
+ share_ctrl->vfebase + VFE_IRQ_CLEAR_0);
+ msm_camera_io_w(VFE_CLEAR_ALL_IRQS,
+ share_ctrl->vfebase + VFE_IRQ_CLEAR_1);
+ /* Ensure the write order while writing
+ *to the command register using the barrier */
+ msm_camera_io_w_mb(1,
+ share_ctrl->vfebase + VFE_IRQ_CMD);
+}
+
static void axi_disable_irq(struct vfe_share_ctrl_t *share_ctrl,
uint32_t mode)
{
@@ -632,24 +651,6 @@
msm_camera_io_w(irq_mask, share_ctrl->vfebase +
VFE_IRQ_MASK_0);
}
- /*Dont Disable for concurrent*/
- if (share_ctrl->axi_ref_cnt == 1) {
- atomic_set(&share_ctrl->handle_common_irq, 0);
- msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
- share_ctrl->vfebase + VFE_IRQ_MASK_0);
- msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
- share_ctrl->vfebase + VFE_IRQ_MASK_1);
-
- /* clear all pending interrupts*/
- msm_camera_io_w(VFE_CLEAR_ALL_IRQS,
- share_ctrl->vfebase + VFE_IRQ_CLEAR_0);
- msm_camera_io_w(VFE_CLEAR_ALL_IRQS,
- share_ctrl->vfebase + VFE_IRQ_CLEAR_1);
- /* Ensure the write order while writing
- *to the command register using the barrier */
- msm_camera_io_w_mb(1,
- share_ctrl->vfebase + VFE_IRQ_CMD);
- }
}
static void vfe32_stop(struct vfe32_ctrl_type *vfe32_ctrl)
@@ -5554,6 +5555,8 @@
axi_ctrl->share_ctrl->axi_ref_cnt--;
if (axi_ctrl->share_ctrl->axi_ref_cnt > 0)
return;
+
+ axi_clear_all_interrupts(axi_ctrl->share_ctrl);
axi_ctrl->share_ctrl->dual_enabled = 0;
disable_irq(axi_ctrl->vfeirq->start);
tasklet_kill(&axi_ctrl->vfe32_tasklet);
diff --git a/drivers/media/video/msm/vfe/msm_vfe40.c b/drivers/media/video/msm/vfe/msm_vfe40.c
index e958241..a084a6d 100644
--- a/drivers/media/video/msm/vfe/msm_vfe40.c
+++ b/drivers/media/video/msm/vfe/msm_vfe40.c
@@ -821,24 +821,76 @@
/* stats UB config */
CDBG("%s: Use bayer stats = %d\n", __func__,
vfe40_use_bayer_stats(vfe40_ctrl));
- msm_camera_io_w(0x350001F,
+
+ msm_camera_io_w(0x8350001F,
vfe40_ctrl->share_ctrl->vfebase +
VFE_BUS_STATS_HIST_WR_UB_CFG);
- msm_camera_io_w(0x370002F,
+ msm_camera_io_w(0x8370002F,
vfe40_ctrl->share_ctrl->vfebase +
VFE_BUS_STATS_BG_WR_UB_CFG);
- msm_camera_io_w(0x3A0002F,
+ msm_camera_io_w(0x83A0002F,
vfe40_ctrl->share_ctrl->vfebase +
VFE_BUS_STATS_BF_WR_UB_CFG);
- msm_camera_io_w(0x3D00007,
+ msm_camera_io_w(0x83D0000F,
vfe40_ctrl->share_ctrl->vfebase +
VFE_BUS_STATS_RS_WR_UB_CFG);
- msm_camera_io_w(0x3D8001F,
+ msm_camera_io_w(0x83E00007,
vfe40_ctrl->share_ctrl->vfebase +
VFE_BUS_STATS_CS_WR_UB_CFG);
- msm_camera_io_w(0x3F80007,
+ msm_camera_io_w(0x83E80007,
vfe40_ctrl->share_ctrl->vfebase +
VFE_BUS_STATS_SKIN_WR_UB_CFG);
+ msm_camera_io_w(0x83F0000F,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_AWB_WR_UB_CFG);
+
+
+ /* stats frame subsample config*/
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_HIST_WR_FRAMEDROP_PATTERN);
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_BG_WR_FRAMEDROP_PATTERN);
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_BF_WR_FRAMEDROP_PATTERN);
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_RS_WR_FRAMEDROP_PATTERN);
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_CS_WR_FRAMEDROP_PATTERN);
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_SKIN_WR_FRAMEDROP_PATTERN);
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_AWB_WR_FRAMEDROP_PATTERN);
+
+ /* stats irq subsample config*/
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_HIST_WR_IRQ_SUBSAMPLE_PATTERN);
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_BG_WR_IRQ_SUBSAMPLE_PATTERN);
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_BF_WR_IRQ_SUBSAMPLE_PATTERN);
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_RS_WR_IRQ_SUBSAMPLE_PATTERN);
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_CS_WR_IRQ_SUBSAMPLE_PATTERN);
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_SKIN_WR_IRQ_SUBSAMPLE_PATTERN);
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_AWB_WR_IRQ_SUBSAMPLE_PATTERN);
+
vfe40_reset_dmi_tables(vfe40_ctrl);
}
@@ -967,7 +1019,7 @@
rc = vfe40_ctrl->stats_ops.dqbuf(
vfe40_ctrl->stats_ops.stats_ctrl, stats_type, &buf);
if (rc < 0) {
- pr_err("%s: dq stats buf (type = %d) err = %d",
+ pr_err("%s: dq stats buf (type = %d) err = %d\n",
__func__, stats_type, rc);
return 0L;
}
@@ -1043,7 +1095,7 @@
addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, MSM_STATS_TYPE_AWB);
spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
if (!addr) {
- pr_err("%s: dq awb ping buf from free buf queue", __func__);
+ pr_err("%s: dq awb ping buf from free buf queue\n", __func__);
return -ENOMEM;
}
msm_camera_io_w(addr,
@@ -1053,7 +1105,7 @@
addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, MSM_STATS_TYPE_AWB);
spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
if (!addr) {
- pr_err("%s: dq awb ping buf from free buf queue",
+ pr_err("%s: dq awb ping buf from free buf queue\n",
__func__);
return -ENOMEM;
}
@@ -1070,14 +1122,12 @@
unsigned long flags;
uint32_t stats_type;
- stats_type =
- (!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSM_STATS_TYPE_AEC
- : MSM_STATS_TYPE_BG;
+ stats_type = MSM_STATS_TYPE_BG;
spin_lock_irqsave(&vfe40_ctrl->stats_bufq_lock, flags);
addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, stats_type);
spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
if (!addr) {
- pr_err("%s: dq aec ping buf from free buf queue",
+ pr_err("%s: dq aec/Bg ping buf from free buf queue\n",
__func__);
return -ENOMEM;
}
@@ -1088,7 +1138,7 @@
addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, stats_type);
spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
if (!addr) {
- pr_err("%s: dq aec pong buf from free buf queue",
+ pr_err("%s: dq aec/Bg pong buf from free buf queue\n",
__func__);
return -ENOMEM;
}
@@ -1106,9 +1156,7 @@
int rc = 0;
uint32_t stats_type;
- stats_type =
- (!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSM_STATS_TYPE_AF
- : MSM_STATS_TYPE_BF;
+ stats_type = MSM_STATS_TYPE_BF;
spin_lock_irqsave(&vfe40_ctrl->stats_bufq_lock, flags);
rc = vfe40_stats_flush_enqueue(vfe40_ctrl, stats_type);
@@ -1193,7 +1241,7 @@
addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, MSM_STATS_TYPE_IHIST);
spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
if (!addr) {
- pr_err("%s: dq ihist pong buf from free buf queue",
+ pr_err("%s: dq Ihist pong buf from free buf queue",
__func__);
return -ENOMEM;
}
@@ -1299,6 +1347,7 @@
msm_camera_io_w_mb(1, vfe40_ctrl->share_ctrl->vfebase +
VFE_CAMIF_COMMAND);
}
+
}
static int vfe40_start_recording(
@@ -1874,11 +1923,6 @@
}
break;
case VFE_CMD_STATS_AWB_START: {
- if (vfe40_use_bayer_stats(vfe40_ctrl)) {
- /* Error */
- rc = -EFAULT;
- goto proc_general_done;
- }
rc = vfe_stats_awb_buf_init(vfe40_ctrl, NULL);
if (rc < 0) {
pr_err("%s: cannot config ping/pong address of AWB",
@@ -1992,11 +2036,6 @@
case VFE_CMD_STATS_BG_START:
case VFE_CMD_STATS_BF_START:
case VFE_CMD_STATS_BHIST_START: {
- if (!vfe40_use_bayer_stats(vfe40_ctrl)) {
- /* Error */
- rc = -EFAULT;
- goto proc_general_done;
- }
old_val = msm_camera_io_r(
vfe40_ctrl->share_ctrl->vfebase + VFE_STATS_CFG);
module_val = msm_camera_io_r(
@@ -2005,7 +2044,7 @@
module_val |= BG_ENABLE_MASK;
rc = vfe_stats_aec_bg_buf_init(vfe40_ctrl);
if (rc < 0) {
- pr_err("%s: cannot config ping/pong address of CS",
+ pr_err("%s: cannot config ping/pong address of BG",
__func__);
goto proc_general_done;
}
@@ -2013,7 +2052,7 @@
module_val |= BF_ENABLE_MASK;
rc = vfe_stats_af_bf_buf_init(vfe40_ctrl);
if (rc < 0) {
- pr_err("%s: cannot config ping/pong address of CS",
+ pr_err("%s: cannot config ping/pong address of BF",
__func__);
goto proc_general_done;
}
@@ -2022,7 +2061,7 @@
old_val |= STATS_BHIST_ENABLE_MASK;
rc = vfe_stats_bhist_buf_init(vfe40_ctrl);
if (rc < 0) {
- pr_err("%s: cannot config ping/pong address of CS",
+ pr_err("%s: cannot config ping/pong address of BHist",
__func__);
goto proc_general_done;
}
@@ -3933,11 +3972,13 @@
VFE_BUS_PING_PONG_STATUS))
& ((uint32_t)(1<<(statsNum + 7)))) >> (statsNum + 7);
/* stats bits starts at 7 */
- CDBG("statsNum %d, pingpongStatus %d\n", statsNum, pingpongStatus);
+ CDBG("%s:statsNum %d, pingpongStatus %d\n", __func__,
+ statsNum, pingpongStatus);
pingpongAddr =
((uint32_t)(vfe40_ctrl->share_ctrl->vfebase +
VFE_BUS_STATS_PING_PONG_BASE)) +
- (3*statsNum)*4 + (1-pingpongStatus)*4;
+ (VFE_STATS_BUS_REG_NUM*statsNum)*4 +
+ (1-pingpongStatus)*4;
returnAddr = msm_camera_io_r((uint32_t *)pingpongAddr);
msm_camera_io_w(newAddr, (uint32_t *)pingpongAddr);
return returnAddr;
@@ -3959,13 +4000,9 @@
msgStats.frameCounter--;
msgStats.buffer = bufAddress;
switch (statsNum) {
- case statsAeNum:{
- msgStats.id =
- (!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSG_ID_STATS_AEC
- : MSG_ID_STATS_BG;
- stats_type =
- (!vfe40_use_bayer_stats(vfe40_ctrl)) ?
- MSM_STATS_TYPE_AEC : MSM_STATS_TYPE_BG;
+ case statsBgNum:{
+ msgStats.id = MSG_ID_STATS_BG;
+ stats_type = MSM_STATS_TYPE_BG;
rc = vfe40_ctrl->stats_ops.dispatch(
vfe40_ctrl->stats_ops.stats_ctrl,
stats_type, bufAddress,
@@ -3973,13 +4010,9 @@
vfe40_ctrl->stats_ops.client);
}
break;
- case statsAfNum:{
- msgStats.id =
- (!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSG_ID_STATS_AF
- : MSG_ID_STATS_BF;
- stats_type =
- (!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSM_STATS_TYPE_AF
- : MSM_STATS_TYPE_BF;
+ case statsBfNum:{
+ msgStats.id = MSG_ID_STATS_BF;
+ stats_type = MSM_STATS_TYPE_BF;
rc = vfe40_ctrl->stats_ops.dispatch(
vfe40_ctrl->stats_ops.stats_ctrl,
stats_type, bufAddress,
@@ -4085,19 +4118,17 @@
unsigned long flags;
uint32_t addr;
uint32_t stats_type;
- stats_type =
- (!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSM_STATS_TYPE_AEC
- : MSM_STATS_TYPE_BG;
+ stats_type = MSM_STATS_TYPE_BG;
spin_lock_irqsave(&vfe40_ctrl->stats_bufq_lock, flags);
addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, stats_type);
spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
if (addr) {
vfe40_ctrl->aecbgStatsControl.bufToRender =
- vfe40_process_stats_irq_common(vfe40_ctrl, statsAeNum,
+ vfe40_process_stats_irq_common(vfe40_ctrl, statsBgNum,
addr);
vfe_send_stats_msg(vfe40_ctrl,
- vfe40_ctrl->aecbgStatsControl.bufToRender, statsAeNum);
+ vfe40_ctrl->aecbgStatsControl.bufToRender, statsBgNum);
} else{
vfe40_ctrl->aecbgStatsControl.droppedStatsFrameCount++;
CDBG("%s: droppedStatsFrameCount = %d", __func__,
@@ -4131,19 +4162,17 @@
unsigned long flags;
uint32_t addr;
uint32_t stats_type;
- stats_type =
- (!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSM_STATS_TYPE_AF
- : MSM_STATS_TYPE_BF;
+ stats_type = MSM_STATS_TYPE_BF;
spin_lock_irqsave(&vfe40_ctrl->stats_bufq_lock, flags);
addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, stats_type);
spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
if (addr) {
vfe40_ctrl->afbfStatsControl.bufToRender =
- vfe40_process_stats_irq_common(vfe40_ctrl, statsAfNum,
+ vfe40_process_stats_irq_common(vfe40_ctrl, statsBfNum,
addr);
vfe_send_stats_msg(vfe40_ctrl,
- vfe40_ctrl->afbfStatsControl.bufToRender, statsAfNum);
+ vfe40_ctrl->afbfStatsControl.bufToRender, statsBfNum);
} else{
vfe40_ctrl->afbfStatsControl.droppedStatsFrameCount++;
CDBG("%s: droppedStatsFrameCount = %d", __func__,
@@ -4248,9 +4277,7 @@
CDBG("%s, stats = 0x%x\n", __func__, status_bits);
spin_lock_irqsave(&vfe40_ctrl->stats_bufq_lock, flags);
- stats_type =
- (!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSM_STATS_TYPE_AEC
- : MSM_STATS_TYPE_BG;
+ stats_type = MSM_STATS_TYPE_BG;
if (status_bits & VFE_IRQ_STATUS0_STATS_BG) {
addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl,
@@ -4258,7 +4285,7 @@
if (addr) {
vfe40_ctrl->aecbgStatsControl.bufToRender =
vfe40_process_stats_irq_common(
- vfe40_ctrl, statsAeNum, addr);
+ vfe40_ctrl, statsBgNum, addr);
process_stats = true;
} else{
vfe40_ctrl->aecbgStatsControl.bufToRender = 0;
@@ -4285,16 +4312,14 @@
vfe40_ctrl->awbStatsControl.bufToRender = 0;
}
- stats_type =
- (!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSM_STATS_TYPE_AF
- : MSM_STATS_TYPE_BF;
+ stats_type = MSM_STATS_TYPE_BF;
if (status_bits & VFE_IRQ_STATUS0_STATS_BF) {
addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl,
stats_type);
if (addr) {
vfe40_ctrl->afbfStatsControl.bufToRender =
vfe40_process_stats_irq_common(
- vfe40_ctrl, statsAfNum,
+ vfe40_ctrl, statsBfNum,
addr);
process_stats = true;
} else {
@@ -4615,19 +4640,19 @@
NOTIFY_VFE_IRQ,
(void *)VFE_IRQ_STATUS0_STATS_CS);
- if (qcmd->vfeInterruptStatus0 &
+ if (qcmd->vfeInterruptStatus1 &
VFE_IRQ_STATUS1_SYNC_TIMER0)
v4l2_subdev_notify(&vfe40_ctrl->subdev,
NOTIFY_VFE_IRQ,
(void *)VFE_IRQ_STATUS1_SYNC_TIMER0);
- if (qcmd->vfeInterruptStatus0 &
+ if (qcmd->vfeInterruptStatus1 &
VFE_IRQ_STATUS1_SYNC_TIMER1)
v4l2_subdev_notify(&vfe40_ctrl->subdev,
NOTIFY_VFE_IRQ,
(void *)VFE_IRQ_STATUS1_SYNC_TIMER1);
- if (qcmd->vfeInterruptStatus0 &
+ if (qcmd->vfeInterruptStatus1 &
VFE_IRQ_STATUS1_SYNC_TIMER2)
v4l2_subdev_notify(&vfe40_ctrl->subdev,
NOTIFY_VFE_IRQ,
@@ -6288,8 +6313,8 @@
axi40_do_tasklet, (unsigned long)axi_ctrl);
vfe40_ctrl->pdev = pdev;
- /*disable bayer stats by default*/
- vfe40_ctrl->ver_num.main = 0;
+ /*enable bayer stats by default*/
+ vfe40_ctrl->ver_num.main = 4;
return 0;
diff --git a/drivers/media/video/msm/vfe/msm_vfe40.h b/drivers/media/video/msm/vfe/msm_vfe40.h
index 8201d18..6363bfb 100644
--- a/drivers/media/video/msm/vfe/msm_vfe40.h
+++ b/drivers/media/video/msm/vfe/msm_vfe40.h
@@ -1035,13 +1035,16 @@
uint32_t simultaneous_sof_stat;
};
-#define statsAeNum 0
-#define statsAfNum 1
-#define statsAwbNum 2
-#define statsRsNum 3
-#define statsCsNum 4
-#define statsIhistNum 5
-#define statsSkinNum 6
+#define statsBeNum 0
+#define statsBgNum 1
+#define statsBfNum 2
+#define statsAwbNum 3
+#define statsRsNum 4
+#define statsCsNum 5
+#define statsIhistNum 6
+#define statsSkinNum 7
+
+#define VFE_STATS_BUS_REG_NUM 6
struct vfe_cmd_stats_ack {
uint32_t nextStatsBuf;
diff --git a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
index fda03de..a608e1ab 100644
--- a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
@@ -486,7 +486,7 @@
|| CONTAINS(buff_off, size, temp->buff_off)
|| OVERLAPS(buff_off, size,
temp->buff_off, temp->size))) {
- dprintk(VIDC_WARN,
+ dprintk(VIDC_INFO,
"This memory region is already mapped\n");
ret = temp;
break;
@@ -509,7 +509,7 @@
if (!list_empty(list)) {
list_for_each_entry(temp, list, list) {
if (temp && temp->fd == fd) {
- dprintk(VIDC_ERR, "Found same fd buffer\n");
+ dprintk(VIDC_INFO, "Found same fd buffer\n");
ret = temp;
break;
}
@@ -709,7 +709,7 @@
b->m.planes[i].reserved[1],
b->m.planes[i].length);
if (binfo) {
- dprintk(VIDC_WARN,
+ dprintk(VIDC_INFO,
"This memory region has already been prepared\n");
rc = -EINVAL;
goto exit;
@@ -1153,7 +1153,7 @@
ocmem_notifier_register(OCMEM_VIDEO, &ocmem->vidc_ocmem_nb);
if (!ocmem->handle) {
dprintk(VIDC_WARN, "Failed to register OCMEM notifier.");
- dprintk(VIDC_WARN, " Performance will be impacted\n");
+ dprintk(VIDC_INFO, " Performance will be impacted\n");
}
return rc;
fail_register_domains:
diff --git a/drivers/media/video/msm_vidc/msm_vdec.c b/drivers/media/video/msm_vidc/msm_vdec.c
index d843d87..49b28ae 100644
--- a/drivers/media/video/msm_vidc/msm_vdec.c
+++ b/drivers/media/video/msm_vidc/msm_vdec.c
@@ -344,7 +344,7 @@
0, 0);
if (!inst->extradata_handle) {
dprintk(VIDC_ERR,
- "Failed to allocate extradta memory\n");
+ "Failed to allocate extradata memory\n");
rc = -ENOMEM;
break;
}
@@ -436,7 +436,7 @@
rc = vb2_dqbuf(&q->vb2_bufq, b, true);
mutex_unlock(&q->lock);
if (rc)
- dprintk(VIDC_WARN, "Failed to dqbuf, %d\n", rc);
+ dprintk(VIDC_DBG, "Failed to dqbuf, %d\n", rc);
return rc;
}
@@ -622,7 +622,7 @@
sizeof(f->description));
f->pixelformat = fmt->fourcc;
} else {
- dprintk(VIDC_WARN, "No more formats found\n");
+ dprintk(VIDC_INFO, "No more formats found\n");
rc = -EINVAL;
}
return rc;
@@ -709,7 +709,6 @@
static inline int start_streaming(struct msm_vidc_inst *inst)
{
int rc = 0;
- unsigned long flags;
struct vb2_buf_entry *temp;
struct list_head *ptr, *next;
inst->in_reconfig = false;
@@ -737,7 +736,7 @@
goto fail_start;
}
- spin_lock_irqsave(&inst->lock, flags);
+ mutex_lock(&inst->sync_lock);
if (!list_empty(&inst->pendingq)) {
list_for_each_safe(ptr, next, &inst->pendingq) {
temp = list_entry(ptr, struct vb2_buf_entry, list);
@@ -751,7 +750,7 @@
kfree(temp);
}
}
- spin_unlock_irqrestore(&inst->lock, flags);
+ mutex_unlock(&inst->sync_lock);
return rc;
fail_start:
return rc;
diff --git a/drivers/media/video/msm_vidc/msm_venc.c b/drivers/media/video/msm_vidc/msm_venc.c
index 948676a..4573018 100644
--- a/drivers/media/video/msm_vidc/msm_venc.c
+++ b/drivers/media/video/msm_vidc/msm_venc.c
@@ -574,7 +574,6 @@
{
int i, rc = 0;
struct msm_vidc_inst *inst;
- struct hal_frame_size frame_sz;
unsigned long flags;
if (!q || !q->drv_priv) {
dprintk(VIDC_ERR, "Invalid input, q = %p\n", q);
@@ -598,26 +597,6 @@
dprintk(VIDC_ERR, "Failed to open instance\n");
break;
}
- frame_sz.buffer_type = HAL_BUFFER_INPUT;
- frame_sz.width = inst->prop.width;
- frame_sz.height = inst->prop.height;
- dprintk(VIDC_DBG, "width = %d, height = %d\n",
- frame_sz.width, frame_sz.height);
- rc = vidc_hal_session_set_property((void *)inst->session,
- HAL_PARAM_FRAME_SIZE, &frame_sz);
- if (rc) {
- dprintk(VIDC_ERR,
- "Failed to set framesize for Output port\n");
- break;
- }
- frame_sz.buffer_type = HAL_BUFFER_OUTPUT;
- rc = vidc_hal_session_set_property((void *)inst->session,
- HAL_PARAM_FRAME_SIZE, &frame_sz);
- if (rc) {
- dprintk(VIDC_ERR,
- "Failed to set hal property for framesize\n");
- break;
- }
rc = msm_comm_try_get_bufreqs(inst);
if (rc) {
dprintk(VIDC_ERR,
@@ -626,7 +605,9 @@
}
*num_planes = 1;
spin_lock_irqsave(&inst->lock, flags);
- *num_buffers = inst->buff_req.buffer[0].buffer_count_actual;
+ *num_buffers = inst->buff_req.buffer[0].buffer_count_actual =
+ max(*num_buffers, inst->buff_req.buffer[0].
+ buffer_count_actual);
spin_unlock_irqrestore(&inst->lock, flags);
dprintk(VIDC_DBG, "size = %d, alignment = %d, count = %d\n",
inst->buff_req.buffer[0].buffer_size,
@@ -648,7 +629,6 @@
static inline int start_streaming(struct msm_vidc_inst *inst)
{
int rc = 0;
- unsigned long flags;
struct vb2_buf_entry *temp;
struct list_head *ptr, *next;
@@ -679,7 +659,7 @@
"Failed to move inst: %p to start done state\n", inst);
goto fail_start;
}
- spin_lock_irqsave(&inst->lock, flags);
+ mutex_lock(&inst->sync_lock);
if (!list_empty(&inst->pendingq)) {
list_for_each_safe(ptr, next, &inst->pendingq) {
temp = list_entry(ptr, struct vb2_buf_entry, list);
@@ -693,7 +673,7 @@
kfree(temp);
}
}
- spin_unlock_irqrestore(&inst->lock, flags);
+ mutex_unlock(&inst->sync_lock);
return rc;
fail_start:
return rc;
@@ -1345,6 +1325,7 @@
int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
{
const struct msm_vidc_format *fmt = NULL;
+ struct hal_frame_size frame_sz;
int rc = 0;
int i;
if (!inst || !f) {
@@ -1366,6 +1347,26 @@
} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
inst->prop.width = f->fmt.pix_mp.width;
inst->prop.height = f->fmt.pix_mp.height;
+ frame_sz.buffer_type = HAL_BUFFER_INPUT;
+ frame_sz.width = inst->prop.width;
+ frame_sz.height = inst->prop.height;
+ dprintk(VIDC_DBG, "width = %d, height = %d\n",
+ frame_sz.width, frame_sz.height);
+ rc = vidc_hal_session_set_property((void *)inst->session,
+ HAL_PARAM_FRAME_SIZE, &frame_sz);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to set framesize for Output port\n");
+ goto exit;
+ }
+ frame_sz.buffer_type = HAL_BUFFER_OUTPUT;
+ rc = vidc_hal_session_set_property((void *)inst->session,
+ HAL_PARAM_FRAME_SIZE, &frame_sz);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to set hal property for framesize\n");
+ goto exit;
+ }
fmt = msm_comm_get_pixel_fmt_fourcc(venc_formats,
ARRAY_SIZE(venc_formats), f->fmt.pix_mp.pixelformat,
OUTPUT_PORT);
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.c b/drivers/media/video/msm_vidc/msm_vidc_common.c
index 1cad40f..4ff28d62 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_common.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.c
@@ -65,8 +65,8 @@
int i;
if (!load)
return 0;
- for (i = num_rows - 1; i > 1; i--) {
- if (load >= bus_table[i])
+ for (i = 0; i < num_rows; i++) {
+ if (load <= bus_table[i])
break;
}
dprintk(VIDC_DBG, "Required bus = %d\n", i);
@@ -234,7 +234,7 @@
k++;
}
if (i == size) {
- dprintk(VIDC_WARN, "Format not found\n");
+ dprintk(VIDC_INFO, "Format not found\n");
return NULL;
}
return &fmt[i];
@@ -252,7 +252,7 @@
break;
}
if (i == size) {
- dprintk(VIDC_WARN, "Format not found\n");
+ dprintk(VIDC_INFO, "Format not found\n");
return NULL;
}
return &fmt[i];
@@ -521,6 +521,7 @@
dqevent.id = 0;
v4l2_event_queue_fh(&inst->event_handler, &dqevent);
wake_up(&inst->kernel_event_queue);
+ show_stats(inst);
} else {
dprintk(VIDC_ERR,
"Failed to get valid response for session close\n");
@@ -571,6 +572,7 @@
vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
mutex_unlock(&inst->bufq[OUTPUT_PORT].lock);
wake_up(&inst->kernel_event_queue);
+ msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_EBD);
}
}
@@ -636,6 +638,7 @@
vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
mutex_unlock(&inst->bufq[CAPTURE_PORT].lock);
wake_up(&inst->kernel_event_queue);
+ msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_FBD);
} else {
/*
* FIXME:
@@ -1094,7 +1097,7 @@
}
if (msm_comm_scale_clocks(core, inst->session_type)) {
dprintk(VIDC_WARN, "Failed to scale clocks while closing\n");
- dprintk(VIDC_WARN, "Power might be impacted\n");
+ dprintk(VIDC_INFO, "Power might be impacted\n");
}
if (list_empty(&core->instances)) {
msm_comm_unset_ocmem(core);
@@ -1258,7 +1261,7 @@
rc = vidc_hal_session_start((void *) inst->session);
if (rc) {
dprintk(VIDC_ERR,
- "Failed to send load resources\n");
+ "Failed to send start\n");
goto exit;
}
change_inst_state(inst, MSM_VIDC_START);
@@ -1304,7 +1307,7 @@
rc = vidc_hal_session_release_res((void *) inst->session);
if (rc) {
dprintk(VIDC_ERR,
- "Failed to send load resources\n");
+ "Failed to send release resources\n");
goto exit;
}
change_inst_state(inst, MSM_VIDC_RELEASE_RESOURCES);
@@ -1328,7 +1331,7 @@
rc = vidc_hal_session_end((void *) inst->session);
if (rc) {
dprintk(VIDC_ERR,
- "Failed to send load resources\n");
+ "Failed to send close\n");
goto exit;
}
change_inst_state(inst, MSM_VIDC_OPEN);
@@ -1451,7 +1454,6 @@
int rc = 0;
struct vb2_queue *q;
struct msm_vidc_inst *inst;
- unsigned long flags;
struct vb2_buf_entry *entry;
struct vidc_frame_data frame_data;
q = vb->vb2_queue;
@@ -1468,10 +1470,9 @@
goto err_no_mem;
}
entry->vb = vb;
- dprintk(VIDC_DBG, "Queueing buffer in pendingq\n");
- spin_lock_irqsave(&inst->lock, flags);
+ mutex_lock(&inst->sync_lock);
list_add_tail(&entry->list, &inst->pendingq);
- spin_unlock_irqrestore(&inst->lock, flags);
+ mutex_unlock(&inst->sync_lock);
} else {
int64_t time_usec = timeval_to_ns(&vb->v4l2_buf.timestamp);
do_div(time_usec, NSEC_PER_USEC);
@@ -1501,6 +1502,9 @@
frame_data.alloc_len, frame_data.filled_len);
rc = vidc_hal_session_etb((void *) inst->session,
&frame_data);
+ if (!rc)
+ msm_vidc_debugfs_update(inst,
+ MSM_VIDC_DEBUGFS_EVENT_ETB);
dprintk(VIDC_DBG, "Sent etb to HAL\n");
} else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
struct vidc_seq_hdr seq_hdr;
@@ -1533,6 +1537,9 @@
} else {
rc = vidc_hal_session_ftb((void *)
inst->session, &frame_data);
+ if (!rc)
+ msm_vidc_debugfs_update(inst,
+ MSM_VIDC_DEBUGFS_EVENT_FTB);
}
inst->ftb_count++;
} else {
@@ -1777,18 +1784,46 @@
int rc = 0;
bool ip_flush = false;
bool op_flush = false;
+ struct list_head *ptr, *next;
+ struct vb2_buf_entry *temp;
+ struct mutex *lock;
ip_flush = flags & V4L2_QCOM_CMD_FLUSH_OUTPUT;
op_flush = flags & V4L2_QCOM_CMD_FLUSH_CAPTURE;
-
if (ip_flush && !op_flush) {
dprintk(VIDC_WARN, "Input only flush not supported\n");
return 0;
}
mutex_lock(&inst->sync_lock);
if (inst->in_reconfig && !ip_flush && op_flush) {
+ if (!list_empty(&inst->pendingq)) {
+ /*Execution can never reach here since port reconfig
+ * wont happen unless pendingq is emptied out
+ * (both pendingq and flush being secured with same
+ * lock). Printing a message here incase this breaks.*/
+ dprintk(VIDC_WARN,
+ "FLUSH BUG: Pending q not empty! It should be empty\n");
+ }
rc = vidc_hal_session_flush(inst->session,
HAL_FLUSH_OUTPUT);
} else {
+ if (!list_empty(&inst->pendingq)) {
+ /*If flush is called after queueing buffers but before
+ * streamon driver should flush the pending queue*/
+ list_for_each_safe(ptr, next, &inst->pendingq) {
+ temp =
+ list_entry(ptr, struct vb2_buf_entry, list);
+ if (temp->vb->v4l2_buf.type ==
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ lock = &inst->bufq[CAPTURE_PORT].lock;
+ else
+ lock = &inst->bufq[OUTPUT_PORT].lock;
+ mutex_lock(lock);
+ vb2_buffer_done(temp->vb, VB2_BUF_STATE_DONE);
+ mutex_unlock(lock);
+ list_del(&temp->list);
+ kfree(temp);
+ }
+ }
rc = vidc_hal_session_flush(inst->session,
HAL_FLUSH_ALL);
}
diff --git a/drivers/media/video/msm_vidc/msm_vidc_debug.c b/drivers/media/video/msm_vidc/msm_vidc_debug.c
index 7921f84..914c422 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_debug.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_debug.c
@@ -15,6 +15,7 @@
#define MAX_DBG_BUF_SIZE 4096
int msm_vidc_debug;
+int msm_fw_debug;
struct debug_buffer {
char ptr[MAX_DBG_BUF_SIZE];
@@ -89,6 +90,7 @@
goto failed_create_dir;
}
msm_vidc_debug = 0;
+ msm_fw_debug = 0;
snprintf(debugfs_name, MAX_DEBUGFS_NAME, "core%d", core->id);
dir = debugfs_create_dir(debugfs_name, parent);
if (!dir) {
@@ -104,6 +106,13 @@
dprintk(VIDC_ERR, "debugfs_create_file: fail\n");
goto failed_create_dir;
}
+ msm_vidc_debug = 0x3;
+ if (!debugfs_create_u32("fw_level", S_IRUGO | S_IWUSR,
+ parent, &msm_fw_debug)) {
+ dprintk(VIDC_ERR, "debugfs_create_file: fail\n");
+ goto failed_create_dir;
+ }
+ msm_fw_debug = 0x18;
failed_create_dir:
return dir;
}
@@ -180,7 +189,46 @@
dprintk(VIDC_ERR, "debugfs_create_file: fail\n");
goto failed_create_dir;
}
-
+ inst->debug.pdata[FRAME_PROCESSING].sampling = true;
failed_create_dir:
return dir;
}
+
+void msm_vidc_debugfs_update(struct msm_vidc_inst *inst,
+ enum msm_vidc_debugfs_event e)
+{
+ struct msm_vidc_debug *d = &inst->debug;
+ char a[64] = "Frame processing";
+ switch (e) {
+ case MSM_VIDC_DEBUGFS_EVENT_ETB:
+ inst->count.etb++;
+ if (inst->count.ftb > inst->count.fbd) {
+ d->pdata[FRAME_PROCESSING].name[0] = '\0';
+ tic(inst, FRAME_PROCESSING, a);
+ }
+ break;
+ case MSM_VIDC_DEBUGFS_EVENT_EBD:
+ inst->count.ebd++;
+ if (inst->count.ebd == inst->count.etb)
+ toc(inst, FRAME_PROCESSING);
+ break;
+ case MSM_VIDC_DEBUGFS_EVENT_FTB: {
+ inst->count.ftb++;
+ if (inst->count.etb > inst->count.ebd) {
+ d->pdata[FRAME_PROCESSING].name[0] = '\0';
+ tic(inst, FRAME_PROCESSING, a);
+ }
+ }
+ break;
+ case MSM_VIDC_DEBUGFS_EVENT_FBD:
+ inst->count.fbd++;
+ inst->debug.counter++;
+ if (inst->count.fbd == inst->count.ftb)
+ toc(inst, FRAME_PROCESSING);
+ break;
+ default:
+ dprintk(VIDC_ERR, "Invalid state in debugfs: %d\n", e);
+ break;
+ }
+}
+
diff --git a/drivers/media/video/msm_vidc/msm_vidc_debug.h b/drivers/media/video/msm_vidc/msm_vidc_debug.h
index b7928e9..1a51173 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_debug.h
+++ b/drivers/media/video/msm_vidc/msm_vidc_debug.h
@@ -14,28 +14,107 @@
#ifndef __MSM_VIDC_DEBUG__
#define __MSM_VIDC_DEBUG__
#include <linux/debugfs.h>
+#include <linux/delay.h>
#include "msm_vidc_internal.h"
#define VIDC_DBG_TAG "msm_vidc: %d: "
+/* To enable messages OR these values and
+ * echo the result to debugfs file.
+ *
+ * To enable all messages set debug_level = 0x101F
+ */
+
enum vidc_msg_prio {
- VIDC_ERR,
- VIDC_WARN,
- VIDC_INFO,
- VIDC_DBG,
+ VIDC_ERR = 0x0001,
+ VIDC_WARN = 0x0002,
+ VIDC_INFO = 0x0004,
+ VIDC_DBG = 0x0008,
+ VIDC_PROF = 0x0010,
+ VIDC_FW = 0x1000,
+};
+
+enum msm_vidc_debugfs_event {
+ MSM_VIDC_DEBUGFS_EVENT_ETB,
+ MSM_VIDC_DEBUGFS_EVENT_EBD,
+ MSM_VIDC_DEBUGFS_EVENT_FTB,
+ MSM_VIDC_DEBUGFS_EVENT_FBD,
};
extern int msm_vidc_debug;
-#define dprintk(level, fmt, arg...) \
- do { \
- if (msm_vidc_debug >= level) \
- printk(KERN_DEBUG VIDC_DBG_TAG fmt, \
- level, ## arg); \
+extern int msm_fw_debug;
+
+#define dprintk(__level, __fmt, arg...) \
+ do { \
+ if (msm_vidc_debug & __level) \
+ printk(KERN_DEBUG VIDC_DBG_TAG \
+ __fmt, __level, ## arg); \
} while (0)
struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core,
struct dentry *parent);
struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst,
struct dentry *parent);
+void msm_vidc_debugfs_update(struct msm_vidc_inst *inst,
+ enum msm_vidc_debugfs_event e);
+
+static inline void tic(struct msm_vidc_inst *i, enum profiling_points p,
+ char *b)
+{
+ struct timeval __ddl_tv;
+ if (!i->debug.pdata[p].name[0])
+ memcpy(i->debug.pdata[p].name, b, 64);
+ if ((msm_vidc_debug & VIDC_PROF) &&
+ i->debug.pdata[p].sampling) {
+ do_gettimeofday(&__ddl_tv);
+ i->debug.pdata[p].start =
+ (__ddl_tv.tv_sec * 1000) + (__ddl_tv.tv_usec / 1000);
+ i->debug.pdata[p].sampling = false;
+ }
+}
+
+static inline void toc(struct msm_vidc_inst *i, enum profiling_points p)
+{
+ struct timeval __ddl_tv;
+ if ((msm_vidc_debug & VIDC_PROF) &&
+ !i->debug.pdata[p].sampling) {
+ do_gettimeofday(&__ddl_tv);
+ i->debug.pdata[p].stop = (__ddl_tv.tv_sec * 1000)
+ + (__ddl_tv.tv_usec / 1000);
+ i->debug.pdata[p].cumulative =
+ (i->debug.pdata[p].stop - i->debug.pdata[p].start);
+ if (i->count.fbd) {
+ if (i->debug.pdata[p].average != 0) {
+ i->debug.pdata[p].average = ((i->debug.pdata[p].
+ average * (i->count.fbd -
+ i->debug.counter) +
+ i->debug.pdata[p].cumulative)
+ / i->count.fbd);
+ } else {
+ i->debug.pdata[p].average =
+ i->debug.pdata[p].cumulative
+ / i->count.fbd;
+ }
+ }
+ i->debug.counter = 0;
+ i->debug.pdata[p].cumulative = 0;
+ i->debug.pdata[p].sampling = true;
+ }
+}
+
+static inline void show_stats(struct msm_vidc_inst *i)
+{
+ int x;
+ for (x = 0; x < MAX_PROFILING_POINTS; x++) {
+ if ((i->debug.pdata[x].name[0]) &&
+ (msm_vidc_debug & VIDC_PROF)) {
+ dprintk(VIDC_PROF, "%s averaged %d ms/sample\n",
+ i->debug.pdata[x].name,
+ i->debug.pdata[x].average);
+ dprintk(VIDC_PROF, "%s Samples: %d",
+ i->debug.pdata[x].name, i->count.fbd);
+ }
+ }
+}
#endif
diff --git a/drivers/media/video/msm_vidc/msm_vidc_internal.h b/drivers/media/video/msm_vidc/msm_vidc_internal.h
index 1ea92fc..9806d771 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_internal.h
+++ b/drivers/media/video/msm_vidc/msm_vidc_internal.h
@@ -193,6 +193,37 @@
struct mutex lock;
};
+enum profiling_points {
+ SYS_INIT = 0,
+ SESSION_INIT,
+ LOAD_RESOURCES,
+ FRAME_PROCESSING,
+ FW_IDLE,
+ MAX_PROFILING_POINTS,
+};
+
+struct buf_count {
+ int etb;
+ int ftb;
+ int fbd;
+ int ebd;
+};
+
+struct profile_data {
+ int start;
+ int stop;
+ int cumulative;
+ char name[64];
+ int sampling;
+ int average;
+};
+
+struct msm_vidc_debug {
+ struct profile_data pdata[MAX_PROFILING_POINTS];
+ int profile;
+ int counter;
+};
+
struct msm_vidc_core {
struct list_head list;
struct mutex sync_lock;
@@ -240,6 +271,8 @@
u32 ftb_count;
struct vb2_buffer *vb2_seq_hdr;
void *priv;
+ struct msm_vidc_debug debug;
+ struct buf_count count;
};
extern struct msm_vidc_drv *vidc_driver;
@@ -259,5 +292,4 @@
void handle_cmd_response(enum command_response cmd, void *data);
int msm_vidc_ocmem_notify_handler(struct notifier_block *this,
unsigned long event, void *data);
-
#endif
diff --git a/drivers/media/video/msm_vidc/vidc_hal.c b/drivers/media/video/msm_vidc/vidc_hal.c
index f0d0e73..89f0273 100644
--- a/drivers/media/video/msm_vidc/vidc_hal.c
+++ b/drivers/media/video/msm_vidc/vidc_hal.c
@@ -248,7 +248,7 @@
struct vidc_iface_q_info *qinfo;
if (!info || !packet || !pb_tx_req_is_set) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params");
return -EINVAL;
}
@@ -311,11 +311,11 @@
int rc = 0;
if (!mem || !clnt || !size) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params");
return -EINVAL;
}
vmem = (struct vidc_mem_addr *)mem;
- dprintk(VIDC_WARN, "start to alloc: size:%d, Flags: %d", size, flags);
+ dprintk(VIDC_INFO, "start to alloc: size:%d, Flags: %d", size, flags);
alloc = msm_smem_alloc(clnt, size, align, flags, domain, 1, 1);
dprintk(VIDC_DBG, "Alloc done");
@@ -390,7 +390,7 @@
int result = -EPERM;
if (!device || !pkt) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params");
return -EINVAL;
}
@@ -422,7 +422,7 @@
struct vidc_iface_q_info *q_info;
if (!pkt) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params");
return -EINVAL;
}
spin_lock(&device->read_lock);
@@ -456,7 +456,7 @@
struct vidc_iface_q_info *q_info;
if (!pkt) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params");
return -EINVAL;
}
spin_lock(&device->read_lock);
@@ -592,7 +592,7 @@
u32 ctrl_status = 0, count = 0, rc = 0;
int max_tries = 100;
write_register(device->hal_data->register_base_addr,
- VIDC_WRAPPER_INTR_MASK, 0, 0);
+ VIDC_WRAPPER_INTR_MASK, 0x8, 0);
write_register(device->hal_data->register_base_addr,
VIDC_CPU_CS_SCIACMDARG3, 1, 0);
while (!ctrl_status && count < max_tries) {
@@ -644,6 +644,24 @@
VIDC_VENUS0_WRAPPER_VBIF_REQ_PRIORITY, 0x5555556, 0);
}
+static int vidc_hal_sys_set_debug(struct hal_device *device, int debug)
+{
+ struct hfi_debug_config *hfi;
+ u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE];
+ struct hfi_cmd_sys_set_property_packet *pkt =
+ (struct hfi_cmd_sys_set_property_packet *) &packet;
+ pkt->size = sizeof(struct hfi_cmd_sys_set_property_packet) +
+ sizeof(struct hfi_debug_config) + sizeof(u32);
+ pkt->packet_type = HFI_CMD_SYS_SET_PROPERTY;
+ pkt->num_properties = 1;
+ pkt->rg_property_data[0] = HFI_PROPERTY_SYS_DEBUG_CONFIG;
+ hfi = (struct hfi_debug_config *) &pkt->rg_property_data[1];
+ hfi->debug_config = debug;
+ if (vidc_hal_iface_cmdq_write(device, pkt))
+ return -ENOTEMPTY;
+ return 0;
+}
+
int vidc_hal_core_init(void *device, int domain)
{
struct hfi_cmd_sys_init_packet pkt;
@@ -760,7 +778,7 @@
"times: %d interrupt_status: %d",
(u32) device, ++device->reg_count, intr_status);
} else {
- dprintk(VIDC_WARN, "SPURIOUS_INTR for device: 0x%x: "
+ dprintk(VIDC_INFO, "SPURIOUS_INTR for device: 0x%x: "
"times: %d interrupt_status: %d",
(u32) device, ++device->spur_count, intr_status);
}
@@ -1437,23 +1455,13 @@
prop->multi_slice);
break;
}
+ hfi->slice_size = prop->slice_size;
pkt->size += sizeof(u32) + sizeof(struct
hfi_multi_slice_control);
break;
}
case HAL_CONFIG_VPE_DEINTERLACE:
break;
- case HAL_SYS_DEBUG_CONFIG:
- {
- struct hfi_debug_config *hfi;
- pkt->rg_property_data[0] = HFI_PROPERTY_SYS_DEBUG_CONFIG;
- hfi = (struct hfi_debug_config *) &pkt->rg_property_data[1];
- hfi->debug_config = ((struct hal_debug_config *)
- pdata)->debug_config;
- pkt->size = sizeof(struct hfi_cmd_sys_set_property_packet) +
- sizeof(struct hfi_debug_config);
- break;
- }
/* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */
case HAL_CONFIG_BUFFER_REQUIREMENTS:
case HAL_CONFIG_PRIORITY:
@@ -1624,7 +1632,7 @@
if (device) {
dev = device;
} else {
- dprintk(VIDC_ERR, ":invalid device");
+ dprintk(VIDC_ERR, "invalid device");
return NULL;
}
@@ -1644,6 +1652,8 @@
pkt.session_codec = codec_type;
if (vidc_hal_iface_cmdq_write(dev, &pkt))
return NULL;
+ if (vidc_hal_sys_set_debug(dev, msm_fw_debug))
+ dprintk(VIDC_ERR, "Setting fw_debug msg ON failed");
return (void *) new_session;
}
@@ -1657,7 +1667,7 @@
if (session_id) {
session = session_id;
} else {
- dprintk(VIDC_ERR, ":invalid session");
+ dprintk(VIDC_ERR, "invalid session");
return -ENODEV;
}
@@ -1693,7 +1703,7 @@
struct hal_session *session;
if (!sess || !buffer_info) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params");
return -EINVAL;
} else {
session = sess;
@@ -1757,7 +1767,7 @@
struct hal_session *session;
if (!sess || !buffer_info) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params");
return -EINVAL;
} else {
session = sess;
@@ -1849,7 +1859,7 @@
struct hal_session *session;
if (!sess || !input_frame) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params");
return -EINVAL;
} else {
session = sess;
@@ -1907,7 +1917,7 @@
struct hal_session *session;
if (!sess || !output_frame) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params");
return -EINVAL;
} else {
session = sess;
@@ -1939,7 +1949,7 @@
struct hal_session *session;
if (!sess || !seq_hdr) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params");
return -EINVAL;
} else {
session = sess;
@@ -1966,7 +1976,7 @@
struct hal_session *session;
if (!sess || !seq_hdr) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params");
return -EINVAL;
} else {
session = sess;
@@ -1993,7 +2003,7 @@
if (sess) {
session = sess;
} else {
- dprintk(VIDC_ERR, ":invalid session");
+ dprintk(VIDC_ERR, "invalid session");
return -ENODEV;
}
@@ -2016,7 +2026,7 @@
if (sess) {
session = sess;
} else {
- dprintk(VIDC_ERR, ":invalid session");
+ dprintk(VIDC_ERR, "invalid session");
return -ENODEV;
}
diff --git a/drivers/media/video/msm_vidc/vidc_hal_api.h b/drivers/media/video/msm_vidc/vidc_hal_api.h
index 659cf7e..879418d 100644
--- a/drivers/media/video/msm_vidc/vidc_hal_api.h
+++ b/drivers/media/video/msm_vidc/vidc_hal_api.h
@@ -210,7 +210,7 @@
enum hal_mpeg4_profile {
HAL_MPEG4_PROFILE_SIMPLE = 0x00000001,
- HAL_MPEG4_PROFILE_SIMPLESCALABLE = 0x00000002,
+ HAL_MPEG4_PROFILE_ADVANCEDSIMPLE = 0x00000002,
HAL_MPEG4_PROFILE_CORE = 0x00000004,
HAL_MPEG4_PROFILE_MAIN = 0x00000008,
HAL_MPEG4_PROFILE_NBIT = 0x00000010,
@@ -224,7 +224,7 @@
HAL_MPEG4_PROFILE_ADVANCEDCODING = 0x00001000,
HAL_MPEG4_PROFILE_ADVANCEDCORE = 0x00002000,
HAL_MPEG4_PROFILE_ADVANCEDSCALABLE = 0x00004000,
- HAL_MPEG4_PROFILE_ADVANCEDSIMPLE = 0x00008000,
+ HAL_MPEG4_PROFILE_SIMPLESCALABLE = 0x00008000,
HAL_UNUSED_MPEG4_PROFILE = 0x10000000,
};
diff --git a/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c b/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
index 795024d..043ed73 100644
--- a/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
+++ b/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
@@ -530,7 +530,7 @@
struct hal_session *session;
if (!msg_hdr) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params");
return;
}
@@ -703,7 +703,6 @@
struct hfi_msg_sys_session_end_done_packet *pkt)
{
struct msm_vidc_cb_cmd_done cmd_done;
- struct list_head *curr, *next;
struct hal_session *sess_close;
dprintk(VIDC_DBG, "RECEIVED:SESSION_END_DONE");
@@ -715,13 +714,11 @@
return;
}
- list_for_each_safe(curr, next, &device->sess_head) {
- sess_close = list_entry(curr, struct hal_session, list);
- dprintk(VIDC_INFO, "deleted the session: 0x%x",
- sess_close->session_id);
- list_del(&sess_close->list);
- kfree(sess_close);
- }
+ sess_close = (struct hal_session *)pkt->session_id;
+ dprintk(VIDC_INFO, "deleted the session: 0x%x",
+ sess_close->session_id);
+ list_del(&sess_close->list);
+ kfree(sess_close);
memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
cmd_done.device_id = device->device_id;
@@ -849,7 +846,12 @@
if (device) {
while (!vidc_hal_iface_msgq_read(device, packet)) {
hal_process_msg_packet(device,
- (struct vidc_hal_msg_pkt_hdr *) packet);
+ (struct vidc_hal_msg_pkt_hdr *) packet);
+ }
+ while (!vidc_hal_iface_dbgq_read(device, packet)) {
+ struct hfi_msg_sys_debug_packet *pkt =
+ (struct hfi_msg_sys_debug_packet *) packet;
+ dprintk(VIDC_FW, "FW-SAYS: %s", pkt->rg_msg_data);
}
} else {
dprintk(VIDC_ERR, "SPURIOUS_INTERRUPT");
diff --git a/drivers/media/video/msm_wfd/wfd-ioctl.c b/drivers/media/video/msm_wfd/wfd-ioctl.c
index 04b787a..d8080dd 100644
--- a/drivers/media/video/msm_wfd/wfd-ioctl.c
+++ b/drivers/media/video/msm_wfd/wfd-ioctl.c
@@ -91,6 +91,7 @@
u32 out_buf_size;
struct list_head input_mem_list;
struct wfd_stats stats;
+ struct completion stop_mdp_thread;
};
struct wfd_vid_buffer {
@@ -522,7 +523,7 @@
static int mdp_output_thread(void *data)
{
- int rc = 0;
+ int rc = 0, no_sig_wait = 0;
struct file *filp = (struct file *)data;
struct wfd_inst *inst = filp->private_data;
struct wfd_device *wfd_dev =
@@ -531,6 +532,14 @@
struct mem_region *mregion;
struct vsg_buf_info ibuf_vsg;
while (!kthread_should_stop()) {
+ if (rc) {
+ WFD_MSG_DBG("%s() error in output thread\n", __func__);
+ if (!no_sig_wait) {
+ wait_for_completion(&inst->stop_mdp_thread);
+ no_sig_wait = 1;
+ }
+ continue;
+ }
WFD_MSG_DBG("waiting for mdp output\n");
rc = v4l2_subdev_call(&wfd_dev->mdp_sdev,
core, ioctl, MDP_DQ_BUFFER, (void *)&obuf_mdp);
@@ -540,7 +549,7 @@
WFD_MSG_ERR("MDP reported err %d\n", rc);
WFD_MSG_ERR("Streamoff called\n");
- break;
+ continue;
} else {
wfd_stats_update(&inst->stats,
WFD_STAT_EVENT_MDP_DEQUEUE);
@@ -550,7 +559,7 @@
if (!mregion) {
WFD_MSG_ERR("mdp cookie is null\n");
rc = -EINVAL;
- break;
+ continue;
}
ibuf_vsg.mdp_buf_info = obuf_mdp;
@@ -565,7 +574,7 @@
if (rc) {
WFD_MSG_ERR("Failed to queue frame to vsg\n");
- break;
+ continue;
} else {
wfd_stats_update(&inst->stats,
WFD_STAT_EVENT_VSG_QUEUE);
@@ -599,7 +608,7 @@
WFD_MSG_ERR("Failed to start vsg\n");
goto subdev_start_fail;
}
-
+ init_completion(&inst->stop_mdp_thread);
inst->mdp_task = kthread_run(mdp_output_thread, priv_data,
"mdp_output_thread");
if (IS_ERR(inst->mdp_task)) {
@@ -634,6 +643,7 @@
if (rc)
WFD_MSG_ERR("Failed to stop VSG\n");
+ complete(&inst->stop_mdp_thread);
kthread_stop(inst->mdp_task);
rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
ENCODE_FLUSH, (void *)inst->venc_inst);
diff --git a/drivers/media/video/vcap_v4l2.c b/drivers/media/video/vcap_v4l2.c
index 7ac78cb..7757b5c 100644
--- a/drivers/media/video/vcap_v4l2.c
+++ b/drivers/media/video/vcap_v4l2.c
@@ -19,13 +19,23 @@
#include <linux/videodev2.h>
#include <linux/platform_device.h>
#include <linux/memory_alloc.h>
+#include <linux/ctype.h>
+#include <linux/debugfs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/iommu.h>
#include <mach/board.h>
#include <mach/gpio.h>
#include <mach/irqs.h>
+#include <mach/clk.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+#include <mach/iommu.h>
+#include <mach/iommu_domains.h>
#include <media/videobuf2-msm-mem.h>
-
#include <media/videobuf2-vmalloc.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
@@ -33,16 +43,9 @@
#include <media/v4l2-fh.h>
#include <media/v4l2-common.h>
#include <media/v4l2-event.h>
-#include <linux/regulator/consumer.h>
-#include <mach/clk.h>
-#include <linux/clk.h>
-#include <linux/interrupt.h>
-#include <mach/msm_bus.h>
-#include <mach/msm_bus_board.h>
-#include <mach/iommu_domains.h>
-
#include <media/vcap_v4l2.h>
#include <media/vcap_fmt.h>
+
#include "vcap_vc.h"
#include "vcap_vp.h"
@@ -51,15 +54,33 @@
static struct vcap_dev *vcap_ctrl;
-static unsigned debug;
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *vcap_debugfs_base;
+static struct reg_range debug_reg_range[] = {
+ {
+ VCAP_REG_RANGE_1_MIN,
+ VCAP_REG_RANGE_1_MAX,
+ },
+ {
+ VCAP_REG_RANGE_2_MIN,
+ VCAP_REG_RANGE_2_MAX,
+ },
+ {
+ VCAP_REG_RANGE_3_MIN,
+ VCAP_REG_RANGE_3_MAX,
+ },
+ {
+ VCAP_REG_RANGE_4_MIN,
+ VCAP_REG_RANGE_4_MAX,
+ },
+ {
+ VCAP_REG_RANGE_5_MIN,
+ VCAP_REG_RANGE_5_MAX,
+ },
+};
+#endif
-#define dprintk(level, fmt, arg...) \
- do { \
- if (debug >= level) \
- printk(KERN_DEBUG "VCAP: " fmt, ## arg); \
- } while (0)
-
-int vcap_reg_powerup(struct vcap_dev *dev)
+static int vcap_reg_powerup(struct vcap_dev *dev)
{
dev->fs_vcap = regulator_get(NULL, "fs_vcap");
if (IS_ERR(dev->fs_vcap)) {
@@ -75,7 +96,7 @@
return 0;
}
-void vcap_reg_powerdown(struct vcap_dev *dev)
+static void vcap_reg_powerdown(struct vcap_dev *dev)
{
if (dev->fs_vcap == NULL)
return;
@@ -85,13 +106,13 @@
return;
}
-int config_gpios(int on, struct vcap_platform_data *pdata)
+static int vcap_config_gpios(int on, struct vcap_platform_data *pdata)
{
int i, ret;
int num_gpios = pdata->num_gpios;
unsigned *gpios = pdata->gpios;
- dprintk(4, "GPIO config start\n");
+ pr_debug("GPIO config start\n");
if (on) {
for (i = 0; i < num_gpios; i++) {
ret = gpio_request(gpios[i], "vcap:vc");
@@ -112,7 +133,7 @@
for (i = 0; i < num_gpios; i++)
gpio_free(gpios[i]);
}
- dprintk(4, "GPIO config exit\n");
+ pr_debug("GPIO config exit\n");
return 0;
gpio_failed:
for (i--; i >= 0; i--)
@@ -120,7 +141,7 @@
return -EINVAL;
}
-int vcap_clk_powerup(struct vcap_dev *dev, struct device *ddev,
+static int vcap_clk_powerup(struct vcap_dev *dev, struct device *ddev,
unsigned long rate)
{
int ret = 0;
@@ -151,6 +172,7 @@
pr_err("%s: Failed core set_rate %d\n", __func__, ret);
goto fail_vcap_clk;
}
+ dev->dbg_p.clk_rate = (uint32_t) rate;
dev->vcap_npl_clk = clk_get(ddev, "vcap_npl_clk");
if (IS_ERR(dev->vcap_npl_clk)) {
@@ -198,6 +220,7 @@
dev->vcap_npl_clk = NULL;
fail_vcap_clk:
+ dev->dbg_p.clk_rate = 0;
clk_disable(dev->vcap_clk);
fail_vcap_clk_unprep:
clk_unprepare(dev->vcap_clk);
@@ -206,7 +229,7 @@
return -EINVAL;
}
-void vcap_clk_powerdown(struct vcap_dev *dev)
+static void vcap_clk_powerdown(struct vcap_dev *dev)
{
if (dev->vcap_p_clk != NULL) {
clk_disable(dev->vcap_p_clk);
@@ -228,9 +251,11 @@
clk_put(dev->vcap_clk);
dev->vcap_clk = NULL;
}
+
+ dev->dbg_p.clk_rate = 0;
}
-int vcap_get_bus_client_handle(struct vcap_dev *dev)
+static int vcap_get_bus_client_handle(struct vcap_dev *dev)
{
struct msm_bus_scale_pdata *vcap_axi_client_pdata =
dev->vcap_pdata->bus_client_pdata;
@@ -240,10 +265,11 @@
return 0;
}
-int vcap_enable(struct vcap_dev *dev, struct device *ddev,
+static int vcap_enable(struct vcap_dev *dev, struct device *ddev,
unsigned long rate)
{
int rc;
+ pr_debug("Enter %s", __func__);
rc = vcap_reg_powerup(dev);
if (rc < 0)
@@ -254,13 +280,24 @@
rc = vcap_get_bus_client_handle(dev);
if (rc < 0)
goto bus_r_failed;
- rc = config_gpios(1, dev->vcap_pdata);
+ rc = vcap_config_gpios(1, dev->vcap_pdata);
if (rc < 0)
goto gpio_failed;
+ rc = iommu_attach_device(dev->iommu_vcap_domain, dev->vc_iommu_ctx);
+ if (rc < 0)
+ goto vc_iommu_attach_failed;
+ rc = iommu_attach_device(dev->iommu_vcap_domain, dev->vp_iommu_ctx);
+ if (rc < 0)
+ goto vp_iommu_attach_failed;
writel_relaxed(0x00030003, VCAP_OFFSET(0xD78));
writel_relaxed(0x00030003, VCAP_OFFSET(0xD7C));
+ pr_debug("Success Exit %s", __func__);
return 0;
+vp_iommu_attach_failed:
+ iommu_detach_device(dev->iommu_vcap_domain, dev->vc_iommu_ctx);
+vc_iommu_attach_failed:
+ vcap_config_gpios(0, dev->vcap_pdata);
gpio_failed:
msm_bus_scale_unregister_client(dev->bus_client_handle);
dev->bus_client_handle = 0;
@@ -272,17 +309,38 @@
return rc;
}
-int vcap_disable(struct vcap_dev *dev)
+static int vcap_disable(struct vcap_dev *dev)
{
- config_gpios(0, dev->vcap_pdata);
+ pr_debug("Enter %s", __func__);
+ iommu_detach_device(dev->iommu_vcap_domain, dev->vp_iommu_ctx);
+ iommu_detach_device(dev->iommu_vcap_domain, dev->vc_iommu_ctx);
+
+ vcap_config_gpios(0, dev->vcap_pdata);
msm_bus_scale_unregister_client(dev->bus_client_handle);
dev->bus_client_handle = 0;
+ dev->dbg_p.bw_request = 0;
vcap_clk_powerdown(dev);
vcap_reg_powerdown(dev);
return 0;
}
+static int vcap_register_domain(void)
+{
+ struct msm_iova_partition vcap_partition = {
+ .start = 0,
+ .size = SZ_2G,
+ };
+ struct msm_iova_layout vcap_layout = {
+ .partitions = &vcap_partition,
+ .npartitions = 1,
+ .client_name = "vcap",
+ .domain_flags = 0,
+ };
+
+ return msm_register_domain(&vcap_layout);
+}
+
enum vcap_op_mode determine_mode(struct vcap_client_data *cd)
{
if (cd->set_cap == 1 && cd->set_vp_o == 0 &&
@@ -311,34 +369,34 @@
struct vb2_buffer *vb;
if (q->fileio) {
- dprintk(1, "%s: file io in progress\n", __func__);
+ pr_debug("%s: file io in progress\n", __func__);
return -EBUSY;
}
if (b->type != q->type) {
- dprintk(1, "%s: invalid buffer type\n", __func__);
+ pr_debug("%s: invalid buffer type\n", __func__);
return -EINVAL;
}
if (b->index >= q->num_buffers) {
- dprintk(1, "%s: buffer index out of range\n", __func__);
+ pr_debug("%s: buffer index out of range\n", __func__);
return -EINVAL;
}
vb = q->bufs[b->index];
if (NULL == vb) {
- dprintk(1, "%s: buffer is NULL\n", __func__);
+ pr_debug("%s: buffer is NULL\n", __func__);
return -EINVAL;
}
if (b->memory != q->memory) {
- dprintk(1, "%s: invalid memory type\n", __func__);
+ pr_debug("%s: invalid memory type\n", __func__);
return -EINVAL;
}
if (vb->state != VB2_BUF_STATE_DEQUEUED &&
vb->state != VB2_BUF_STATE_PREPARED) {
- dprintk(1, "%s: buffer already in use\n", __func__);
+ pr_err("%s: buffer already in use\n", __func__);
return -EINVAL;
}
@@ -361,17 +419,17 @@
unsigned long flags;
if (q->fileio) {
- dprintk(1, "%s: file io in progress\n", __func__);
+ pr_debug("%s: file io in progress\n", __func__);
return -EBUSY;
}
if (b->type != q->type) {
- dprintk(1, "%s: invalid buffer type\n", __func__);
+ pr_debug("%s: invalid buffer type\n", __func__);
return -EINVAL;
}
if (!q->streaming) {
- dprintk(1, "Streaming off, will not wait for buffers\n");
+ pr_debug("Streaming off, will not wait for buffers\n");
return -EINVAL;
}
@@ -384,13 +442,13 @@
switch (vb->state) {
case VB2_BUF_STATE_DONE:
- dprintk(3, "%s: Returning done buffer\n", __func__);
+ pr_debug("%s: Returning done buffer\n", __func__);
break;
case VB2_BUF_STATE_ERROR:
- dprintk(3, "%s: Ret done buf with err\n", __func__);
+ pr_debug("%s: Ret done buf with err\n", __func__);
break;
default:
- dprintk(1, "%s: Invalid buffer state\n", __func__);
+ pr_debug("%s: Invalid buffer state\n", __func__);
return -EINVAL;
}
@@ -402,7 +460,7 @@
return 0;
}
- dprintk(1, "No buffers to dequeue\n");
+ pr_debug("%s: No buffers to dequeue\n", __func__);
return -EAGAIN;
}
@@ -415,28 +473,28 @@
int rc;
if (q->fileio) {
- dprintk(1, "%s: file io in progress\n", __func__);
+ pr_debug("%s: file io in progress\n", __func__);
return -EBUSY;
}
if (b->type != q->type) {
- dprintk(1, "%s: invalid buffer type\n", __func__);
+ pr_debug("%s: invalid buffer type\n", __func__);
return -EINVAL;
}
if (b->index >= q->num_buffers) {
- dprintk(1, "%s: buffer index out of range\n", __func__);
+ pr_debug("%s: buffer index out of range\n", __func__);
return -EINVAL;
}
vb = q->bufs[b->index];
if (NULL == vb) {
- dprintk(1, "%s: buffer is NULL\n", __func__);
+ pr_debug("%s: buffer is NULL\n", __func__);
return -EINVAL;
}
if (vb->state != VB2_BUF_STATE_DEQUEUED) {
- dprintk(1, "%s: buffer already in use\n", __func__);
+ pr_debug("%s: buffer already in use\n", __func__);
return -EINVAL;
}
@@ -448,8 +506,9 @@
buf->ion_handle = NULL;
return -ENOMEM;
}
- rc = ion_phys(dev->ion_client, buf->ion_handle,
- &buf->paddr, (size_t *)&len);
+ rc = ion_map_iommu(dev->ion_client, buf->ion_handle,
+ dev->domain_num, 0, SZ_4K, 0, &buf->paddr, &len,
+ 0, 0);
if (rc < 0) {
pr_err("%s: Could not get phys addr\n", __func__);
ion_free(dev->ion_client, buf->ion_handle);
@@ -468,10 +527,11 @@
buf = container_of(vb, struct vcap_buffer, vb);
if (buf->ion_handle == NULL) {
- dprintk(1, "%s: no ION handle to free\n", __func__);
+ pr_debug("%s: no ION handle to free\n", __func__);
return;
}
buf->paddr = 0;
+ ion_unmap_iommu(dev->ion_client, buf->ion_handle, dev->domain_num, 0);
ion_free(dev->ion_client, buf->ion_handle);
buf->ion_handle = NULL;
return;
@@ -546,7 +606,7 @@
static int capture_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct vcap_client_data *c_data = vb2_get_drv_priv(vq);
- dprintk(2, "VC start streaming\n");
+ pr_debug("VC start streaming\n");
return vc_start_capture(c_data);
}
@@ -643,7 +703,7 @@
static int vp_in_start_streaming(struct vb2_queue *vq, unsigned int count)
{
- dprintk(2, "VP IN start streaming\n");
+ pr_debug("VP IN start streaming\n");
return 0;
}
@@ -652,7 +712,8 @@
struct vcap_client_data *c_data = vb2_get_drv_priv(vq);
struct vb2_buffer *vb;
- dprintk(2, "VP stop streaming\n");
+ pr_debug("VP IN stop streaming\n");
+ vp_stop_capture(c_data);
while (!list_empty(&c_data->vp_action.in_active)) {
struct vcap_buffer *buf;
@@ -749,7 +810,7 @@
struct vcap_client_data *c_data = vb2_get_drv_priv(vq);
struct vb2_buffer *vb;
- dprintk(2, "VP out q stop streaming\n");
+ pr_debug("VP OUT q stop streaming\n");
vp_stop_capture(c_data);
while (!list_empty(&c_data->vp_action.out_active)) {
@@ -838,6 +899,7 @@
size = (c_data->vc_format.hactive_end -
c_data->vc_format.hactive_start);
+ size = VCAP_STRIDE_CALC(size);
if (c_data->vc_format.color_space)
size *= 3;
@@ -890,7 +952,7 @@
struct vcap_dev *dev = c_data->dev;
int rc;
- dprintk(3, "In Req Buf %08x\n", (unsigned int)rb->type);
+ pr_debug("VCAP: In Req Buf %08x\n", (unsigned int)rb->type);
c_data->op_mode = determine_mode(c_data);
if (c_data->op_mode == UNKNOWN_VCAP_OP) {
pr_err("VCAP Error: %s: VCAP in unknown mode\n", __func__);
@@ -968,25 +1030,25 @@
struct vb2_queue *q;
int rc;
- dprintk(3, "In Q Buf %08x\n", (unsigned int)p->type);
+ pr_debug("VCAP In Q Buf %08x\n", (unsigned int)p->type);
switch (p->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
if (c_data->op_mode == VC_AND_VP_VCAP_OP) {
/* If buffer in vp_in_q it will be coming back */
q = &c_data->vp_in_vidq;
if (p->index >= q->num_buffers) {
- dprintk(1, "qbuf: buffer index out of range\n");
+ pr_debug("VCAP qbuf: buffer index out of range\n");
return -EINVAL;
}
vb = q->bufs[p->index];
if (NULL == vb) {
- dprintk(1, "qbuf: buffer is NULL\n");
+ pr_debug("VCAP qbuf: buffer is NULL\n");
return -EINVAL;
}
if (vb->state != VB2_BUF_STATE_DEQUEUED) {
- dprintk(1, "qbuf: buffer already in use\n");
+ pr_debug("VCAP qbuf: buffer already in use\n");
return -EINVAL;
}
rc = get_phys_addr(c_data->dev, &c_data->vc_vidq, p);
@@ -1035,7 +1097,7 @@
struct vcap_client_data *c_data = to_client_data(file->private_data);
int rc;
- dprintk(3, "In DQ Buf %08x\n", (unsigned int)p->type);
+ pr_debug("VCAP In DQ Buf %08x\n", (unsigned int)p->type);
switch (p->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
if (c_data->op_mode == VC_AND_VP_VCAP_OP)
@@ -1072,18 +1134,18 @@
int streamon_validate_q(struct vb2_queue *q)
{
if (q->fileio) {
- dprintk(1, "streamon: file io in progress\n");
+ pr_debug("%s: file io in progress\n", __func__);
return -EBUSY;
}
if (q->streaming) {
- dprintk(1, "streamon: already streaming\n");
+ pr_debug("%s: already streaming\n", __func__);
return -EBUSY;
}
if (V4L2_TYPE_IS_OUTPUT(q->type)) {
if (list_empty(&q->queued_list)) {
- dprintk(1, "streamon: no output buffers queued\n");
+ pr_debug("%s: no output buffers queued\n", __func__);
return -EINVAL;
}
}
@@ -1103,10 +1165,11 @@
idx++;
} while (idx < length);
if (idx == length) {
- pr_err("VCAP: Defaulting to highest BW request\n");
+ pr_info("VCAP: Defaulting to highest BW request\n");
idx--;
}
msm_bus_scale_client_update_request(dev->bus_client_handle, idx);
+ dev->dbg_p.bw_request = bus_vectors[idx].vectors[0].ab;
return 0;
}
@@ -1118,7 +1181,7 @@
unsigned long rate;
long rate_rc;
- dprintk(3, "In Stream ON\n");
+ pr_debug("VCAP: In Stream ON\n");
if (determine_mode(c_data) != c_data->op_mode) {
pr_err("VCAP Error: %s: s_fmt called after req_buf", __func__);
return -ENOTRECOVERABLE;
@@ -1160,6 +1223,8 @@
if (rc < 0)
goto free_res;
+ dev->dbg_p.clk_rate = (uint32_t) rate;
+
rate = (c_data->vc_format.hactive_end -
c_data->vc_format.hactive_start);
@@ -1204,6 +1269,8 @@
}
rate = (unsigned long)rate_rc;
rc = clk_set_rate(dev->vcap_clk, rate);
+
+ dev->dbg_p.clk_rate = (uint32_t) rate;
if (rc < 0)
goto free_res;
@@ -1279,6 +1346,8 @@
if (rc < 0)
goto free_res;
+ dev->dbg_p.clk_rate = (uint32_t) rate;
+
rate = (c_data->vc_format.hactive_end -
c_data->vc_format.hactive_start);
@@ -1376,12 +1445,12 @@
int streamoff_validate_q(struct vb2_queue *q)
{
if (q->fileio) {
- dprintk(1, "streamoff: file io in progress\n");
+ pr_debug("%s: file io in progress\n", __func__);
return -EBUSY;
}
if (!q->streaming) {
- dprintk(1, "streamoff: not streaming\n");
+ pr_debug("%s: not streaming\n", __func__);
return -EINVAL;
}
return 0;
@@ -1791,7 +1860,7 @@
struct vb2_queue *q;
unsigned int mask = 0;
- dprintk(1, "Enter slect/poll\n");
+ pr_debug("%s: Enter slect/poll\n", __func__);
switch (c_data->op_mode) {
case VC_VCAP_OP:
@@ -1866,13 +1935,276 @@
return vc_handler(vcap_ctrl);
}
+#ifdef CONFIG_DEBUG_FS
+/* Query VCAP resource usage */
+static ssize_t read_dump_info(struct file *file, char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ struct vcap_dev *dev = file->private_data;
+ char str_buf[512];
+ size_t tot_size = 0, size;
+
+ if (dev->vc_client) {
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "VCAP: VC\n");
+ tot_size += size;
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "vc_resourse = %d\n", dev->vc_resource);
+ tot_size += size;
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "vc_enabled = %d\n", atomic_read(&dev->vc_enabled));
+ tot_size += size;
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "vc_client id = %p\n", dev->vc_client);
+ tot_size += size;
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "vc_queue_count = %d\n",
+ atomic_read(&dev->vc_client->vc_vidq.queued_count));
+ tot_size += size;
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "vc_total_buffers = %d\n",
+ dev->vc_client->vc_action.tot_buf);
+ tot_size += size;
+ } else {
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "VCAP: VC not in use\n");
+ tot_size += size;
+ }
+ if (dev->vp_client) {
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "VCAP: VP\n");
+ tot_size += size;
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "vp_resourse = %d\n", dev->vp_resource);
+ tot_size += size;
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "vp_enabled = %d\n", atomic_read(&dev->vp_enabled));
+ tot_size += size;
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "vp_client id = %p\n", dev->vp_client);
+ tot_size += size;
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "vp_in_queue_count = %d\n",
+ atomic_read(
+ &dev->vp_client->vp_in_vidq.queued_count));
+ tot_size += size;
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "vp_out_queue_count = %d\n",
+ atomic_read(
+ &dev->vp_client->vp_out_vidq.queued_count));
+ tot_size += size;
+ } else {
+ size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+ "VCAP: VP not in use\n");
+ tot_size += size;
+ }
+
+ return simple_read_from_buffer(user_buf, len, ppos, str_buf, tot_size);
+}
+
+static const struct file_operations dump_info_fops = {
+ .read = read_dump_info,
+ .open = simple_open,
+ .llseek = default_llseek,
+};
+
+static int vcap_debug_clk_rate_get(void *data, u64 *val)
+{
+ struct vcap_dev *dev = data;
+ *val = (u64)dev->dbg_p.clk_rate;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(clk_rate_fops, vcap_debug_clk_rate_get,
+ NULL, "%llu\n");
+
+static int vcap_debug_bw_req_get(void *data, u64 *val)
+{
+ struct vcap_dev *dev = data;
+ *val = (u64)dev->dbg_p.bw_request;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(bw_req_fops, vcap_debug_bw_req_get,
+ NULL, "%llu\n");
+
+static int vcap_debug_drop_frames_get(void *data, u64 *val)
+{
+ struct vcap_dev *dev = data;
+ struct timeval tv;
+ int drop_count;
+
+ if (!dev->vc_resource)
+ return -EPERM;
+ drop_count = atomic_read(&dev->dbg_p.vc_drop_count);
+ atomic_set(&dev->dbg_p.vc_drop_count, 0);
+
+ do_gettimeofday(&tv);
+ dev->dbg_p.vc_timestamp = (uint32_t) (tv.tv_sec * VCAP_USEC +
+ tv.tv_usec);
+
+ *val = (u64)drop_count;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(tot_frame_drop_fops, vcap_debug_drop_frames_get,
+ NULL, "%llu\n");
+
+static int vcap_debug_drop_fps_get(void *data, u64 *val)
+{
+ struct vcap_dev *dev = data;
+ struct timeval tv;
+ int drop_count;
+ uint32_t new_ts;
+
+ if (!dev->vc_resource)
+ return -EPERM;
+ drop_count = atomic_read(&dev->dbg_p.vc_drop_count);
+ atomic_set(&dev->dbg_p.vc_drop_count, 0);
+
+ do_gettimeofday(&tv);
+ new_ts = (uint32_t) (tv.tv_sec * VCAP_USEC +
+ tv.tv_usec);
+
+ if ((new_ts - dev->dbg_p.vc_timestamp) / VCAP_USEC &&
+ new_ts > dev->dbg_p.vc_timestamp)
+ drop_count /= ((new_ts - dev->dbg_p.vc_timestamp) / VCAP_USEC);
+ else
+ drop_count = 0;
+
+ dev->dbg_p.vc_timestamp = new_ts;
+ *val = (u64)drop_count;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(drop_fps_fops, vcap_debug_drop_fps_get,
+ NULL, "%llu\n");
+
+static int vcap_debug_vp_lat_get(void *data, u64 *val)
+{
+ struct vcap_dev *dev = data;
+
+ if (!dev->vp_resource)
+ return -EPERM;
+ *val = (u64)dev->dbg_p.vp_ewma;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(vp_lat_fops, vcap_debug_vp_lat_get,
+ NULL, "%llu\n");
+
+/* Read/Write to VCAP Registers */
+static int vcap_debug_reg_set(void *data, u64 val)
+{
+ struct vcap_dev *dev = data;
+ int i;
+ for (i = 0; i < ARRAY_SIZE(debug_reg_range); i++) {
+ if (val >= debug_reg_range[i].min_val && val <=
+ debug_reg_range[i].max_val)
+ break;
+ }
+ if (i == ARRAY_SIZE(debug_reg_range))
+ return -EINVAL;
+ dev->dbg_p.reg_addr = (uint32_t) val;
+ return 0;
+}
+
+static int vcap_debug_reg_get(void *data, u64 *val)
+{
+ struct vcap_dev *dev = data;
+ *val = (u64)dev->dbg_p.reg_addr;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(vcap_reg_fops, vcap_debug_reg_get,
+ vcap_debug_reg_set, "0x%08llx\n")
+
+static int vcap_debug_reg_rdwr_set(void *data, u64 val)
+{
+ struct vcap_dev *dev = data;
+ u32 reg_val = (u32) val;
+
+ writel_iowmb(reg_val, VCAP_OFFSET(dev->dbg_p.reg_addr));
+ return 0;
+}
+
+static int vcap_debug_reg_rdwr_get(void *data, u64 *val)
+{
+ struct vcap_dev *dev = data;
+ *val = (u64)readl_relaxed(VCAP_OFFSET(dev->dbg_p.reg_addr));
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(vcap_reg_rdwr_fops, vcap_debug_reg_rdwr_get,
+ vcap_debug_reg_rdwr_set, "0x%08llx\n");
+
+static int vcap_debugfs_init(struct vcap_dev *dev)
+{
+ vcap_debugfs_base = debugfs_create_dir("vcap", NULL);
+ if (!vcap_debugfs_base)
+ return -ENOMEM;
+
+ if (!debugfs_create_file("dump_info", S_IRUGO,
+ vcap_debugfs_base, dev, &dump_info_fops))
+ goto error;
+
+ if (!debugfs_create_file("vcap_core_clk_rate", S_IRUGO,
+ vcap_debugfs_base, dev, &clk_rate_fops))
+ goto error;
+
+ if (!debugfs_create_file("vcap_bw_req", S_IRUGO,
+ vcap_debugfs_base, dev, &bw_req_fops))
+ goto error;
+
+ if (!debugfs_create_file("vc_total_frames_drop", S_IRUGO,
+ vcap_debugfs_base, dev, &tot_frame_drop_fops))
+ goto error;
+
+ if (!debugfs_create_file("vc_drop_fps", S_IRUGO,
+ vcap_debugfs_base, dev, &drop_fps_fops))
+ goto error;
+
+ if (!debugfs_create_file("vp_avg_completion_t", S_IRUGO,
+ vcap_debugfs_base, dev, &vp_lat_fops))
+ goto error;
+
+ if (!debugfs_create_file("vcap_reg_addr", S_IRUGO | S_IWUSR,
+ vcap_debugfs_base, dev, &vcap_reg_fops))
+ goto error;
+
+ if (!debugfs_create_file("vcap_reg_val", S_IRUGO | S_IWUSR,
+ vcap_debugfs_base, dev, &vcap_reg_rdwr_fops))
+ goto error;
+ return 0;
+
+error:
+ debugfs_remove_recursive(vcap_debugfs_base);
+ vcap_debugfs_base = NULL;
+ return -ENOMEM;
+}
+
+static void vcap_debugfs_remove(void)
+{
+ if (vcap_debugfs_base) {
+ debugfs_remove_recursive(vcap_debugfs_base);
+ vcap_debugfs_base = NULL;
+ }
+}
+#else
+
+static int vcap_debugfs_init(struct vcap_dev *dev)
+{
+ return 0;
+}
+static void vcap_debugfs_remove(void) {}
+#endif
+
static int __devinit vcap_probe(struct platform_device *pdev)
{
struct vcap_dev *dev;
struct video_device *vfd;
int ret;
- dprintk(1, "Probe started\n");
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
@@ -1945,10 +2277,39 @@
if (ret)
goto free_resource;
+ dev->vc_iommu_ctx = msm_iommu_get_ctx("vcap_vc");
+ if (!dev->vc_iommu_ctx) {
+ pr_err("%s: No iommu vc context found\n", __func__);
+ ret = -ENODEV;
+ goto free_resource;
+ }
+
+ dev->vp_iommu_ctx = msm_iommu_get_ctx("vcap_vp");
+ if (!dev->vp_iommu_ctx) {
+ pr_err("%s: No iommu vp context found\n", __func__);
+ ret = -ENODEV;
+ goto free_resource;
+ }
+
+ dev->domain_num = vcap_register_domain();
+ if (dev->domain_num < 0) {
+ pr_err("%s: VCAP iommu domain register failed\n", __func__);
+ ret = -ENODEV;
+ goto free_resource;
+ }
+
+ dev->iommu_vcap_domain = msm_get_iommu_domain(dev->domain_num);
+ if (!dev->iommu_vcap_domain) {
+ pr_err("%s: No iommu vcap domain found\n", __func__);
+ ret = -ENODEV;
+ goto free_resource;
+ }
+
ret = vcap_enable(dev, &pdev->dev, 54860000);
if (ret)
goto unreg_dev;
msm_bus_scale_client_update_request(dev->bus_client_handle, 0);
+ dev->dbg_p.bw_request = 0;
ret = detect_vc(dev);
@@ -1957,8 +2318,10 @@
/* init video device*/
vfd = video_device_alloc();
- if (!vfd)
+ if (!vfd) {
+ ret = -ENOMEM;
goto deinit_vc;
+ }
*vfd = vcap_template;
vfd->v4l2_dev = &dev->v4l2_dev;
@@ -1972,6 +2335,7 @@
dev->vcap_wq = create_workqueue("vcap");
if (!dev->vcap_wq) {
+ ret = -ENOMEM;
pr_err("Could not create workqueue");
goto rel_vdev;
}
@@ -1979,9 +2343,16 @@
dev->ion_client = msm_ion_client_create(-1, "vcap");
if (IS_ERR((void *)dev->ion_client)) {
pr_err("could not get ion client");
+ ret = PTR_ERR(dev->ion_client);
+ dev->ion_client = NULL;
goto rel_vcap_wq;
}
+ atomic_set(&dev->dbg_p.vc_drop_count, 0);
+ ret = vcap_debugfs_init(dev);
+ if (ret < 0)
+ pr_err("VCAP debugfs failed to load");
+
dev->vc_tot_buf = 2;
atomic_set(&dev->vc_enabled, 0);
atomic_set(&dev->vp_enabled, 0);
@@ -1991,7 +2362,6 @@
init_waitqueue_head(&dev->vp_dummy_waitq);
vcap_disable(dev);
- dprintk(1, "Exit probe succesfully");
return 0;
rel_vcap_wq:
destroy_workqueue(dev->vcap_wq);
@@ -2015,6 +2385,7 @@
static int __devexit vcap_remove(struct platform_device *pdev)
{
struct vcap_dev *dev = vcap_ctrl;
+ vcap_debugfs_remove();
ion_client_destroy(dev->ion_client);
flush_workqueue(dev->vcap_wq);
destroy_workqueue(dev->vcap_wq);
diff --git a/drivers/media/video/vcap_vc.c b/drivers/media/video/vcap_vc.c
index 572c272..92b205e 100644
--- a/drivers/media/video/vcap_vc.c
+++ b/drivers/media/video/vcap_vc.c
@@ -27,14 +27,6 @@
#include <media/vcap_fmt.h>
#include "vcap_vc.h"
-static unsigned debug;
-
-#define dprintk(level, fmt, arg...) \
- do { \
- if (debug >= level) \
- printk(KERN_DEBUG "VC: " fmt, ## arg); \
- } while (0)
-
void config_buffer(struct vcap_client_data *c_data,
struct vcap_buffer *buf,
void __iomem *y_addr,
@@ -43,10 +35,11 @@
if (c_data->vc_format.color_space == HAL_VCAP_RGB) {
writel_relaxed(buf->paddr, y_addr);
} else {
- int size = ((c_data->vc_format.hactive_end -
- c_data->vc_format.hactive_start) *
- (c_data->vc_format.vactive_end -
- c_data->vc_format.vactive_start));
+ int size = (c_data->vc_format.hactive_end -
+ c_data->vc_format.hactive_start);
+ size = VCAP_STRIDE_CALC(size);
+ size *= (c_data->vc_format.vactive_end -
+ c_data->vc_format.vactive_start);
writel_relaxed(buf->paddr, y_addr);
writel_relaxed(buf->paddr + size, c_addr);
}
@@ -73,7 +66,7 @@
vb_vc = vp_work->cd->vc_vidq.bufs[p.index];
if (NULL == vb_vc) {
- dprintk(1, "%s: buffer is NULL\n", __func__);
+ pr_debug("%s: buffer is NULL\n", __func__);
vcvp_qbuf(&vp_work->cd->vc_vidq, &p);
return;
}
@@ -81,7 +74,7 @@
vb_vp = vp_work->cd->vp_in_vidq.bufs[p.index];
if (NULL == vb_vp) {
- dprintk(1, "%s: buffer is NULL\n", __func__);
+ pr_debug("%s: buffer is NULL\n", __func__);
vcvp_qbuf(&vp_work->cd->vc_vidq, &p);
return;
}
@@ -145,7 +138,7 @@
irq = readl_relaxed(VCAP_VC_INT_STATUS);
- dprintk(1, "%s: irq=0x%08x\n", __func__, irq);
+ pr_debug("%s: irq=0x%08x\n", __func__, irq);
c_data = dev->vc_client;
if (!c_data->streaming) {
@@ -254,6 +247,8 @@
v4l2_event_queue(dev->vfd, &v4l2_evt);
c_data->vc_action.top_field =
!c_data->vc_action.top_field;
+
+ atomic_inc(&dev->dbg_p.vc_drop_count);
continue;
}
buf = list_entry(c_data->vc_action.active.next,
@@ -296,12 +291,13 @@
{
struct vc_action *vc_action = &c_data->vc_action;
struct vcap_dev *dev;
+ struct timeval tv;
unsigned long flags = 0;
int rc, i, counter = 0;
struct vcap_buffer *buf;
dev = c_data->dev;
- dprintk(2, "Start Kickoff\n");
+ pr_debug("Start Kickoff\n");
if (dev->vc_client == NULL) {
pr_err("No active vc client\n");
@@ -344,6 +340,11 @@
c_data->vc_action.vc_ts.tv_usec =
c_data->vc_action.last_ts % VCAP_USEC;
+ atomic_set(&dev->dbg_p.vc_drop_count, 0);
+ do_gettimeofday(&tv);
+ dev->dbg_p.vc_timestamp = (uint32_t) (tv.tv_sec * VCAP_USEC +
+ tv.tv_usec);
+
rc = 0;
for (i = 0; i < c_data->vc_action.tot_buf; i++)
rc = rc << 1 | 0x2;
@@ -416,7 +417,7 @@
rc = readl_relaxed(VCAP_VC_NPL_CTRL);
writel_iowmb(0x00000002, VCAP_VC_NPL_CTRL);
- dprintk(2, "%s: Starting VC configuration\n", __func__);
+ pr_debug("%s: Starting VC configuration\n", __func__);
writel_iowmb(0x00000002, VCAP_VC_NPL_CTRL);
writel_iowmb(0x00000004 | vc_format->color_space << 1 |
vc_format->mode << 3 |
@@ -449,6 +450,7 @@
writel_iowmb(0x000033FF, VCAP_VC_BUF_CTRL);
rc = vc_format->hactive_end - vc_format->hactive_start;
+ rc = VCAP_STRIDE_CALC(rc);
if (vc_format->color_space)
rc *= 3;
@@ -464,7 +466,7 @@
writel_relaxed(0x00006b38, VCAP_VC_IN_CTRL5);
writel_iowmb(0x00000001 , VCAP_OFFSET(0x0d00));
- dprintk(2, "%s: Done VC configuration\n", __func__);
+ pr_debug("%s: Done VC configuration\n", __func__);
return 0;
}
@@ -473,7 +475,7 @@
{
int result;
result = readl_relaxed(VCAP_HARDWARE_VERSION_REG);
- dprintk(1, "Hardware version: %08x\n", result);
+ pr_debug("Hardware version: %08x\n", result);
if (result != VCAP_HARDWARE_VERSION)
return -ENODEV;
INIT_WORK(&dev->vc_to_vp_work.work, mov_buf_to_vp);
diff --git a/drivers/media/video/vcap_vp.c b/drivers/media/video/vcap_vp.c
index aa39aad..57813f5 100644
--- a/drivers/media/video/vcap_vp.c
+++ b/drivers/media/video/vcap_vp.c
@@ -15,24 +15,18 @@
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
-#include <mach/camera.h>
-#include <linux/io.h>
-#include <mach/clk.h>
#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <mach/camera.h>
+#include <mach/clk.h>
#include <media/v4l2-event.h>
#include <media/vcap_v4l2.h>
#include <media/vcap_fmt.h>
+
#include "vcap_vp.h"
-static unsigned debug;
-
-#define dprintk(level, fmt, arg...) \
- do { \
- if (debug >= level) \
- printk(KERN_DEBUG "VP: " fmt, ## arg); \
- } while (0)
-
void config_nr_buffer(struct vcap_client_data *c_data,
struct vcap_buffer *buf)
{
@@ -72,10 +66,10 @@
if (!c_data->streaming)
return -ENOEXEC;
dev = c_data->dev;
- dprintk(2, "Start setup buffers\n");
+ pr_debug("VP: Start setup buffers\n");
if (dev->vp_shutdown) {
- dprintk(1, "%s: VP shutting down, no buf setup\n",
+ pr_debug("%s: VP shutting down, no buf setup\n",
__func__);
return -EPERM;
}
@@ -86,7 +80,7 @@
spin_lock_irqsave(&dev->vp_client->cap_slock, flags);
if (list_empty(&vp_act->in_active)) {
spin_unlock_irqrestore(&dev->vp_client->cap_slock, flags);
- dprintk(1, "%s: VP We have no more input buffers\n",
+ pr_debug("%s: VP We have no more input buffers\n",
__func__);
return -EAGAIN;
}
@@ -94,7 +88,7 @@
if (list_empty(&vp_act->out_active)) {
spin_unlock_irqrestore(&dev->vp_client->cap_slock,
flags);
- dprintk(1, "%s: VP We have no more output buffers\n",
+ pr_debug("%s: VP We have no more output buffers\n",
__func__);
return -EAGAIN;
}
@@ -136,7 +130,7 @@
vb_vc = vp_work->cd->vc_vidq.bufs[p.index];
if (NULL == vb_vc) {
- dprintk(1, "%s: buffer is NULL\n", __func__);
+ pr_debug("%s: buffer is NULL\n", __func__);
vcvp_qbuf(&vp_work->cd->vp_in_vidq, &p);
return;
}
@@ -144,7 +138,7 @@
vb_vp = vp_work->cd->vp_in_vidq.bufs[p.index];
if (NULL == vb_vp) {
- dprintk(1, "%s: buffer is NULL\n", __func__);
+ pr_debug("%s: buffer is NULL\n", __func__);
vcvp_qbuf(&vp_work->cd->vp_in_vidq, &p);
return;
}
@@ -158,7 +152,7 @@
/* This call should not fail */
rc = vcvp_qbuf(&vp_work->cd->vc_vidq, &p);
if (rc < 0) {
- dprintk(1, "%s: qbuf to vc failed\n", __func__);
+ pr_err("%s: qbuf to vc failed\n", __func__);
buf_vp->ion_handle = buf_vc->ion_handle;
buf_vp->paddr = buf_vc->paddr;
buf_vc->ion_handle = NULL;
@@ -197,6 +191,7 @@
struct vp_work_t *vp_work = container_of(work, struct vp_work_t, work);
struct vcap_dev *dev;
struct vp_action *vp_act;
+ struct timeval tv;
unsigned long flags = 0;
uint32_t irq;
int rc;
@@ -278,6 +273,11 @@
writel_iowmb(0x00000000 | top_field, VCAP_VP_CTRL);
writel_iowmb(0x00010000 | top_field, VCAP_VP_CTRL);
enable_irq(dev->vpirq->start);
+
+ do_gettimeofday(&tv);
+ dev->dbg_p.vp_timestamp = (uint32_t) (tv.tv_sec * VCAP_USEC +
+ tv.tv_usec);
+
writel_iowmb(irq, VCAP_VP_INT_CLEAR);
}
@@ -288,6 +288,8 @@
struct v4l2_event v4l2_evt;
uint32_t irq;
int rc;
+ struct timeval tv;
+ uint32_t new_ts;
irq = readl_relaxed(VCAP_VP_INT_STATUS);
if (dev->vp_dummy_event == true) {
@@ -304,7 +306,7 @@
}
if (irq & 0x01000000) {
v4l2_evt.type = V4L2_EVENT_PRIVATE_START +
- VCAP_VC_LINE_ERR_EVENT;
+ VCAP_VP_REG_W_ERR_EVENT;
v4l2_event_queue(dev->vfd, &v4l2_evt);
}
if (irq & 0x00020000) {
@@ -318,7 +320,7 @@
v4l2_event_queue(dev->vfd, &v4l2_evt);
}
- dprintk(1, "%s: irq=0x%08x\n", __func__, irq);
+ pr_debug("%s: irq=0x%08x\n", __func__, irq);
if (!(irq & (VP_PIC_DONE | VP_MODE_CHANGE))) {
writel_relaxed(irq, VCAP_VP_INT_CLEAR);
pr_err("VP IRQ shows some error\n");
@@ -341,6 +343,17 @@
return -EAGAIN;
}
+ do_gettimeofday(&tv);
+ new_ts = (uint32_t) (tv.tv_sec * VCAP_USEC +
+ tv.tv_usec);
+ if (new_ts > dev->dbg_p.vp_timestamp) {
+ dev->dbg_p.vp_ewma = ((new_ts - dev->dbg_p.vp_timestamp) /
+ 10 + (dev->dbg_p.vp_ewma / 10 * 9));
+ }
+
+ dev->dbg_p.vp_timestamp = (uint32_t) (tv.tv_sec * VCAP_USEC +
+ tv.tv_usec);
+
INIT_WORK(&dev->vp_work.work, vp_wq_fnc);
dev->vp_work.cd = c_data;
rc = queue_work(dev->vcap_wq, &dev->vp_work.work);
@@ -382,6 +395,7 @@
msecs_to_jiffies(50));
if (rc == 0 && atomic_read(&dev->vp_enabled) == 1) {
/* This should not happen, if it does hw is stuck */
+ disable_irq_nosync(dev->vpirq->start);
pr_err("%s: VP Timeout and VP still running\n",
__func__);
}
@@ -452,9 +466,8 @@
int rc;
struct vcap_dev *dev = c_data->dev;
struct ion_handle *handle = NULL;
- unsigned long paddr, ionflag = 0;
+ unsigned long paddr, len, ionflag = 0;
void *vaddr;
- size_t len;
size_t size = ((c_data->vp_out_fmt.width + 63) >> 6) *
((c_data->vp_out_fmt.height + 7) >> 3) * 16;
@@ -469,12 +482,6 @@
pr_err("%s: ion_alloc failed\n", __func__);
return -ENOMEM;
}
- rc = ion_phys(dev->ion_client, handle, &paddr, &len);
- if (rc < 0) {
- pr_err("%s: ion_phys failed\n", __func__);
- ion_free(dev->ion_client, handle);
- return rc;
- }
rc = ion_handle_get_flags(dev->ion_client, handle, &ionflag);
if (rc) {
@@ -492,10 +499,20 @@
}
memset(vaddr, 0, size);
+ ion_unmap_kernel(dev->ion_client, handle);
+
+ rc = ion_map_iommu(dev->ion_client, handle,
+ dev->domain_num, 0, SZ_4K, 0, &paddr, &len,
+ 0, 0);
+ if (rc < 0) {
+ pr_err("%s: map_iommu failed\n", __func__);
+ ion_free(dev->ion_client, handle);
+ return rc;
+ }
+
c_data->vp_action.motionHandle = handle;
vaddr = NULL;
- ion_unmap_kernel(dev->ion_client, handle);
writel_iowmb(paddr, VCAP_VP_MOTION_EST_ADDR);
return 0;
@@ -510,6 +527,8 @@
}
writel_iowmb(0x00000000, VCAP_VP_MOTION_EST_ADDR);
+ ion_unmap_iommu(dev->ion_client, c_data->vp_action.motionHandle,
+ dev->domain_num, 0);
ion_free(dev->ion_client, c_data->vp_action.motionHandle);
c_data->vp_action.motionHandle = NULL;
return;
@@ -519,8 +538,8 @@
{
struct vcap_dev *dev = c_data->dev;
struct ion_handle *handle = NULL;
- size_t frame_size, tot_size, len;
- unsigned long paddr;
+ size_t frame_size, tot_size;
+ unsigned long paddr, len;
int rc;
if (c_data->vp_action.bufNR.nr_handle) {
@@ -541,9 +560,11 @@
return -ENOMEM;
}
- rc = ion_phys(dev->ion_client, handle, &paddr, &len);
+ rc = ion_map_iommu(dev->ion_client, handle,
+ dev->domain_num, 0, SZ_4K, 0, &paddr, &len,
+ 0, 0);
if (rc < 0) {
- pr_err("%s: ion_phys failed\n", __func__);
+ pr_err("%s: map_iommu failed\n", __func__);
ion_free(dev->ion_client, handle);
return rc;
}
@@ -577,6 +598,7 @@
rc &= !(0x0FF00001);
writel_relaxed(rc, VCAP_VP_NR_CONFIG2);
+ ion_unmap_iommu(dev->ion_client, buf->nr_handle, dev->domain_num, 0);
ion_free(dev->ion_client, buf->nr_handle);
buf->nr_handle = NULL;
buf->paddr = 0;
@@ -658,12 +680,11 @@
struct vcap_dev *dev = c_data->dev;
unsigned int width, height;
struct ion_handle *handle = NULL;
- unsigned long paddr;
- size_t len;
+ unsigned long paddr, len;
uint32_t reg;
int rc = 0;
- dprintk(2, "%s: Start VP dummy event\n", __func__);
+ pr_debug("%s: Start VP dummy event\n", __func__);
handle = ion_alloc(dev->ion_client, 0x1200, SZ_4K,
ION_HEAP(ION_CP_MM_HEAP_ID), 0);
if (IS_ERR_OR_NULL(handle)) {
@@ -671,9 +692,11 @@
return -ENOMEM;
}
- rc = ion_phys(dev->ion_client, handle, &paddr, &len);
+ rc = ion_map_iommu(dev->ion_client, handle,
+ dev->domain_num, 0, SZ_4K, 0, &paddr, &len,
+ 0, 0);
if (rc < 0) {
- pr_err("%s: ion_phys failed\n", __func__);
+ pr_err("%s: map_iommu failed\n", __func__);
ion_free(dev->ion_client, handle);
return rc;
}
@@ -721,9 +744,10 @@
c_data->vp_out_fmt.width = width;
c_data->vp_out_fmt.height = height;
+ ion_unmap_iommu(dev->ion_client, handle, dev->domain_num, 0);
ion_free(dev->ion_client, handle);
- dprintk(2, "%s: Exit VP dummy event\n", __func__);
+ pr_debug("%s: Exit VP dummy event\n", __func__);
return rc;
}
@@ -731,6 +755,7 @@
{
struct vcap_dev *dev;
struct vp_action *vp_act;
+ struct timeval tv;
unsigned long flags = 0;
unsigned int chroma_fmt = 0;
int size;
@@ -740,7 +765,7 @@
return -ENOEXEC;
dev = c_data->dev;
- dprintk(2, "Start Kickoff\n");
+ pr_debug("Start VP Kickoff\n");
if (dev->vp_client == NULL) {
pr_err("No active vp client\n");
@@ -815,6 +840,11 @@
writel_iowmb(0x00010000 | top_field, VCAP_VP_CTRL);
atomic_set(&c_data->dev->vp_enabled, 1);
enable_irq(dev->vpirq->start);
+
+ do_gettimeofday(&tv);
+ dev->dbg_p.vp_timestamp = (uint32_t) (tv.tv_sec * VCAP_USEC +
+ tv.tv_usec);
+
return 0;
}
@@ -822,10 +852,11 @@
{
struct vcap_dev *dev;
struct vp_action *vp_act;
+ struct timeval tv;
int rc;
bool top_field = 0;
- dprintk(2, "Start Continue\n");
+ pr_debug("Start VP Continue\n");
dev = c_data->dev;
if (dev->vp_client == NULL) {
@@ -854,5 +885,10 @@
atomic_set(&c_data->dev->vp_enabled, 1);
enable_irq(dev->vpirq->start);
+
+ do_gettimeofday(&tv);
+ dev->dbg_p.vp_timestamp = (uint32_t) (tv.tv_sec * VCAP_USEC +
+ tv.tv_usec);
+
return 0;
}
diff --git a/drivers/mfd/pm8038-core.c b/drivers/mfd/pm8038-core.c
index 9815f6e..48bc92d 100644
--- a/drivers/mfd/pm8038-core.c
+++ b/drivers/mfd/pm8038-core.c
@@ -33,6 +33,11 @@
#define REG_RTC_BASE 0x11D
#define REG_IRQ_BASE 0x1BB
+#define REG_BATT_ALARM_THRESH 0x023
+#define REG_BATT_ALARM_CTRL1 0x024
+#define REG_BATT_ALARM_CTRL2 0x021
+#define REG_BATT_ALARM_PWM_CTRL 0x020
+
#define REG_SPK_BASE 0x253
#define REG_SPK_REGISTERS 6
@@ -336,6 +341,27 @@
.pdata_size = sizeof(struct pm8xxx_tm_core_data),
};
+static const struct resource batt_alarm_cell_resources[] __devinitconst = {
+ SINGLE_IRQ_RESOURCE("pm8921_batt_alarm_irq", PM8038_BATT_ALARM_IRQ),
+};
+
+static struct pm8xxx_batt_alarm_core_data batt_alarm_cdata = {
+ .irq_name = "pm8921_batt_alarm_irq",
+ .reg_addr_threshold = REG_BATT_ALARM_THRESH,
+ .reg_addr_ctrl1 = REG_BATT_ALARM_CTRL1,
+ .reg_addr_ctrl2 = REG_BATT_ALARM_CTRL2,
+ .reg_addr_pwm_ctrl = REG_BATT_ALARM_PWM_CTRL,
+};
+
+static struct mfd_cell batt_alarm_cell __devinitdata = {
+ .name = PM8XXX_BATT_ALARM_DEV_NAME,
+ .id = -1,
+ .resources = batt_alarm_cell_resources,
+ .num_resources = ARRAY_SIZE(batt_alarm_cell_resources),
+ .platform_data = &batt_alarm_cdata,
+ .pdata_size = sizeof(struct pm8xxx_batt_alarm_core_data),
+};
+
static const struct resource ccadc_cell_resources[] __devinitconst = {
SINGLE_IRQ_RESOURCE("PM8921_BMS_CCADC_EOC", PM8921_BMS_CCADC_EOC),
};
@@ -661,6 +687,13 @@
goto bail;
}
+ ret = mfd_add_devices(pmic->dev, 0, &batt_alarm_cell, 1, NULL,
+ irq_base);
+ if (ret) {
+ pr_err("Failed to add battery alarm subdevice ret=%d\n", ret);
+ goto bail;
+ }
+
if (pdata->ccadc_pdata) {
ccadc_cell.platform_data = pdata->ccadc_pdata;
ccadc_cell.pdata_size =
diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c
index 0ea843c..62f1a93 100644
--- a/drivers/mfd/wcd9xxx-core.c
+++ b/drivers/mfd/wcd9xxx-core.c
@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/mfd/core.h>
#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
@@ -257,12 +258,20 @@
u8 byte[4];
struct mfd_cell *dev;
int size;
+ int num_irqs;
} wcd9xxx_codecs[] = {
- {{0x2, 0x0, 0x0, 0x1}, tabla_devs, ARRAY_SIZE(tabla_devs)},
- {{0x1, 0x0, 0x0, 0x1}, tabla1x_devs, ARRAY_SIZE(tabla1x_devs)},
- {{0x0, 0x0, 0x2, 0x1}, taiko_devs, ARRAY_SIZE(taiko_devs)},
- {{0x0, 0x0, 0x0, 0x1}, sitar_devs, ARRAY_SIZE(sitar_devs)},
- {{0x1, 0x0, 0x1, 0x1}, sitar_devs, ARRAY_SIZE(sitar_devs)},
+ {{0x2, 0x0, 0x0, 0x1}, tabla_devs, ARRAY_SIZE(tabla_devs),
+ TABLA_NUM_IRQS},
+ {{0x1, 0x0, 0x0, 0x1}, tabla1x_devs, ARRAY_SIZE(tabla1x_devs),
+ TABLA_NUM_IRQS},
+ {{0x0, 0x0, 0x2, 0x1}, taiko_devs, ARRAY_SIZE(taiko_devs),
+ TAIKO_NUM_IRQS},
+ {{0x0, 0x0, 0x0, 0x1}, sitar_devs, ARRAY_SIZE(sitar_devs),
+ SITAR_NUM_IRQS},
+ {{0x1, 0x0, 0x1, 0x1}, sitar_devs, ARRAY_SIZE(sitar_devs),
+ SITAR_NUM_IRQS},
+ {{0x2, 0x0, 0x1, 0x1}, sitar_devs, ARRAY_SIZE(sitar_devs),
+ SITAR_NUM_IRQS},
};
static void wcd9xxx_bring_up(struct wcd9xxx *wcd9xxx)
@@ -311,21 +320,21 @@
}
}
static int wcd9xxx_check_codec_type(struct wcd9xxx *wcd9xxx,
- struct mfd_cell **wcd9xxx_dev,
- int *wcd9xxx_dev_size)
+ struct mfd_cell **wcd9xxx_dev,
+ int *wcd9xxx_dev_size,
+ int *wcd9xxx_dev_num_irqs)
{
- struct wcd9xx_codec_type *cdc = wcd9xxx_codecs;
- int index;
+ int i;
int ret;
- index = WCD9XXX_A_CHIP_ID_BYTE_0;
- while (index <= WCD9XXX_A_CHIP_ID_BYTE_3) {
- ret = wcd9xxx_reg_read(wcd9xxx, index);
+ i = WCD9XXX_A_CHIP_ID_BYTE_0;
+ while (i <= WCD9XXX_A_CHIP_ID_BYTE_3) {
+ ret = wcd9xxx_reg_read(wcd9xxx, i);
if (ret < 0)
goto exit;
- wcd9xxx->idbyte[index-WCD9XXX_A_CHIP_ID_BYTE_0] = (u8)ret;
+ wcd9xxx->idbyte[i-WCD9XXX_A_CHIP_ID_BYTE_0] = (u8)ret;
pr_debug("%s: wcd9xx read = %x, byte = %x\n", __func__, ret,
- index);
- index++;
+ i);
+ i++;
}
/* Read codec version */
@@ -333,18 +342,20 @@
if (ret < 0)
goto exit;
wcd9xxx->version = (u8)ret & 0x1F;
-
- while (cdc < (cdc + ARRAY_SIZE(wcd9xxx_codecs)) && cdc != NULL) {
- if ((cdc->byte[0] == wcd9xxx->idbyte[0]) &&
- (cdc->byte[1] == wcd9xxx->idbyte[1]) &&
- (cdc->byte[2] == wcd9xxx->idbyte[2]) &&
- (cdc->byte[3] == wcd9xxx->idbyte[3])) {
- pr_info("%s: codec is %s", __func__, cdc->dev->name);
- *wcd9xxx_dev = cdc->dev;
- *wcd9xxx_dev_size = cdc->size;
+ i = 0;
+ while (i < ARRAY_SIZE(wcd9xxx_codecs)) {
+ if ((wcd9xxx_codecs[i].byte[0] == wcd9xxx->idbyte[0]) &&
+ (wcd9xxx_codecs[i].byte[1] == wcd9xxx->idbyte[1]) &&
+ (wcd9xxx_codecs[i].byte[2] == wcd9xxx->idbyte[2]) &&
+ (wcd9xxx_codecs[i].byte[3] == wcd9xxx->idbyte[3])) {
+ pr_info("%s: codec is %s", __func__,
+ wcd9xxx_codecs[i].dev->name);
+ *wcd9xxx_dev = wcd9xxx_codecs[i].dev;
+ *wcd9xxx_dev_size = wcd9xxx_codecs[i].size;
+ *wcd9xxx_dev_num_irqs = wcd9xxx_codecs[i].num_irqs;
break;
}
- cdc++;
+ i++;
}
if (*wcd9xxx_dev == NULL || *wcd9xxx_dev_size == 0)
ret = -ENODEV;
@@ -358,7 +369,7 @@
return ret;
}
-static int wcd9xxx_device_init(struct wcd9xxx *wcd9xxx, int irq)
+static int wcd9xxx_device_init(struct wcd9xxx *wcd9xxx)
{
int ret;
struct mfd_cell *wcd9xxx_dev = NULL;
@@ -378,6 +389,11 @@
wcd9xxx_bring_up(wcd9xxx);
+ ret = wcd9xxx_check_codec_type(wcd9xxx, &wcd9xxx_dev, &wcd9xxx_dev_size,
+ &wcd9xxx->num_irqs);
+ if (ret < 0)
+ goto err_irq;
+
if (wcd9xxx->irq != -1) {
ret = wcd9xxx_irq_init(wcd9xxx);
if (ret) {
@@ -385,11 +401,7 @@
goto err;
}
}
- ret = wcd9xxx_check_codec_type(wcd9xxx, &wcd9xxx_dev,
- &wcd9xxx_dev_size);
- if (ret < 0)
- goto err_irq;
ret = mfd_add_devices(wcd9xxx->dev, -1, wcd9xxx_dev, wcd9xxx_dev_size,
NULL, 0);
if (ret != 0) {
@@ -794,10 +806,12 @@
wcd9xxx->read_dev = wcd9xxx_i2c_read;
wcd9xxx->write_dev = wcd9xxx_i2c_write;
- wcd9xxx->irq = pdata->irq;
- wcd9xxx->irq_base = pdata->irq_base;
+ if (!wcd9xxx->dev->of_node) {
+ wcd9xxx->irq = pdata->irq;
+ wcd9xxx->irq_base = pdata->irq_base;
+ }
- ret = wcd9xxx_device_init(wcd9xxx, wcd9xxx->irq);
+ ret = wcd9xxx_device_init(wcd9xxx);
if (ret) {
pr_err("%s: error, initializing device failed\n", __func__);
goto err_device_init;
@@ -1071,7 +1085,6 @@
pdata->reset_gpio);
goto err;
}
- pdata->irq = -1;
ret = wcd9xxx_dt_parse_slim_interface_dev_info(dev,
&pdata->slimbus_slave_device);
@@ -1162,14 +1175,12 @@
}
wcd9xxx->read_dev = wcd9xxx_slim_read_device;
wcd9xxx->write_dev = wcd9xxx_slim_write_device;
- wcd9xxx->irq = pdata->irq;
- wcd9xxx->irq_base = pdata->irq_base;
wcd9xxx_pgd_la = wcd9xxx->slim->laddr;
-
- if (pdata->num_irqs < TABLA_NUM_IRQS)
- pr_warn("%s: Not enough interrupt lines allocated\n", __func__);
-
wcd9xxx->slim_slave = &pdata->slimbus_slave_device;
+ if (!wcd9xxx->dev->of_node) {
+ wcd9xxx->irq = pdata->irq;
+ wcd9xxx->irq_base = pdata->irq_base;
+ }
ret = slim_add_device(slim->ctrl, wcd9xxx->slim_slave);
if (ret) {
@@ -1189,14 +1200,11 @@
wcd9xxx_inf_la = wcd9xxx->slim_slave->laddr;
wcd9xxx_intf = WCD9XXX_INTERFACE_TYPE_SLIMBUS;
- ret = wcd9xxx_device_init(wcd9xxx, wcd9xxx->irq);
+ ret = wcd9xxx_device_init(wcd9xxx);
if (ret) {
pr_err("%s: error, initializing device failed\n", __func__);
goto err_slim_add;
}
-
- wcd9xxx_init_slimslave(wcd9xxx, wcd9xxx_pgd_la);
-
#ifdef CONFIG_DEBUG_FS
debugCodec = wcd9xxx;
diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c
index 68c4557..103c1a3 100644
--- a/drivers/mfd/wcd9xxx-irq.c
+++ b/drivers/mfd/wcd9xxx-irq.c
@@ -18,26 +18,32 @@
#include <linux/mfd/wcd9xxx/core.h>
#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
#include <linux/mfd/wcd9xxx/wcd9310_registers.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
+#include <linux/delay.h>
+#include <linux/irqdomain.h>
#include <linux/interrupt.h>
-
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
#include <mach/cpuidle.h>
#define BYTE_BIT_MASK(nr) (1UL << ((nr) % BITS_PER_BYTE))
#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE)
-struct wcd9xxx_irq {
- bool level;
-};
+#define WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS 100
-static struct wcd9xxx_irq wcd9xxx_irqs[TABLA_NUM_IRQS] = {
- [0] = { .level = 1},
-/* All other wcd9xxx interrupts are edge triggered */
+#ifdef CONFIG_OF
+struct wcd9xxx_irq_drv_data {
+ struct irq_domain *domain;
+ int irq;
};
+#endif
-static inline int irq_to_wcd9xxx_irq(struct wcd9xxx *wcd9xxx, int irq)
-{
- return irq - wcd9xxx->irq_base;
-}
+static int virq_to_phyirq(struct wcd9xxx *wcd9xxx, int virq);
+static int phyirq_to_virq(struct wcd9xxx *wcd9xxx, int irq);
+static unsigned int wcd9xxx_irq_get_upstream_irq(struct wcd9xxx *wcd9xxx);
+static void wcd9xxx_irq_put_upstream_irq(struct wcd9xxx *wcd9xxx);
+static int wcd9xxx_map_irq(struct wcd9xxx *wcd9xxx, int irq);
static void wcd9xxx_irq_lock(struct irq_data *data)
{
@@ -56,8 +62,9 @@
*/
if (wcd9xxx->irq_masks_cur[i] != wcd9xxx->irq_masks_cache[i]) {
wcd9xxx->irq_masks_cache[i] = wcd9xxx->irq_masks_cur[i];
- wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MASK0+i,
- wcd9xxx->irq_masks_cur[i]);
+ wcd9xxx_reg_write(wcd9xxx,
+ WCD9XXX_A_INTR_MASK0 + i,
+ wcd9xxx->irq_masks_cur[i]);
}
}
@@ -67,7 +74,7 @@
static void wcd9xxx_irq_enable(struct irq_data *data)
{
struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
- int wcd9xxx_irq = irq_to_wcd9xxx_irq(wcd9xxx, data->irq);
+ int wcd9xxx_irq = virq_to_phyirq(wcd9xxx, data->irq);
wcd9xxx->irq_masks_cur[BIT_BYTE(wcd9xxx_irq)] &=
~(BYTE_BIT_MASK(wcd9xxx_irq));
}
@@ -75,9 +82,14 @@
static void wcd9xxx_irq_disable(struct irq_data *data)
{
struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
- int wcd9xxx_irq = irq_to_wcd9xxx_irq(wcd9xxx, data->irq);
+ int wcd9xxx_irq = virq_to_phyirq(wcd9xxx, data->irq);
wcd9xxx->irq_masks_cur[BIT_BYTE(wcd9xxx_irq)]
- |= BYTE_BIT_MASK(wcd9xxx_irq);
+ |= BYTE_BIT_MASK(wcd9xxx_irq);
+}
+
+static void wcd9xxx_irq_mask(struct irq_data *d)
+{
+ /* do nothing but required as linux calls irq_mask without NULL check */
}
static struct irq_chip wcd9xxx_irq_chip = {
@@ -86,6 +98,7 @@
.irq_bus_sync_unlock = wcd9xxx_irq_sync_unlock,
.irq_disable = wcd9xxx_irq_disable,
.irq_enable = wcd9xxx_irq_enable,
+ .irq_mask = wcd9xxx_irq_mask,
};
enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg(struct wcd9xxx *wcd9xxx,
@@ -106,11 +119,17 @@
{
enum wcd9xxx_pm_state os;
- /* wcd9xxx_{lock/unlock}_sleep will be called by wcd9xxx_irq_thread
+ /*
+ * wcd9xxx_{lock/unlock}_sleep will be called by wcd9xxx_irq_thread
* and its subroutines only motly.
* but btn0_lpress_fn is not wcd9xxx_irq_thread's subroutine and
- * it can race with wcd9xxx_irq_thread.
- * so need to embrace wlock_holders with mutex.
+ * It can race with wcd9xxx_irq_thread.
+ * So need to embrace wlock_holders with mutex.
+ *
+ * If system didn't resume, we can simply return false so codec driver's
+ * IRQ handler can return without handling IRQ.
+ * As interrupt line is still active, codec will have another IRQ to
+ * retry shortly.
*/
mutex_lock(&wcd9xxx->pm_lock);
if (wcd9xxx->wlock_holders++ == 0) {
@@ -124,11 +143,11 @@
WCD9XXX_PM_AWAKE)) ==
WCD9XXX_PM_SLEEPABLE ||
(os == WCD9XXX_PM_AWAKE)),
- 5 * HZ)) {
- pr_err("%s: system didn't resume within 5000ms, state %d, "
- "wlock %d\n", __func__, wcd9xxx->pm_state,
- wcd9xxx->wlock_holders);
- WARN_ON(1);
+ msecs_to_jiffies(WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS))) {
+ pr_warn("%s: system didn't resume within %dms, s %d, w %d\n",
+ __func__,
+ WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS, wcd9xxx->pm_state,
+ wcd9xxx->wlock_holders);
wcd9xxx_unlock_sleep(wcd9xxx);
return false;
}
@@ -141,8 +160,14 @@
{
mutex_lock(&wcd9xxx->pm_lock);
if (--wcd9xxx->wlock_holders == 0) {
- wcd9xxx->pm_state = WCD9XXX_PM_SLEEPABLE;
- pr_debug("%s: releasing wake lock\n", __func__);
+ pr_debug("%s: releasing wake lock pm_state %d -> %d\n",
+ __func__, wcd9xxx->pm_state, WCD9XXX_PM_SLEEPABLE);
+ /*
+ * if wcd9xxx_lock_sleep failed, pm_state would be still
+ * WCD9XXX_PM_ASLEEP, don't overwrite
+ */
+ if (likely(wcd9xxx->pm_state == WCD9XXX_PM_AWAKE))
+ wcd9xxx->pm_state = WCD9XXX_PM_SLEEPABLE;
pm_qos_update_request(&wcd9xxx->pm_qos_req,
PM_QOS_DEFAULT_VALUE);
}
@@ -153,62 +178,72 @@
static void wcd9xxx_irq_dispatch(struct wcd9xxx *wcd9xxx, int irqbit)
{
- if ((irqbit <= TABLA_IRQ_MBHC_INSERTION) &&
- (irqbit >= TABLA_IRQ_MBHC_REMOVAL)) {
- wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_CLEAR0 +
- BIT_BYTE(irqbit), BYTE_BIT_MASK(irqbit));
+ if ((irqbit <= WCD9XXX_IRQ_MBHC_INSERTION) &&
+ (irqbit >= WCD9XXX_IRQ_MBHC_REMOVAL)) {
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_CLEAR0 +
+ BIT_BYTE(irqbit),
+ BYTE_BIT_MASK(irqbit));
if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
- wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MODE, 0x02);
- handle_nested_irq(wcd9xxx->irq_base + irqbit);
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_MODE, 0x02);
+ handle_nested_irq(phyirq_to_virq(wcd9xxx, irqbit));
} else {
- handle_nested_irq(wcd9xxx->irq_base + irqbit);
- wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_CLEAR0 +
- BIT_BYTE(irqbit), BYTE_BIT_MASK(irqbit));
+ handle_nested_irq(phyirq_to_virq(wcd9xxx, irqbit));
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_CLEAR0 +
+ BIT_BYTE(irqbit),
+ BYTE_BIT_MASK(irqbit));
if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
- wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MODE, 0x02);
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_MODE, 0x02);
}
}
+static int wcd9xxx_num_irq_regs(const struct wcd9xxx *wcd9xxx)
+{
+ return (wcd9xxx->num_irqs / 8) + ((wcd9xxx->num_irqs % 8) ? 1 : 0);
+}
+
static irqreturn_t wcd9xxx_irq_thread(int irq, void *data)
{
int ret;
- struct wcd9xxx *wcd9xxx = data;
- u8 status[WCD9XXX_NUM_IRQ_REGS];
int i;
+ struct wcd9xxx *wcd9xxx = data;
+ int num_irq_regs = wcd9xxx_num_irq_regs(wcd9xxx);
+ u8 status[num_irq_regs];
if (unlikely(wcd9xxx_lock_sleep(wcd9xxx) == false)) {
dev_err(wcd9xxx->dev, "Failed to hold suspend\n");
return IRQ_NONE;
}
- ret = wcd9xxx_bulk_read(wcd9xxx, TABLA_A_INTR_STATUS0,
- WCD9XXX_NUM_IRQ_REGS, status);
+ ret = wcd9xxx_bulk_read(wcd9xxx, WCD9XXX_A_INTR_STATUS0,
+ num_irq_regs, status);
if (ret < 0) {
dev_err(wcd9xxx->dev, "Failed to read interrupt status: %d\n",
ret);
wcd9xxx_unlock_sleep(wcd9xxx);
return IRQ_NONE;
}
+
/* Apply masking */
- for (i = 0; i < WCD9XXX_NUM_IRQ_REGS; i++)
+ for (i = 0; i < num_irq_regs; i++)
status[i] &= ~wcd9xxx->irq_masks_cur[i];
/* Find out which interrupt was triggered and call that interrupt's
* handler function
*/
- if (status[BIT_BYTE(TABLA_IRQ_SLIMBUS)] &
- BYTE_BIT_MASK(TABLA_IRQ_SLIMBUS))
- wcd9xxx_irq_dispatch(wcd9xxx, TABLA_IRQ_SLIMBUS);
+ if (status[BIT_BYTE(WCD9XXX_IRQ_SLIMBUS)] &
+ BYTE_BIT_MASK(WCD9XXX_IRQ_SLIMBUS))
+ wcd9xxx_irq_dispatch(wcd9xxx, WCD9XXX_IRQ_SLIMBUS);
/* Since codec has only one hardware irq line which is shared by
* codec's different internal interrupts, so it's possible master irq
* handler dispatches multiple nested irq handlers after breaking
* order. Dispatch MBHC interrupts order to follow MBHC state
* machine's order */
- for (i = TABLA_IRQ_MBHC_INSERTION; i >= TABLA_IRQ_MBHC_REMOVAL; i--) {
+ for (i = WCD9XXX_IRQ_MBHC_INSERTION;
+ i >= WCD9XXX_IRQ_MBHC_REMOVAL; i--) {
if (status[BIT_BYTE(i)] & BYTE_BIT_MASK(i))
wcd9xxx_irq_dispatch(wcd9xxx, i);
}
- for (i = TABLA_IRQ_BG_PRECHARGE; i < TABLA_NUM_IRQS; i++) {
+ for (i = WCD9XXX_IRQ_BG_PRECHARGE; i < wcd9xxx->num_irqs; i++) {
if (status[BIT_BYTE(i)] & BYTE_BIT_MASK(i))
wcd9xxx_irq_dispatch(wcd9xxx, i);
}
@@ -217,59 +252,105 @@
return IRQ_HANDLED;
}
+void wcd9xxx_free_irq(struct wcd9xxx *wcd9xxx, int irq, void *data)
+{
+ free_irq(phyirq_to_virq(wcd9xxx, irq), data);
+}
+
+void wcd9xxx_enable_irq(struct wcd9xxx *wcd9xxx, int irq)
+{
+ enable_irq(phyirq_to_virq(wcd9xxx, irq));
+}
+
+void wcd9xxx_disable_irq(struct wcd9xxx *wcd9xxx, int irq)
+{
+ disable_irq_nosync(phyirq_to_virq(wcd9xxx, irq));
+}
+
+void wcd9xxx_disable_irq_sync(struct wcd9xxx *wcd9xxx, int irq)
+{
+ disable_irq(phyirq_to_virq(wcd9xxx, irq));
+}
+
+static int wcd9xxx_irq_setup_downstream_irq(struct wcd9xxx *wcd9xxx)
+{
+ int irq, virq, ret;
+
+ pr_debug("%s: enter\n", __func__);
+
+ for (irq = 0; irq < wcd9xxx->num_irqs; irq++) {
+ /* Map OF irq */
+ virq = wcd9xxx_map_irq(wcd9xxx, irq);
+ pr_debug("%s: irq %d -> %d\n", __func__, irq, virq);
+ if (virq == NO_IRQ) {
+ pr_err("%s, No interrupt specifier for irq %d\n",
+ __func__, irq);
+ return NO_IRQ;
+ }
+
+ ret = irq_set_chip_data(virq, wcd9xxx);
+ if (ret) {
+ pr_err("%s: Failed to configure irq %d (%d)\n",
+ __func__, irq, ret);
+ return ret;
+ }
+
+ if (wcd9xxx->irq_level_high[irq])
+ irq_set_chip_and_handler(virq, &wcd9xxx_irq_chip,
+ handle_level_irq);
+ else
+ irq_set_chip_and_handler(virq, &wcd9xxx_irq_chip,
+ handle_edge_irq);
+
+ irq_set_nested_thread(virq, 1);
+ }
+
+ pr_debug("%s: leave\n", __func__);
+
+ return 0;
+}
+
int wcd9xxx_irq_init(struct wcd9xxx *wcd9xxx)
{
- int ret;
- unsigned int i, cur_irq;
+ int i, ret;
+ u8 irq_level[wcd9xxx_num_irq_regs(wcd9xxx)];
mutex_init(&wcd9xxx->irq_lock);
+ wcd9xxx->irq = wcd9xxx_irq_get_upstream_irq(wcd9xxx);
if (!wcd9xxx->irq) {
- dev_warn(wcd9xxx->dev,
- "No interrupt specified, no interrupts\n");
- wcd9xxx->irq_base = 0;
- return 0;
+ pr_warn("%s: irq driver is not yet initialized\n", __func__);
+ mutex_destroy(&wcd9xxx->irq_lock);
+ return -EPROBE_DEFER;
+ }
+ pr_debug("%s: probed irq %d\n", __func__, wcd9xxx->irq);
+
+ /* Setup downstream IRQs */
+ ret = wcd9xxx_irq_setup_downstream_irq(wcd9xxx);
+ if (ret) {
+ pr_err("%s: Failed to setup downstream IRQ\n", __func__);
+ wcd9xxx_irq_put_upstream_irq(wcd9xxx);
+ return ret;
}
- if (!wcd9xxx->irq_base) {
- dev_err(wcd9xxx->dev,
- "No interrupt base specified, no interrupts\n");
- return 0;
- }
- /* Mask the individual interrupt sources */
- for (i = 0, cur_irq = wcd9xxx->irq_base; i < TABLA_NUM_IRQS; i++,
- cur_irq++) {
+ /* All other wcd9xxx interrupts are edge triggered */
+ wcd9xxx->irq_level_high[0] = true;
- irq_set_chip_data(cur_irq, wcd9xxx);
-
- if (wcd9xxx_irqs[i].level)
- irq_set_chip_and_handler(cur_irq, &wcd9xxx_irq_chip,
- handle_level_irq);
- else
- irq_set_chip_and_handler(cur_irq, &wcd9xxx_irq_chip,
- handle_edge_irq);
-
- irq_set_nested_thread(cur_irq, 1);
-
- /* ARM needs us to explicitly flag the IRQ as valid
- * and will set them noprobe when we do so. */
-#ifdef CONFIG_ARM
- set_irq_flags(cur_irq, IRQF_VALID);
-#else
- set_irq_noprobe(cur_irq);
-#endif
-
+ /* mask all the interrupts */
+ memset(irq_level, 0, wcd9xxx_num_irq_regs(wcd9xxx));
+ for (i = 0; i < wcd9xxx->num_irqs; i++) {
wcd9xxx->irq_masks_cur[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
wcd9xxx->irq_masks_cache[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
- wcd9xxx->irq_level[BIT_BYTE(i)] |= wcd9xxx_irqs[i].level <<
- (i % BITS_PER_BYTE);
+ irq_level[BIT_BYTE(i)] |=
+ wcd9xxx->irq_level_high[i] << (i % BITS_PER_BYTE);
}
- for (i = 0; i < WCD9XXX_NUM_IRQ_REGS; i++) {
+
+ for (i = 0; i < wcd9xxx_num_irq_regs(wcd9xxx); i++) {
/* Initialize interrupt mask and level registers */
- wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_LEVEL0 + i,
- wcd9xxx->irq_level[i]);
- wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MASK0 + i,
- wcd9xxx->irq_masks_cur[i]);
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_LEVEL0 + i,
+ irq_level[i]);
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_MASK0 + i,
+ wcd9xxx->irq_masks_cur[i]);
}
ret = request_threaded_irq(wcd9xxx->irq, NULL, wcd9xxx_irq_thread,
@@ -294,18 +375,226 @@
free_irq(wcd9xxx->irq, wcd9xxx);
}
- if (ret)
+ if (ret) {
+ pr_err("%s: Failed to init wcd9xxx irq\n", __func__);
+ wcd9xxx_irq_put_upstream_irq(wcd9xxx);
mutex_destroy(&wcd9xxx->irq_lock);
+ }
return ret;
}
+int wcd9xxx_request_irq(struct wcd9xxx *wcd9xxx, int irq, irq_handler_t handler,
+ const char *name, void *data)
+{
+ int virq;
+
+ virq = phyirq_to_virq(wcd9xxx, irq);
+
+ /*
+ * ARM needs us to explicitly flag the IRQ as valid
+ * and will set them noprobe when we do so.
+ */
+#ifdef CONFIG_ARM
+ set_irq_flags(virq, IRQF_VALID);
+#else
+ set_irq_noprobe(virq);
+#endif
+
+ return request_threaded_irq(virq, NULL, handler, IRQF_TRIGGER_RISING,
+ name, data);
+}
+
void wcd9xxx_irq_exit(struct wcd9xxx *wcd9xxx)
{
if (wcd9xxx->irq) {
disable_irq_wake(wcd9xxx->irq);
free_irq(wcd9xxx->irq, wcd9xxx);
+ /* Release parent's of node */
+ wcd9xxx_irq_put_upstream_irq(wcd9xxx);
device_init_wakeup(wcd9xxx->dev, 0);
}
mutex_destroy(&wcd9xxx->irq_lock);
}
+
+#ifndef CONFIG_OF
+static int phyirq_to_virq(struct wcd9xxx *wcd9xxx, int offset)
+{
+ return wcd9xxx->irq_base + offset;
+}
+
+static int virq_to_phyirq(struct wcd9xxx *wcd9xxx, int virq)
+{
+ return virq - wcd9xxx->irq_base;
+}
+
+static unsigned int wcd9xxx_irq_get_upstream_irq(struct wcd9xxx *wcd9xxx)
+{
+ return wcd9xxx->irq;
+}
+
+static void wcd9xxx_irq_put_upstream_irq(struct wcd9xxx *wcd9xxx)
+{
+ /* Do nothing */
+}
+
+static int wcd9xxx_map_irq(struct wcd9xxx *wcd9xxx, int irq)
+{
+ return phyirq_to_virq(wcd9xxx, irq);
+}
+#else
+int __init wcd9xxx_irq_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ struct wcd9xxx_irq_drv_data *data;
+
+ pr_debug("%s: node %s, node parent %s\n", __func__,
+ node->name, node->parent->name);
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /*
+ * wcd9xxx_intc interrupt controller supports N to N irq mapping with
+ * single cell binding with irq numbers(offsets) only.
+ * Use irq_domain_simple_ops that has irq_domain_simple_map and
+ * irq_domain_xlate_onetwocell.
+ */
+ data->domain = irq_domain_add_linear(node, WCD9XXX_MAX_NUM_IRQS,
+ &irq_domain_simple_ops, data);
+ if (!data->domain) {
+ kfree(data);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static struct wcd9xxx_irq_drv_data *
+wcd9xxx_get_irq_drv_d(const struct wcd9xxx *wcd9xxx)
+{
+ struct device_node *pnode;
+ struct irq_domain *domain;
+
+ pnode = of_irq_find_parent(wcd9xxx->dev->of_node);
+ /* Shouldn't happen */
+ if (unlikely(!pnode))
+ return NULL;
+
+ domain = irq_find_host(pnode);
+ return (struct wcd9xxx_irq_drv_data *)domain->host_data;
+}
+
+static int phyirq_to_virq(struct wcd9xxx *wcd9xxx, int offset)
+{
+ struct wcd9xxx_irq_drv_data *data;
+
+ data = wcd9xxx_get_irq_drv_d(wcd9xxx);
+ if (!data) {
+ pr_warn("%s: not registered to interrupt controller\n",
+ __func__);
+ return -EINVAL;
+ }
+ return irq_linear_revmap(data->domain, offset);
+}
+
+static int virq_to_phyirq(struct wcd9xxx *wcd9xxx, int virq)
+{
+ struct irq_data *irq_data = irq_get_irq_data(virq);
+ return irq_data->hwirq;
+}
+
+static unsigned int wcd9xxx_irq_get_upstream_irq(struct wcd9xxx *wcd9xxx)
+{
+ struct wcd9xxx_irq_drv_data *data;
+
+ /* Hold parent's of node */
+ if (!of_node_get(of_irq_find_parent(wcd9xxx->dev->of_node)))
+ return -EINVAL;
+
+ data = wcd9xxx_get_irq_drv_d(wcd9xxx);
+ if (!data) {
+ pr_err("%s: interrupt controller is not registerd\n", __func__);
+ return 0;
+ }
+
+ rmb();
+ return data->irq;
+}
+
+static void wcd9xxx_irq_put_upstream_irq(struct wcd9xxx *wcd9xxx)
+{
+ /* Hold parent's of node */
+ of_node_put(of_irq_find_parent(wcd9xxx->dev->of_node));
+}
+
+static int wcd9xxx_map_irq(struct wcd9xxx *wcd9xxx, int irq)
+{
+ return of_irq_to_resource(wcd9xxx->dev->of_node, irq, NULL);
+}
+
+static int __devinit wcd9xxx_irq_probe(struct platform_device *pdev)
+{
+ int irq;
+ struct irq_domain *domain;
+ struct wcd9xxx_irq_drv_data *data;
+ int ret = -EINVAL;
+
+ irq = platform_get_irq_byname(pdev, "cdc-int");
+ if (irq < 0) {
+ dev_err(&pdev->dev, "%s: Couldn't find cdc-int node(%d)\n",
+ __func__, irq);
+ return -EINVAL;
+ } else {
+ dev_dbg(&pdev->dev, "%s: virq = %d\n", __func__, irq);
+ domain = irq_find_host(pdev->dev.of_node);
+ data = (struct wcd9xxx_irq_drv_data *)domain->host_data;
+ data->irq = irq;
+ wmb();
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int wcd9xxx_irq_remove(struct platform_device *pdev)
+{
+ struct irq_domain *domain;
+ struct wcd9xxx_irq_drv_data *data;
+
+ domain = irq_find_host(pdev->dev.of_node);
+ data = (struct wcd9xxx_irq_drv_data *)domain->host_data;
+ data->irq = 0;
+ wmb();
+
+ return 0;
+}
+
+static const struct of_device_id of_match[] = {
+ { .compatible = "qcom,wcd9xxx-irq" },
+ { }
+};
+
+static struct platform_driver wcd9xxx_irq_driver = {
+ .probe = wcd9xxx_irq_probe,
+ .remove = wcd9xxx_irq_remove,
+ .driver = {
+ .name = "wcd9xxx_intc",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(of_match),
+ },
+};
+
+static int wcd9xxx_irq_drv_init(void)
+{
+ return platform_driver_register(&wcd9xxx_irq_driver);
+}
+subsys_initcall(wcd9xxx_irq_drv_init);
+
+static void wcd9xxx_irq_drv_exit(void)
+{
+ platform_driver_unregister(&wcd9xxx_irq_driver);
+}
+module_exit(wcd9xxx_irq_drv_exit);
+#endif /* CONFIG_OF */
diff --git a/drivers/mfd/wcd9xxx-slimslave.c b/drivers/mfd/wcd9xxx-slimslave.c
index b4cf435..6e6de37 100644
--- a/drivers/mfd/wcd9xxx-slimslave.c
+++ b/drivers/mfd/wcd9xxx-slimslave.c
@@ -16,44 +16,20 @@
#define WCD9XXX_CHIP_ID_TAIKO 0x00000201
-struct wcd9xxx_slim_sch_rx {
- u32 sph;
- u32 ch_num;
- u16 ch_h;
- u16 grph;
-};
-
-struct wcd9xxx_slim_sch_tx {
- u32 sph;
- u32 ch_num;
- u16 ch_h;
- u16 grph;
-};
-
struct wcd9xxx_slim_sch {
- struct wcd9xxx_slim_sch_rx rx[SLIM_MAX_RX_PORTS];
- struct wcd9xxx_slim_sch_tx tx[SLIM_MAX_TX_PORTS];
-
- u16 rx_port_start_offset;
- u16 num_rx_slave_port;
- u16 port_ch_0_start_port_id;
- u16 port_ch_0_end_port_id;
- u16 pgd_tx_port_ch_1_end_port_id;
u16 rx_port_ch_reg_base;
u16 port_tx_cfg_reg_base;
u16 port_rx_cfg_reg_base;
- int number_of_tx_slave_dev_ports;
- int number_of_rx_slave_dev_ports;
};
static struct wcd9xxx_slim_sch sh_ch;
-static int wcd9xxx_alloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx,
- u8 wcd9xxx_pgd_la);
-static int wcd9xxx_alloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx,
- u8 wcd9xxx_pgd_la);
-static int wcd9xxx_dealloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx);
-static int wcd9xxx_dealloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx);
+static int wcd9xxx_alloc_slim_sh_ch(struct wcd9xxx *wcd9xxx,
+ u8 wcd9xxx_pgd_la, u32 cnt,
+ struct wcd9xxx_ch *channels, u32 path);
+
+static int wcd9xxx_dealloc_slim_sh_ch(struct slim_device *slim,
+ u32 cnt, struct wcd9xxx_ch *channels);
static int wcd9xxx_configure_ports(struct wcd9xxx *wcd9xxx)
{
@@ -65,55 +41,27 @@
id = cpu_to_be32(id);
pr_debug("%s: chip id 0x%08x\n", __func__, id);
if (id != WCD9XXX_CHIP_ID_TAIKO) {
- sh_ch.rx_port_start_offset =
- TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS;
- sh_ch.num_rx_slave_port =
- TABLA_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS;
- sh_ch.port_ch_0_start_port_id =
- TABLA_SB_PGD_RX_PORT_MULTI_CHANNEL_0_START_PORT_ID;
- sh_ch.port_ch_0_end_port_id =
- TABLA_SB_PGD_RX_PORT_MULTI_CHANNEL_0_END_PORT_ID;
- sh_ch.pgd_tx_port_ch_1_end_port_id =
- TABLA_SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID;
-
- sh_ch.rx_port_ch_reg_base =
- 0x180 + (TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS * 4);
- sh_ch.port_rx_cfg_reg_base =
- 0x040 + (TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS);
- sh_ch.port_tx_cfg_reg_base = 0x040;
-
- sh_ch.number_of_tx_slave_dev_ports =
- TABLA_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS;
- sh_ch.number_of_rx_slave_dev_ports =
- TABLA_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS;
- } else {
- sh_ch.rx_port_start_offset =
- TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS;
- sh_ch.num_rx_slave_port =
- TAIKO_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS;
- sh_ch.port_ch_0_start_port_id =
- TAIKO_SB_PGD_RX_PORT_MULTI_CHANNEL_0_START_PORT_ID;
- sh_ch.port_ch_0_end_port_id =
- TAIKO_SB_PGD_RX_PORT_MULTI_CHANNEL_0_END_PORT_ID;
- sh_ch.pgd_tx_port_ch_1_end_port_id =
- TAIKO_SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID;
-
- sh_ch.rx_port_ch_reg_base = 0x180;
+ sh_ch.rx_port_ch_reg_base = 0x180 ;
sh_ch.port_rx_cfg_reg_base = 0x040;
+ sh_ch.port_tx_cfg_reg_base = 0x040;
+ } else {
+ sh_ch.rx_port_ch_reg_base =
+ 0x180 - (TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS * 4);
+ sh_ch.port_rx_cfg_reg_base =
+ 0x040 - TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS ;
sh_ch.port_tx_cfg_reg_base = 0x050;
-
- sh_ch.number_of_tx_slave_dev_ports =
- TAIKO_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS;
- sh_ch.number_of_rx_slave_dev_ports =
- TAIKO_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS;
}
return 0;
}
-int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la)
+
+int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
{
int ret = 0;
+ int i;
ret = wcd9xxx_configure_ports(wcd9xxx);
if (ret) {
@@ -122,125 +70,106 @@
goto err;
}
- ret = wcd9xxx_alloc_slim_sh_ch_rx(wcd9xxx, wcd9xxx_pgd_la);
- if (ret) {
- pr_err("%s: Failed to alloc rx slimbus shared channels\n",
- __func__);
- goto err;
+ if (wcd9xxx->rx_chs) {
+ wcd9xxx->num_rx_port = rx_num;
+ for (i = 0; i < rx_num; i++) {
+ wcd9xxx->rx_chs[i].ch_num = rx_slot[i];
+ INIT_LIST_HEAD(&wcd9xxx->rx_chs[i].list);
+ }
+ ret = wcd9xxx_alloc_slim_sh_ch(wcd9xxx, wcd9xxx_pgd_la,
+ wcd9xxx->num_rx_port,
+ wcd9xxx->rx_chs,
+ SLIM_SINK);
+ if (ret) {
+ pr_err("%s: Failed to alloc %d rx slimbus channels\n",
+ __func__, wcd9xxx->num_rx_port);
+ kfree(wcd9xxx->rx_chs);
+ wcd9xxx->rx_chs = NULL;
+ wcd9xxx->num_rx_port = 0;
+ }
+ } else {
+ pr_err("Not able to allocate memory for %d slimbus rx ports\n",
+ wcd9xxx->num_rx_port);
}
- ret = wcd9xxx_alloc_slim_sh_ch_tx(wcd9xxx, wcd9xxx_pgd_la);
- if (ret) {
- pr_err("%s: Failed to alloc tx slimbus shared channels\n",
- __func__);
- goto tx_err;
+
+ if (wcd9xxx->tx_chs) {
+ wcd9xxx->num_tx_port = tx_num;
+ for (i = 0; i < tx_num; i++) {
+ wcd9xxx->tx_chs[i].ch_num = tx_slot[i];
+ INIT_LIST_HEAD(&wcd9xxx->tx_chs[i].list);
+ }
+ ret = wcd9xxx_alloc_slim_sh_ch(wcd9xxx, wcd9xxx_pgd_la,
+ wcd9xxx->num_tx_port,
+ wcd9xxx->tx_chs,
+ SLIM_SRC);
+ if (ret) {
+ pr_err("%s: Failed to alloc %d tx slimbus channels\n",
+ __func__, wcd9xxx->num_tx_port);
+ kfree(wcd9xxx->tx_chs);
+ wcd9xxx->tx_chs = NULL;
+ wcd9xxx->num_tx_port = 0;
+ }
+ } else {
+ pr_err("Not able to allocate memory for %d slimbus tx ports\n",
+ wcd9xxx->num_tx_port);
}
+
return 0;
-tx_err:
- wcd9xxx_dealloc_slim_sh_ch_rx(wcd9xxx);
err:
return ret;
}
-
int wcd9xxx_deinit_slimslave(struct wcd9xxx *wcd9xxx)
{
- int ret = 0;
- ret = wcd9xxx_dealloc_slim_sh_ch_rx(wcd9xxx);
- if (ret < 0) {
- pr_err("%s: fail to dealloc rx slim ports\n", __func__);
- goto err;
+ if (wcd9xxx->num_rx_port) {
+ wcd9xxx_dealloc_slim_sh_ch(wcd9xxx->slim,
+ wcd9xxx->num_rx_port,
+ wcd9xxx->rx_chs);
+ wcd9xxx->num_rx_port = 0;
}
- ret = wcd9xxx_dealloc_slim_sh_ch_tx(wcd9xxx);
- if (ret < 0) {
- pr_err("%s: fail to dealloc tx slim ports\n", __func__);
- goto err;
+ if (wcd9xxx->num_tx_port) {
+ wcd9xxx_dealloc_slim_sh_ch(wcd9xxx->slim,
+ wcd9xxx->num_tx_port,
+ wcd9xxx->tx_chs);
+ wcd9xxx->num_tx_port = 0;
}
-err:
- return ret;
-}
-
-int wcd9xxx_get_channel(struct wcd9xxx *wcd9xxx, unsigned int *rx_ch,
- unsigned int *tx_ch)
-{
- int ch_idx = 0;
- struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
- struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
-
- for (ch_idx = 0; ch_idx < sh_ch.number_of_rx_slave_dev_ports; ch_idx++)
- rx_ch[ch_idx] = rx[ch_idx].ch_num;
- for (ch_idx = 0; ch_idx < sh_ch.number_of_tx_slave_dev_ports; ch_idx++)
- tx_ch[ch_idx] = tx[ch_idx].ch_num;
return 0;
}
-static int wcd9xxx_alloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx,
- u8 wcd9xxx_pgd_la)
+
+static int wcd9xxx_alloc_slim_sh_ch(struct wcd9xxx *wcd9xxx,
+ u8 wcd9xxx_pgd_la, u32 cnt,
+ struct wcd9xxx_ch *channels, u32 path)
{
int ret = 0;
- u8 ch_idx ;
- u16 slave_port_id = 0;
- struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+ u32 ch_idx ;
- /*
- * DSP requires channel number to be between 128 and 255.
+ /* The slimbus channel allocation seem take longer time
+ * so do the allocation up front to avoid delay in start of
+ * playback
*/
pr_debug("%s: pgd_la[%d]\n", __func__, wcd9xxx_pgd_la);
- for (ch_idx = 0; ch_idx < sh_ch.number_of_rx_slave_dev_ports;
- ch_idx++) {
- slave_port_id = (ch_idx + sh_ch.rx_port_start_offset);
- rx[ch_idx].ch_num = slave_port_id + BASE_CH_NUM;
- ret = slim_get_slaveport(wcd9xxx_pgd_la, slave_port_id,
- &rx[ch_idx].sph, SLIM_SINK);
+ for (ch_idx = 0; ch_idx < cnt; ch_idx++) {
+ ret = slim_get_slaveport(wcd9xxx_pgd_la,
+ channels[ch_idx].port,
+ &channels[ch_idx].sph, path);
+ pr_debug("%s: pgd_la[%d] channels[%d].port[%d]\n"
+ "channels[%d].sph[%d] path[%d]\n",
+ __func__, wcd9xxx_pgd_la, ch_idx,
+ channels[ch_idx].port,
+ ch_idx, channels[ch_idx].sph, path);
if (ret < 0) {
pr_err("%s: slave port failure id[%d] ret[%d]\n",
- __func__, slave_port_id, ret);
+ __func__, channels[ch_idx].ch_num, ret);
goto err;
}
- ret = slim_query_ch(wcd9xxx->slim, rx[ch_idx].ch_num,
- &rx[ch_idx].ch_h);
+ ret = slim_query_ch(wcd9xxx->slim,
+ channels[ch_idx].ch_num,
+ &channels[ch_idx].ch_h);
if (ret < 0) {
pr_err("%s: slim_query_ch failed ch-num[%d] ret[%d]\n",
- __func__, rx[ch_idx].ch_num, ret);
- goto err;
- }
- pr_debug("%s:ch_num=%d ch_h=%d sph=%d la=%d slave_port_id %d\n",
- __func__, rx[ch_idx].ch_num, rx[ch_idx].ch_h,
- rx[ch_idx].sph, wcd9xxx_pgd_la, slave_port_id);
- }
-err:
- return ret;
-}
-
-static int wcd9xxx_alloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx,
- u8 wcd9xxx_pgd_la)
-{
- int ret = 0;
- u8 ch_idx;
- struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
- u16 slave_port_id = 0;
-
- pr_debug("%s: pgd_la[%d]\n", __func__, wcd9xxx_pgd_la);
- /* DSP requires channel number to be between 128 and 255. For RX port
- * use channel numbers from 138 to 144, for TX port
- * use channel numbers from 128 to 137
- */
- for (ch_idx = 0; ch_idx < sh_ch.number_of_tx_slave_dev_ports;
- ch_idx++) {
- slave_port_id = ch_idx;
- tx[ch_idx].ch_num = slave_port_id + BASE_CH_NUM;
- ret = slim_get_slaveport(wcd9xxx_pgd_la, slave_port_id,
- &tx[ch_idx].sph, SLIM_SRC);
- if (ret < 0) {
- pr_err("%s: slave port failure id[%d] ret[%d]\n",
- __func__, slave_port_id, ret);
- goto err;
- }
- ret = slim_query_ch(wcd9xxx->slim, tx[ch_idx].ch_num,
- &tx[ch_idx].ch_h);
- if (ret < 0) {
- pr_err("%s: slim_query_ch failed ch-num[%d] ret[%d]\n",
- __func__, tx[ch_idx].ch_num, ret);
+ __func__, channels[ch_idx].ch_num, ret);
goto err;
}
}
@@ -248,116 +177,46 @@
return ret;
}
-static int wcd9xxx_dealloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx)
+static int wcd9xxx_dealloc_slim_sh_ch(struct slim_device *slim,
+ u32 cnt, struct wcd9xxx_ch *channels)
{
int idx = 0;
int ret = 0;
- struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
/* slim_dealloc_ch */
- for (idx = 0; idx < sh_ch.number_of_rx_slave_dev_ports; idx++) {
- ret = slim_dealloc_ch(wcd9xxx->slim, rx[idx].ch_h);
+ for (idx = 0; idx < cnt; idx++) {
+ ret = slim_dealloc_ch(slim, channels[idx].ch_h);
if (ret < 0) {
pr_err("%s: slim_dealloc_ch fail ret[%d] ch_h[%d]\n",
- __func__, ret, rx[idx].ch_h);
+ __func__, ret, channels[idx].ch_h);
}
}
- memset(sh_ch.rx, 0, sizeof(sh_ch.rx));
- return ret;
-}
-
-static int wcd9xxx_dealloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx)
-{
- int idx = 0;
- int ret = 0;
- struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
- /* slim_dealloc_ch */
- for (idx = 0; idx < sh_ch.number_of_tx_slave_dev_ports; idx++) {
- ret = slim_dealloc_ch(wcd9xxx->slim, tx[idx].ch_h);
- if (ret < 0) {
- pr_err("%s: slim_dealloc_ch fail ret[%d] ch_h[%d]\n",
- __func__, ret, tx[idx].ch_h);
- }
- }
- memset(sh_ch.tx, 0, sizeof(sh_ch.tx));
return ret;
}
/* Enable slimbus slave device for RX path */
-int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
- unsigned int ch_cnt, unsigned int rate)
+int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx,
+ struct list_head *wcd9xxx_ch_list,
+ unsigned int rate, unsigned int bit_width,
+ u16 *grph)
{
- u8 i;
- u16 grph;
- u32 sph[SLIM_MAX_RX_PORTS] = {0};
+ u8 ch_cnt = 0;
u16 ch_h[SLIM_MAX_RX_PORTS] = {0};
- u16 slave_port_id;
- u8 payload_rx = 0, wm_payload = 0;
- int ret, idx = 0;
- unsigned short multi_chan_cfg_reg_addr;
- struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+ u8 payload = 0;
+ u16 codec_port = 0;
+ int ret;
struct slim_ch prop;
+ struct wcd9xxx_ch *rx;
/* Configure slave interface device */
- pr_debug("%s: ch_cnt[%d] rate=%d\n", __func__, ch_cnt, rate);
- for (i = 0; i < ch_cnt; i++) {
- idx = (ch_num[i] - BASE_CH_NUM - sh_ch.rx_port_start_offset);
- ch_h[i] = rx[idx].ch_h;
- sph[i] = rx[idx].sph;
- slave_port_id = idx;
- pr_debug("%s: idx %d, ch_h %d, sph %d\n",
- __func__, idx, ch_h[i], sph[i]);
- if ((slave_port_id > sh_ch.num_rx_slave_port)) {
- pr_err("Slimbus: invalid slave port id: %d",
- slave_port_id);
- ret = -EINVAL;
- goto err;
- }
- slave_port_id += sh_ch.rx_port_start_offset;
- pr_debug("%s: slave_port_id %d\n", __func__, slave_port_id);
- /* look for the valid port range and chose the
- * payload accordingly
- */
- if ((slave_port_id > sh_ch.pgd_tx_port_ch_1_end_port_id) &&
- (slave_port_id <= sh_ch.port_ch_0_end_port_id)) {
- payload_rx = payload_rx |
- (1 << (slave_port_id -
- sh_ch.port_ch_0_start_port_id));
- } else {
- ret = -EINVAL;
- goto err;
- }
-
- multi_chan_cfg_reg_addr =
- SB_PGD_RX_PORT_MULTI_CHANNEL_0(sh_ch.rx_port_ch_reg_base,
- idx);
- pr_debug("%s: multi_chan_cfg_reg_addr 0x%x\n", __func__,
- multi_chan_cfg_reg_addr);
-
- /* write to interface device */
- ret = wcd9xxx_interface_reg_write(wcd9xxx,
- multi_chan_cfg_reg_addr,
- payload_rx);
- if (ret < 0) {
- pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
- __func__, multi_chan_cfg_reg_addr,
- payload_rx, ret);
- goto err;
- }
- /* configure the slave port for water mark and enable*/
- wm_payload = (SLAVE_PORT_WATER_MARK_VALUE <<
- SLAVE_PORT_WATER_MARK_SHIFT) + SLAVE_PORT_ENABLE;
- ret = wcd9xxx_interface_reg_write(
- wcd9xxx,
- SB_PGD_PORT_CFG_BYTE_ADDR(
- sh_ch.port_rx_cfg_reg_base, idx),
- wm_payload);
- if (ret < 0) {
- pr_err("%s:watermark set failure for port[%d] ret[%d]",
- __func__, slave_port_id, ret);
- }
+ list_for_each_entry(rx, wcd9xxx_ch_list, list) {
+ payload |= 1 << rx->shift;
+ ch_h[ch_cnt] = rx->ch_h;
+ ch_cnt++;
+ pr_debug("list ch->ch_h %d ch->sph %d\n", rx->ch_h, rx->sph);
}
-
+ pr_debug("%s: ch_cnt[%d] rate=%d WATER_MARK_VAL %d\n",
+ __func__, ch_cnt, rate, WATER_MARK_VAL);
/* slim_define_ch api */
prop.prot = SLIM_AUTO_ISO;
prop.baser = SLIM_RATE_4000HZ;
@@ -366,130 +225,97 @@
prop.ratem = (rate/4000);
prop.sampleszbits = 16;
- ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt, true, &grph);
+ pr_debug("Before slim_define_ch:\n"
+ "ch_cnt %d,ch_h[0] %d ch_h[1] %d, grph %d\n",
+ ch_cnt, ch_h[0], ch_h[1], *grph);
+ ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt,
+ true, grph);
if (ret < 0) {
pr_err("%s: slim_define_ch failed ret[%d]\n",
- __func__, ret);
+ __func__, ret);
goto err;
}
- for (i = 0; i < ch_cnt; i++) {
- ret = slim_connect_sink(wcd9xxx->slim, &sph[i], 1, ch_h[i]);
+
+ list_for_each_entry(rx, wcd9xxx_ch_list, list) {
+ codec_port = rx->port;
+ pr_debug("%s: codec_port %d rx 0x%x, payload %d\n"
+ "sh_ch.rx_port_ch_reg_base0 0x%x\n"
+ "sh_ch.port_rx_cfg_reg_base 0x%x\n",
+ __func__, codec_port, (u32)rx, payload,
+ sh_ch.rx_port_ch_reg_base,
+ sh_ch.port_rx_cfg_reg_base);
+
+ /* look for the valid port range and chose the
+ * payload accordingly
+ */
+ /* write to interface device */
+ ret = wcd9xxx_interface_reg_write(wcd9xxx,
+ SB_PGD_RX_PORT_MULTI_CHANNEL_0(
+ sh_ch.rx_port_ch_reg_base, codec_port),
+ payload);
+
+ if (ret < 0) {
+ pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+ __func__,
+ SB_PGD_RX_PORT_MULTI_CHANNEL_0(
+ sh_ch.rx_port_ch_reg_base, codec_port),
+ payload, ret);
+ goto err;
+ }
+ /* configure the slave port for water mark and enable*/
+ ret = wcd9xxx_interface_reg_write(wcd9xxx,
+ SB_PGD_PORT_CFG_BYTE_ADDR(
+ sh_ch.port_rx_cfg_reg_base, codec_port),
+ WATER_MARK_VAL);
+ if (ret < 0) {
+ pr_err("%s:watermark set failure for port[%d] ret[%d]",
+ __func__, codec_port, ret);
+ }
+
+ ret = slim_connect_sink(wcd9xxx->slim, &rx->sph, 1, rx->ch_h);
if (ret < 0) {
pr_err("%s: slim_connect_sink failed ret[%d]\n",
- __func__, ret);
+ __func__, ret);
goto err_close_slim_sch;
}
}
/* slim_control_ch */
- ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_ACTIVATE, true);
+ ret = slim_control_ch(wcd9xxx->slim, *grph, SLIM_CH_ACTIVATE,
+ true);
if (ret < 0) {
pr_err("%s: slim_control_ch failed ret[%d]\n",
- __func__, ret);
+ __func__, ret);
goto err_close_slim_sch;
}
- for (i = 0; i < ch_cnt; i++) {
- idx = (ch_num[i] - BASE_CH_NUM - sh_ch.rx_port_start_offset);
- rx[idx].grph = grph;
- }
return 0;
err_close_slim_sch:
/* release all acquired handles */
- wcd9xxx_close_slim_sch_rx(wcd9xxx, ch_num, ch_cnt);
+ wcd9xxx_close_slim_sch_rx(wcd9xxx, wcd9xxx_ch_list, *grph);
err:
return ret;
}
EXPORT_SYMBOL_GPL(wcd9xxx_cfg_slim_sch_rx);
/* Enable slimbus slave device for RX path */
-int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
- unsigned int ch_cnt, unsigned int rate)
+int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx,
+ struct list_head *wcd9xxx_ch_list,
+ unsigned int rate, unsigned int bit_width,
+ u16 *grph)
{
- u8 i = 0;
- u8 payload_tx_0 = 0, payload_tx_1 = 0, wm_payload = 0;
- u16 grph;
- u32 sph[SLIM_MAX_TX_PORTS] = {0};
+ u16 ch_cnt = 0;
+ u16 payload = 0;
u16 ch_h[SLIM_MAX_TX_PORTS] = {0};
- u16 idx = 0, slave_port_id;
+ u16 codec_port;
int ret = 0;
- unsigned short multi_chan_cfg_reg_addr;
+ struct wcd9xxx_ch *tx;
- struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
struct slim_ch prop;
- pr_debug("%s: ch_cnt[%d] rate[%d]\n", __func__, ch_cnt, rate);
- for (i = 0; i < ch_cnt; i++) {
- idx = (ch_num[i] - BASE_CH_NUM);
- ch_h[i] = tx[idx].ch_h;
- sph[i] = tx[idx].sph;
- slave_port_id = idx;
- pr_debug("%s: idx %d, ch_h %d, sph %d, slave_port_id %d\n",
- __func__, idx, ch_h[i], sph[i], slave_port_id);
- if (slave_port_id > sh_ch.number_of_tx_slave_dev_ports) {
- pr_err("SLIMbus: invalid slave port id: %d",
- slave_port_id);
- ret = -EINVAL;
- goto err;
- }
- /* look for the valid port range and chose the
- * payload accordingly
- */
- if (slave_port_id <=
- SB_PGD_TX_PORT_MULTI_CHANNEL_0_END_PORT_ID) {
- payload_tx_0 = payload_tx_0 | (1 << slave_port_id);
- } else if (slave_port_id <=
- sh_ch.pgd_tx_port_ch_1_end_port_id) {
- payload_tx_1 = payload_tx_1 |
- (1 << (slave_port_id -
- SB_PGD_TX_PORT_MULTI_CHANNEL_1_START_PORT_ID));
- } else {
- pr_err("%s: slave port id %d error\n", __func__,
- slave_port_id);
- ret = -EINVAL;
- goto err;
- }
- multi_chan_cfg_reg_addr =
- SB_PGD_TX_PORT_MULTI_CHANNEL_0(slave_port_id);
- pr_debug("%s: multi_chan_cfg_reg_addr 0x%x\n", __func__,
- multi_chan_cfg_reg_addr);
- /* write to interface device */
- ret = wcd9xxx_interface_reg_write(wcd9xxx,
- multi_chan_cfg_reg_addr,
- payload_tx_0);
- if (ret < 0) {
- pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
- __func__, multi_chan_cfg_reg_addr, payload_tx_0,
- ret);
- goto err;
- }
- multi_chan_cfg_reg_addr =
- SB_PGD_TX_PORT_MULTI_CHANNEL_1(slave_port_id);
- /* ports 8,9 */
- ret = wcd9xxx_interface_reg_write(wcd9xxx,
- multi_chan_cfg_reg_addr,
- payload_tx_1);
- if (ret < 0) {
- pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
- __func__, multi_chan_cfg_reg_addr,
- payload_tx_1, ret);
- goto err;
- }
- /* configure the slave port for water mark and enable*/
- wm_payload = (SLAVE_PORT_WATER_MARK_VALUE <<
- SLAVE_PORT_WATER_MARK_SHIFT) + SLAVE_PORT_ENABLE;
- pr_debug("%s: tx_cfg_reg 0x%x wm 0x%x\n", __func__,
- SB_PGD_PORT_CFG_BYTE_ADDR(sh_ch.port_tx_cfg_reg_base,
- slave_port_id), wm_payload);
- ret = wcd9xxx_interface_reg_write(
- wcd9xxx,
- SB_PGD_PORT_CFG_BYTE_ADDR(
- sh_ch.port_tx_cfg_reg_base,
- slave_port_id),
- wm_payload);
- if (ret < 0) {
- pr_err("%s: watermark set failure for port[%d] ret[%d]",
- __func__, slave_port_id, ret);
- }
+ list_for_each_entry(tx, wcd9xxx_ch_list, list) {
+ payload |= 1 << tx->shift;
+ ch_h[ch_cnt] = tx->ch_h;
+ ch_cnt++;
}
/* slim_define_ch api */
@@ -499,13 +325,53 @@
prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
prop.ratem = (rate/4000);
prop.sampleszbits = 16;
- ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt, true, &grph);
+ ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt,
+ true, grph);
if (ret < 0) {
- pr_err("%s: slim_define_ch failed ret[%d]\n", __func__, ret);
+ pr_err("%s: slim_define_ch failed ret[%d]\n",
+ __func__, ret);
goto err;
}
- for (i = 0; i < ch_cnt; i++) {
- ret = slim_connect_src(wcd9xxx->slim, sph[i], ch_h[i]);
+
+ pr_debug("%s: ch_cnt[%d] rate[%d]\n", __func__, ch_cnt, rate);
+ list_for_each_entry(tx, wcd9xxx_ch_list, list) {
+ codec_port = tx->port;
+ pr_debug("%s: codec_port %d rx 0x%x, payload 0x%x\n",
+ __func__, codec_port, (u32)tx, payload);
+ /* write to interface device */
+ ret = wcd9xxx_interface_reg_write(wcd9xxx,
+ SB_PGD_TX_PORT_MULTI_CHANNEL_0(codec_port),
+ payload & 0x00FF);
+ if (ret < 0) {
+ pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+ __func__,
+ SB_PGD_TX_PORT_MULTI_CHANNEL_0(codec_port),
+ payload, ret);
+ goto err;
+ }
+ /* ports 8,9 */
+ ret = wcd9xxx_interface_reg_write(wcd9xxx,
+ SB_PGD_TX_PORT_MULTI_CHANNEL_1(codec_port),
+ (payload & 0xFF00)>>8);
+ if (ret < 0) {
+ pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+ __func__,
+ SB_PGD_TX_PORT_MULTI_CHANNEL_1(codec_port),
+ payload, ret);
+ goto err;
+ }
+ /* configure the slave port for water mark and enable*/
+ ret = wcd9xxx_interface_reg_write(wcd9xxx,
+ SB_PGD_PORT_CFG_BYTE_ADDR(
+ sh_ch.port_tx_cfg_reg_base, codec_port),
+ WATER_MARK_VAL);
+ if (ret < 0) {
+ pr_err("%s:watermark set failure for port[%d] ret[%d]",
+ __func__, codec_port, ret);
+ }
+
+ ret = slim_connect_src(wcd9xxx->slim, tx->sph, tx->ch_h);
+
if (ret < 0) {
pr_err("%s: slim_connect_src failed ret[%d]\n",
__func__, ret);
@@ -513,91 +379,69 @@
}
}
/* slim_control_ch */
- ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_ACTIVATE, true);
+ ret = slim_control_ch(wcd9xxx->slim, *grph, SLIM_CH_ACTIVATE,
+ true);
if (ret < 0) {
pr_err("%s: slim_control_ch failed ret[%d]\n",
- __func__, ret);
+ __func__, ret);
goto err;
}
- for (i = 0; i < ch_cnt; i++) {
- idx = (ch_num[i] - BASE_CH_NUM);
- tx[idx].grph = grph;
- }
return 0;
err:
/* release all acquired handles */
- wcd9xxx_close_slim_sch_tx(wcd9xxx, ch_num, ch_cnt);
+ wcd9xxx_close_slim_sch_tx(wcd9xxx, wcd9xxx_ch_list, *grph);
return ret;
}
EXPORT_SYMBOL_GPL(wcd9xxx_cfg_slim_sch_tx);
-int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
- unsigned int ch_cnt)
+int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx,
+ struct list_head *wcd9xxx_ch_list, u16 grph)
{
- u16 grph = 0;
- int i = 0 , idx = 0;
+ u32 sph[SLIM_MAX_RX_PORTS] = {0};
+ int ch_cnt = 0 ;
int ret = 0;
- struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+ struct wcd9xxx_ch *rx;
- pr_debug("%s: ch_cnt[%d]\n", __func__, ch_cnt);
- for (i = 0; i < ch_cnt; i++) {
- idx = (ch_num[i] - BASE_CH_NUM - sh_ch.rx_port_start_offset);
- if (idx < 0) {
- pr_err("%s: Error:-Invalid index found = %d\n",
- __func__, idx);
- ret = -EINVAL;
- goto err;
- }
- grph = rx[idx].grph;
- pr_debug("%s: ch_num[%d] %d, idx %d, grph %x\n",
- __func__, i, ch_num[i], idx, grph);
- }
+ list_for_each_entry(rx, wcd9xxx_ch_list, list)
+ sph[ch_cnt++] = rx->sph;
+
+ pr_debug("%s ch_cht %d, sph[0] %d sph[1] %d\n", __func__, ch_cnt,
+ sph[0], sph[1]);
/* slim_control_ch (REMOVE) */
+ pr_debug("%s before slim_control_ch grph %d\n", __func__, grph);
ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true);
if (ret < 0) {
pr_err("%s: slim_control_ch failed ret[%d]\n", __func__, ret);
goto err;
}
- for (i = 0; i < ch_cnt; i++) {
- idx = (ch_num[i] - BASE_CH_NUM - sh_ch.rx_port_start_offset);
- rx[idx].grph = 0;
- }
err:
return ret;
}
EXPORT_SYMBOL_GPL(wcd9xxx_close_slim_sch_rx);
-int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
- unsigned int ch_cnt)
+int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx,
+ struct list_head *wcd9xxx_ch_list,
+ u16 grph)
{
- u16 grph = 0;
+ u32 sph[SLIM_MAX_TX_PORTS] = {0};
int ret = 0;
- int i = 0 , idx = 0;
- struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+ int ch_cnt = 0 ;
+ struct wcd9xxx_ch *tx;
- pr_debug("%s: ch_cnt[%d]\n", __func__, ch_cnt);
- for (i = 0; i < ch_cnt; i++) {
- idx = (ch_num[i] - BASE_CH_NUM);
- if (idx < 0) {
- pr_err("%s: Error:- Invalid index found = %d\n",
- __func__, idx);
- ret = -EINVAL;
- goto err;
- }
- grph = tx[idx].grph;
- }
+ pr_debug("%s\n", __func__);
+ list_for_each_entry(tx, wcd9xxx_ch_list, list)
+ sph[ch_cnt++] = tx->sph;
+
+ pr_debug("%s ch_cht %d, sph[0] %d sph[1] %d\n",
+ __func__, ch_cnt, sph[0], sph[1]);
/* slim_control_ch (REMOVE) */
ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true);
if (ret < 0) {
pr_err("%s: slim_control_ch failed ret[%d]\n",
- __func__, ret);
+ __func__, ret);
goto err;
}
- for (i = 0; i < ch_cnt; i++) {
- idx = (ch_num[i] - BASE_CH_NUM);
- tx[idx].grph = 0;
- }
err:
return ret;
}
@@ -607,8 +451,8 @@
{
int ret = 0;
- pr_debug("%s: ch_num[%d]\n", __func__, ch_num);
ret = (ch_num - BASE_CH_NUM);
+ pr_debug("%s: ch_num[%d] slave port[%d]\n", __func__, ch_num, ret);
if (ret < 0) {
pr_err("%s: Error:- Invalid slave port found = %d\n",
__func__, ret);
@@ -618,39 +462,16 @@
}
EXPORT_SYMBOL_GPL(wcd9xxx_get_slave_port);
-int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
- unsigned int ch_cnt, unsigned int rx_tx)
+int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx,
+ struct list_head *wcd9xxx_ch_list, u16 grph)
{
- u32 sph[SLIM_MAX_TX_PORTS] = {0};
- int i = 0 , idx = 0;
+ u32 sph[SLIM_MAX_TX_PORTS + SLIM_MAX_RX_PORTS] = {0};
+ int ch_cnt = 0 ;
int ret = 0;
- struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
- struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+ struct wcd9xxx_ch *slim_ch;
- pr_debug("%s: ch_cnt[%d], rx_tx flag = %d\n", __func__, ch_cnt, rx_tx);
- for (i = 0; i < ch_cnt; i++) {
- /* rx_tx will be 1 for rx, 0 for tx */
- if (rx_tx) {
- idx = (ch_num[i] - BASE_CH_NUM -
- sh_ch.rx_port_start_offset);
- if (idx < 0) {
- pr_err("%s: Invalid index found for RX = %d\n",
- __func__, idx);
- ret = -EINVAL;
- goto err;
- }
- sph[i] = rx[idx].sph;
- } else {
- idx = (ch_num[i] - BASE_CH_NUM);
- if (idx < 0) {
- pr_err("%s:Invalid index found for TX = %d\n",
- __func__, idx);
- ret = -EINVAL;
- goto err;
- }
- sph[i] = tx[idx].sph;
- }
- }
+ list_for_each_entry(slim_ch, wcd9xxx_ch_list, list)
+ sph[ch_cnt++] = slim_ch->sph;
/* slim_disconnect_port */
ret = slim_disconnect_ports(wcd9xxx->slim, sph, ch_cnt);
@@ -658,7 +479,6 @@
pr_err("%s: slim_disconnect_ports failed ret[%d]\n",
__func__, ret);
}
-err:
return ret;
}
EXPORT_SYMBOL_GPL(wcd9xxx_disconnect_port);
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 703aaa6..12f896e 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -38,6 +38,7 @@
#include <mach/msm_bus_board.h>
#include <mach/scm.h>
#include <mach/peripheral-loader.h>
+#include <mach/socinfo.h>
#include "qseecom_legacy.h"
#define QSEECOM_DEV "qseecom"
@@ -142,7 +143,6 @@
static DEFINE_MUTEX(pil_access_lock);
static DEFINE_MUTEX(qsee_bw_mutex);
-static DEFINE_MUTEX(qsee_sfpb_bw_mutex);
static DEFINE_MUTEX(app_access_lock);
static int qsee_bw_count;
@@ -180,6 +180,7 @@
int send_resp_flag;
uint32_t qseos_version;
+ struct device *pdev;
};
struct qseecom_client_handle {
@@ -209,9 +210,16 @@
atomic_t ioctl_count;
};
+struct clk *ce_core_clk;
+struct clk *ce_clk;
+struct clk *ce_core_src_clk;
+struct clk *ce_bus_clk;
+
/* Function proto types */
static int qsee_vote_for_clock(int32_t);
static void qsee_disable_clock_vote(int32_t);
+static int __qseecom_init_clk(void);
+static void __qseecom_disable_clk(void);
static int __qseecom_is_svc_unique(struct qseecom_dev_handle *data,
struct qseecom_register_listener_req *svc)
@@ -1198,28 +1206,41 @@
case CLK_DFAB:
mutex_lock(&qsee_bw_mutex);
if (!qsee_bw_count) {
- ret = msm_bus_scale_client_update_request(
- qsee_perf_client, 1);
+ if (qsee_sfpb_bw_count > 0)
+ ret = msm_bus_scale_client_update_request(
+ qsee_perf_client, 3);
+ else
+ ret = msm_bus_scale_client_update_request(
+ qsee_perf_client, 1);
if (ret)
pr_err("DFAB Bandwidth req failed (%d)\n",
ret);
else
qsee_bw_count++;
+ } else {
+ qsee_bw_count++;
}
mutex_unlock(&qsee_bw_mutex);
break;
case CLK_SFPB:
- mutex_lock(&qsee_sfpb_bw_mutex);
+ mutex_lock(&qsee_bw_mutex);
if (!qsee_sfpb_bw_count) {
- ret = msm_bus_scale_client_update_request(
- qsee_perf_client, 2);
+ if (qsee_bw_count > 0)
+ ret = msm_bus_scale_client_update_request(
+ qsee_perf_client, 3);
+ else
+ ret = msm_bus_scale_client_update_request(
+ qsee_perf_client, 2);
+
if (ret)
pr_err("SFPB Bandwidth req failed (%d)\n",
ret);
else
qsee_sfpb_bw_count++;
+ } else {
+ qsee_sfpb_bw_count++;
}
- mutex_unlock(&qsee_sfpb_bw_mutex);
+ mutex_unlock(&qsee_bw_mutex);
break;
default:
pr_err("Clock type not defined\n");
@@ -1238,29 +1259,44 @@
switch (clk_type) {
case CLK_DFAB:
mutex_lock(&qsee_bw_mutex);
- if (qsee_bw_count > 0) {
- if (qsee_bw_count-- == 1) {
+ if (qsee_bw_count == 0) {
+ pr_err("Client error.Extra call to disable DFAB clk\n");
+ mutex_unlock(&qsee_bw_mutex);
+ return;
+ }
+
+ if ((qsee_bw_count > 0) && (qsee_bw_count-- == 1)) {
+ if (qsee_sfpb_bw_count > 0)
+ ret = msm_bus_scale_client_update_request(
+ qsee_perf_client, 2);
+ else
ret = msm_bus_scale_client_update_request(
qsee_perf_client, 0);
- if (ret)
- pr_err("SFPB Bandwidth req fail (%d)\n",
+ if (ret)
+ pr_err("SFPB Bandwidth req fail (%d)\n",
ret);
- }
}
mutex_unlock(&qsee_bw_mutex);
break;
case CLK_SFPB:
- mutex_lock(&qsee_sfpb_bw_mutex);
- if (qsee_sfpb_bw_count > 0) {
- if (qsee_sfpb_bw_count-- == 1) {
+ mutex_lock(&qsee_bw_mutex);
+ if (qsee_sfpb_bw_count == 0) {
+ pr_err("Client error.Extra call to disable SFPB clk\n");
+ mutex_unlock(&qsee_bw_mutex);
+ return;
+ }
+ if ((qsee_sfpb_bw_count > 0) && (qsee_sfpb_bw_count-- == 1)) {
+ if (qsee_bw_count > 0)
+ ret = msm_bus_scale_client_update_request(
+ qsee_perf_client, 1);
+ else
ret = msm_bus_scale_client_update_request(
qsee_perf_client, 0);
- if (ret)
- pr_err("SFPB Bandwidth req fail (%d)\n",
+ if (ret)
+ pr_err("SFPB Bandwidth req fail (%d)\n",
ret);
- }
}
- mutex_unlock(&qsee_sfpb_bw_mutex);
+ mutex_unlock(&qsee_bw_mutex);
break;
default:
pr_err("Clock type not defined\n");
@@ -1708,9 +1744,131 @@
.release = qseecom_release
};
+static int __qseecom_init_clk()
+{
+ int rc = 0;
+ struct device *pdev;
+
+ pdev = qseecom.pdev;
+ /* Get CE3 src core clk. */
+ ce_core_src_clk = clk_get(pdev, "core_clk_src");
+ if (!IS_ERR(ce_core_src_clk)) {
+ ce_core_src_clk = ce_core_src_clk;
+
+ /* Set the core src clk @100Mhz */
+ rc = clk_set_rate(ce_core_src_clk, 100000000);
+ if (rc) {
+ clk_put(ce_core_src_clk);
+ pr_err("Unable to set the core src clk @100Mhz.\n");
+ goto err_clk;
+ }
+ } else {
+ pr_warn("Unable to get CE core src clk, set to NULL\n");
+ ce_core_src_clk = NULL;
+ }
+
+ /* Get CE core clk */
+ ce_core_clk = clk_get(pdev, "core_clk");
+ if (IS_ERR(ce_core_clk)) {
+ rc = PTR_ERR(ce_core_clk);
+ pr_err("Unable to get CE core clk\n");
+ if (ce_core_src_clk != NULL)
+ clk_put(ce_core_src_clk);
+ goto err_clk;
+ }
+
+ /* Get CE Interface clk */
+ ce_clk = clk_get(pdev, "iface_clk");
+ if (IS_ERR(ce_clk)) {
+ rc = PTR_ERR(ce_clk);
+ pr_err("Unable to get CE interface clk\n");
+ if (ce_core_src_clk != NULL)
+ clk_put(ce_core_src_clk);
+ clk_put(ce_core_clk);
+ goto err_clk;
+ }
+
+ /* Get CE AXI clk */
+ ce_bus_clk = clk_get(pdev, "bus_clk");
+ if (IS_ERR(ce_bus_clk)) {
+ rc = PTR_ERR(ce_bus_clk);
+ pr_err("Unable to get CE BUS interface clk\n");
+ if (ce_core_src_clk != NULL)
+ clk_put(ce_core_src_clk);
+ clk_put(ce_core_clk);
+ clk_put(ce_clk);
+ goto err_clk;
+ }
+
+ /* Enable CE core clk */
+ rc = clk_prepare_enable(ce_core_clk);
+ if (rc) {
+ pr_err("Unable to enable/prepare CE core clk\n");
+ if (ce_core_src_clk != NULL)
+ clk_put(ce_core_src_clk);
+ clk_put(ce_core_clk);
+ clk_put(ce_clk);
+ goto err_clk;
+ } else {
+ /* Enable CE clk */
+ rc = clk_prepare_enable(ce_clk);
+ if (rc) {
+ pr_err("Unable to enable/prepare CE iface clk\n");
+ clk_disable_unprepare(ce_core_clk);
+ if (ce_core_src_clk != NULL)
+ clk_put(ce_core_src_clk);
+ clk_put(ce_core_clk);
+ clk_put(ce_clk);
+ goto err_clk;
+ } else {
+ /* Enable AXI clk */
+ rc = clk_prepare_enable(ce_bus_clk);
+ if (rc) {
+ pr_err("Unable to enable/prepare CE iface clk\n");
+ clk_disable_unprepare(ce_core_clk);
+ clk_disable_unprepare(ce_clk);
+ if (ce_core_src_clk != NULL)
+ clk_put(ce_core_src_clk);
+ clk_put(ce_core_clk);
+ clk_put(ce_clk);
+ goto err_clk;
+ }
+ }
+ }
+ return rc;
+
+err_clk:
+ if (rc)
+ pr_err("Unable to init CE clks, rc = %d\n", rc);
+ clk_disable_unprepare(ce_clk);
+ clk_disable_unprepare(ce_core_clk);
+ clk_disable_unprepare(ce_bus_clk);
+ if (ce_core_src_clk != NULL)
+ clk_put(ce_core_src_clk);
+ clk_put(ce_clk);
+ clk_put(ce_core_clk);
+ clk_put(ce_bus_clk);
+ return rc;
+}
+
+
+
+static void __qseecom_disable_clk()
+{
+ clk_disable_unprepare(ce_clk);
+ clk_disable_unprepare(ce_core_clk);
+ clk_disable_unprepare(ce_bus_clk);
+ if (ce_core_src_clk != NULL)
+ clk_put(ce_core_src_clk);
+ clk_put(ce_clk);
+ clk_put(ce_core_clk);
+ clk_put(ce_bus_clk);
+}
+
static int __devinit qseecom_probe(struct platform_device *pdev)
{
int rc;
+ int ret;
struct device *class_dev;
char qsee_not_legacy = 0;
struct msm_bus_scale_pdata *qseecom_platform_support;
@@ -1769,6 +1927,8 @@
pil = NULL;
pil_ref_cnt = 0;
}
+
+ qseecom.pdev = class_dev;
/* Create ION msm client */
qseecom.ion_clnt = msm_ion_client_create(0x03, "qseecom-kernel");
if (qseecom.ion_clnt == NULL) {
@@ -1778,17 +1938,23 @@
}
/* register client for bus scaling */
- if (!pdev->dev.of_node) {
+ if (pdev->dev.of_node) {
+ ret = __qseecom_init_clk();
+ if (ret)
+ goto err;
+ qseecom_platform_support = (struct msm_bus_scale_pdata *)
+ msm_bus_cl_get_pdata(pdev);
+ } else {
qseecom_platform_support = (struct msm_bus_scale_pdata *)
pdev->dev.platform_data;
- qsee_perf_client = msm_bus_scale_register_client(
- qseecom_platform_support);
-
- if (!qsee_perf_client)
- pr_err("Unable to register bus client\n");
}
- return 0;
+ qsee_perf_client = msm_bus_scale_register_client(
+ qseecom_platform_support);
+
+ if (!qsee_perf_client)
+ pr_err("Unable to register bus client\n");
+ return 0;
err:
device_destroy(driver_class, qseecom_device_no);
class_destroy:
@@ -1829,6 +1995,9 @@
static void __devexit qseecom_exit(void)
{
+
+ __qseecom_disable_clk();
+
device_destroy(driver_class, qseecom_device_no);
class_destroy(driver_class);
unregister_chrdev_region(qseecom_device_no, 1);
diff --git a/drivers/misc/tsif.c b/drivers/misc/tsif.c
index 1ff4468..b7b1203 100644
--- a/drivers/misc/tsif.c
+++ b/drivers/misc/tsif.c
@@ -140,6 +140,10 @@
unsigned int irq;
int mode;
u32 time_limit;
+ int clock_inverse;
+ int data_inverse;
+ int sync_inverse;
+ int enable_inverse;
enum tsif_state state;
struct wake_lock wake_lock;
/* clocks */
@@ -358,6 +362,19 @@
TSIF_STS_CTL_EN_TIME_LIM |
TSIF_STS_CTL_EN_TCR |
TSIF_STS_CTL_EN_DM;
+
+ if (tsif_device->clock_inverse)
+ ctl |= TSIF_STS_CTL_INV_CLOCK;
+
+ if (tsif_device->data_inverse)
+ ctl |= TSIF_STS_CTL_INV_DATA;
+
+ if (tsif_device->sync_inverse)
+ ctl |= TSIF_STS_CTL_INV_SYNC;
+
+ if (tsif_device->enable_inverse)
+ ctl |= TSIF_STS_CTL_INV_ENABLE;
+
dev_info(&tsif_device->pdev->dev, "%s\n", __func__);
switch (tsif_device->mode) {
case 1: /* mode 1 */
@@ -805,6 +822,10 @@
"Client = %p\n"
"Pkt/Buf = %d\n"
"Pkt/chunk = %d\n"
+ "Clock inv = %d\n"
+ "Data inv = %d\n"
+ "Sync inv = %d\n"
+ "Enable inv = %d\n"
"--statistics--\n"
"Rx chunks = %d\n"
"Overflow = %d\n"
@@ -827,6 +848,10 @@
tsif_device->client_data,
TSIF_PKTS_IN_BUF,
TSIF_PKTS_IN_CHUNK,
+ tsif_device->clock_inverse,
+ tsif_device->data_inverse,
+ tsif_device->sync_inverse,
+ tsif_device->enable_inverse,
tsif_device->stat_rx,
tsif_device->stat_overflow,
tsif_device->stat_lost_sync,
@@ -950,11 +975,120 @@
static DEVICE_ATTR(buf_config, S_IRUGO | S_IWUSR,
show_buf_config, set_buf_config);
+static ssize_t show_clk_inverse(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+ return snprintf(buf, PAGE_SIZE, "%d\n", tsif_device->clock_inverse);
+}
+
+static ssize_t set_clk_inverse(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+ int value;
+ int rc;
+ if (1 != sscanf(buf, "%d", &value)) {
+ dev_err(&tsif_device->pdev->dev,
+ "Failed to parse integer: <%s>\n", buf);
+ return -EINVAL;
+ }
+ rc = tsif_set_clk_inverse(tsif_device, value);
+ if (!rc)
+ rc = count;
+ return rc;
+}
+static DEVICE_ATTR(clk_inverse, S_IRUGO | S_IWUSR,
+ show_clk_inverse, set_clk_inverse);
+
+static ssize_t show_data_inverse(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+ return snprintf(buf, PAGE_SIZE, "%d\n", tsif_device->data_inverse);
+}
+
+static ssize_t set_data_inverse(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+ int value;
+ int rc;
+ if (1 != sscanf(buf, "%d", &value)) {
+ dev_err(&tsif_device->pdev->dev,
+ "Failed to parse integer: <%s>\n", buf);
+ return -EINVAL;
+ }
+ rc = tsif_set_data_inverse(tsif_device, value);
+ if (!rc)
+ rc = count;
+ return rc;
+}
+static DEVICE_ATTR(data_inverse, S_IRUGO | S_IWUSR,
+ show_data_inverse, set_data_inverse);
+
+static ssize_t show_sync_inverse(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+ return snprintf(buf, PAGE_SIZE, "%d\n", tsif_device->sync_inverse);
+}
+
+static ssize_t set_sync_inverse(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+ int value;
+ int rc;
+ if (1 != sscanf(buf, "%d", &value)) {
+ dev_err(&tsif_device->pdev->dev,
+ "Failed to parse integer: <%s>\n", buf);
+ return -EINVAL;
+ }
+ rc = tsif_set_sync_inverse(tsif_device, value);
+ if (!rc)
+ rc = count;
+ return rc;
+}
+static DEVICE_ATTR(sync_inverse, S_IRUGO | S_IWUSR,
+ show_sync_inverse, set_sync_inverse);
+
+static ssize_t show_enable_inverse(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+ return snprintf(buf, PAGE_SIZE, "%d\n", tsif_device->enable_inverse);
+}
+
+static ssize_t set_enable_inverse(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+ int value;
+ int rc;
+ if (1 != sscanf(buf, "%d", &value)) {
+ dev_err(&tsif_device->pdev->dev,
+ "Failed to parse integer: <%s>\n", buf);
+ return -EINVAL;
+ }
+ rc = tsif_set_enable_inverse(tsif_device, value);
+ if (!rc)
+ rc = count;
+ return rc;
+}
+static DEVICE_ATTR(enable_inverse, S_IRUGO | S_IWUSR,
+ show_enable_inverse, set_enable_inverse);
+
+
static struct attribute *dev_attrs[] = {
&dev_attr_stats.attr,
&dev_attr_mode.attr,
&dev_attr_time_limit.attr,
&dev_attr_buf_config.attr,
+ &dev_attr_clk_inverse.attr,
+ &dev_attr_data_inverse.attr,
+ &dev_attr_sync_inverse.attr,
+ &dev_attr_enable_inverse.attr,
NULL,
};
static struct attribute_group dev_attr_grp = {
@@ -1287,6 +1421,10 @@
tsif_device->pdev = pdev;
platform_set_drvdata(pdev, tsif_device);
tsif_device->mode = 1;
+ tsif_device->clock_inverse = 0;
+ tsif_device->data_inverse = 0;
+ tsif_device->sync_inverse = 0;
+ tsif_device->enable_inverse = 0;
tsif_device->pkts_per_chunk = TSIF_PKTS_IN_CHUNK_DEFAULT;
tsif_device->chunks_per_buf = TSIF_CHUNKS_IN_BUF_DEFAULT;
tasklet_init(&tsif_device->dma_refill, tsif_dma_refill,
@@ -1534,6 +1672,78 @@
}
EXPORT_SYMBOL(tsif_set_buf_config);
+int tsif_set_clk_inverse(void *cookie, int value)
+{
+ struct msm_tsif_device *tsif_device = cookie;
+ if (tsif_device->state != tsif_state_stopped) {
+ dev_err(&tsif_device->pdev->dev,
+ "Can't change clock inverse while device is active\n");
+ return -EBUSY;
+ }
+ if ((value != 0) && (value != 1)) {
+ dev_err(&tsif_device->pdev->dev,
+ "Invalid parameter, either 0 or 1: %#x\n", value);
+ return -EINVAL;
+ }
+ tsif_device->clock_inverse = value;
+ return 0;
+}
+EXPORT_SYMBOL(tsif_set_clk_inverse);
+
+int tsif_set_data_inverse(void *cookie, int value)
+{
+ struct msm_tsif_device *tsif_device = cookie;
+ if (tsif_device->state != tsif_state_stopped) {
+ dev_err(&tsif_device->pdev->dev,
+ "Can't change data inverse while device is active\n");
+ return -EBUSY;
+ }
+ if ((value != 0) && (value != 1)) {
+ dev_err(&tsif_device->pdev->dev,
+ "Invalid parameter, either 0 or 1: %#x\n", value);
+ return -EINVAL;
+ }
+ tsif_device->data_inverse = value;
+ return 0;
+}
+EXPORT_SYMBOL(tsif_set_data_inverse);
+
+int tsif_set_sync_inverse(void *cookie, int value)
+{
+ struct msm_tsif_device *tsif_device = cookie;
+ if (tsif_device->state != tsif_state_stopped) {
+ dev_err(&tsif_device->pdev->dev,
+ "Can't change sync inverse while device is active\n");
+ return -EBUSY;
+ }
+ if ((value != 0) && (value != 1)) {
+ dev_err(&tsif_device->pdev->dev,
+ "Invalid parameter, either 0 or 1: %#x\n", value);
+ return -EINVAL;
+ }
+ tsif_device->sync_inverse = value;
+ return 0;
+}
+EXPORT_SYMBOL(tsif_set_sync_inverse);
+
+int tsif_set_enable_inverse(void *cookie, int value)
+{
+ struct msm_tsif_device *tsif_device = cookie;
+ if (tsif_device->state != tsif_state_stopped) {
+ dev_err(&tsif_device->pdev->dev,
+ "Can't change enable inverse while device is active\n");
+ return -EBUSY;
+ }
+ if ((value != 0) && (value != 1)) {
+ dev_err(&tsif_device->pdev->dev,
+ "Invalid parameter, either 0 or 1: %#x\n", value);
+ return -EINVAL;
+ }
+ tsif_device->enable_inverse = value;
+ return 0;
+}
+EXPORT_SYMBOL(tsif_set_enable_inverse);
+
void tsif_get_state(void *cookie, int *ri, int *wi, enum tsif_state *state)
{
struct msm_tsif_device *tsif_device = cookie;
diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig
index ebb4afe..33f0600 100644
--- a/drivers/mmc/card/Kconfig
+++ b/drivers/mmc/card/Kconfig
@@ -76,3 +76,13 @@
This driver is only of interest to those developing or
testing a host driver. Most people should say N here.
+
+config MMC_BLOCK_TEST
+ tristate "MMC block test"
+ depends on MMC_BLOCK && IOSCHED_TEST
+ help
+ MMC block test can be used with test iosched to test the MMC block
+ device.
+ Currently used to test eMMC 4.5 features (packed commands, sanitize,
+ BKOPs).
+
diff --git a/drivers/mmc/card/Makefile b/drivers/mmc/card/Makefile
index c73b406..d55107f 100644
--- a/drivers/mmc/card/Makefile
+++ b/drivers/mmc/card/Makefile
@@ -8,3 +8,4 @@
obj-$(CONFIG_SDIO_UART) += sdio_uart.o
+obj-$(CONFIG_MMC_BLOCK_TEST) += mmc_block_test.o
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index a9f1b53..254672f 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -65,6 +65,11 @@
(rq_data_dir(req) == WRITE))
#define PACKED_CMD_VER 0x01
#define PACKED_CMD_WR 0x02
+#define MMC_BLK_UPDATE_STOP_REASON(stats, reason) \
+ do { \
+ if (stats->enabled) \
+ stats->pack_stop_reason[reason]++; \
+ } while (0)
static DEFINE_MUTEX(block_mutex);
@@ -115,22 +120,13 @@
unsigned int part_curr;
struct device_attribute force_ro;
struct device_attribute power_ro_lock;
+ struct device_attribute num_wr_reqs_to_start_packing;
+ struct device_attribute min_sectors_to_check_bkops_status;
int area_type;
};
static DEFINE_MUTEX(open_lock);
-enum mmc_blk_status {
- MMC_BLK_SUCCESS = 0,
- MMC_BLK_PARTIAL,
- MMC_BLK_CMD_ERR,
- MMC_BLK_RETRY,
- MMC_BLK_ABORT,
- MMC_BLK_DATA_ERR,
- MMC_BLK_ECC_ERR,
- MMC_BLK_NOMEDIUM,
-};
-
enum {
MMC_PACKED_N_IDX = -1,
MMC_PACKED_N_ZERO,
@@ -279,6 +275,80 @@
return ret;
}
+static ssize_t
+num_wr_reqs_to_start_packing_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+ int num_wr_reqs_to_start_packing;
+ int ret;
+
+ num_wr_reqs_to_start_packing = md->queue.num_wr_reqs_to_start_packing;
+
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", num_wr_reqs_to_start_packing);
+
+ mmc_blk_put(md);
+ return ret;
+}
+
+static ssize_t
+num_wr_reqs_to_start_packing_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int value;
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+
+ sscanf(buf, "%d", &value);
+ if (value >= 0)
+ md->queue.num_wr_reqs_to_start_packing = value;
+
+ mmc_blk_put(md);
+ return count;
+}
+
+static ssize_t
+min_sectors_to_check_bkops_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+ unsigned int min_sectors_to_check_bkops_status;
+ struct mmc_card *card = md->queue.card;
+ int ret;
+
+ if (!card)
+ return -EINVAL;
+
+ min_sectors_to_check_bkops_status =
+ card->bkops_info.min_sectors_to_queue_delayed_work;
+
+ ret = snprintf(buf, PAGE_SIZE, "%d\n",
+ min_sectors_to_check_bkops_status);
+
+ mmc_blk_put(md);
+ return ret;
+}
+
+static ssize_t
+min_sectors_to_check_bkops_status_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int value;
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+ struct mmc_card *card = md->queue.card;
+
+ if (!card)
+ return -EINVAL;
+
+ sscanf(buf, "%d", &value);
+ if (value >= 0)
+ card->bkops_info.min_sectors_to_queue_delayed_work = value;
+
+ mmc_blk_put(md);
+ return count;
+}
+
static int mmc_blk_open(struct block_device *bdev, fmode_t mode)
{
struct mmc_blk_data *md = mmc_blk_get(bdev->bd_disk);
@@ -846,6 +916,9 @@
from = blk_rq_pos(req);
nr = blk_rq_sectors(req);
+ if (card->ext_csd.bkops_en)
+ card->bkops_info.sectors_changed += blk_rq_sectors(req);
+
if (mmc_can_discard(card))
arg = MMC_DISCARD_ARG;
else if (mmc_can_trim(card))
@@ -1319,11 +1392,144 @@
}
mqrq->mmc_active.mrq = &brq->mrq;
- mqrq->mmc_active.err_check = mmc_blk_err_check;
+ if (mq->err_check_fn)
+ mqrq->mmc_active.err_check = mq->err_check_fn;
+ else
+ mqrq->mmc_active.err_check = mmc_blk_err_check;
mmc_queue_bounce_pre(mqrq);
}
+static void mmc_blk_write_packing_control(struct mmc_queue *mq,
+ struct request *req)
+{
+ struct mmc_host *host = mq->card->host;
+ int data_dir;
+
+ if (!(host->caps2 & MMC_CAP2_PACKED_WR))
+ return;
+
+ /*
+ * In case the packing control is not supported by the host, it should
+ * not have an effect on the write packing. Therefore we have to enable
+ * the write packing
+ */
+ if (!(host->caps2 & MMC_CAP2_PACKED_WR_CONTROL)) {
+ mq->wr_packing_enabled = true;
+ return;
+ }
+
+ if (!req || (req && (req->cmd_flags & REQ_FLUSH))) {
+ if (mq->num_of_potential_packed_wr_reqs >
+ mq->num_wr_reqs_to_start_packing)
+ mq->wr_packing_enabled = true;
+ mq->num_of_potential_packed_wr_reqs = 0;
+ return;
+ }
+
+ data_dir = rq_data_dir(req);
+
+ if (data_dir == READ) {
+ mq->num_of_potential_packed_wr_reqs = 0;
+ mq->wr_packing_enabled = false;
+ return;
+ } else if (data_dir == WRITE) {
+ mq->num_of_potential_packed_wr_reqs++;
+ }
+
+ if (mq->num_of_potential_packed_wr_reqs >
+ mq->num_wr_reqs_to_start_packing)
+ mq->wr_packing_enabled = true;
+
+}
+
+struct mmc_wr_pack_stats *mmc_blk_get_packed_statistics(struct mmc_card *card)
+{
+ if (!card)
+ return NULL;
+
+ return &card->wr_pack_stats;
+}
+EXPORT_SYMBOL(mmc_blk_get_packed_statistics);
+
+void mmc_blk_init_packed_statistics(struct mmc_card *card)
+{
+ int max_num_of_packed_reqs = 0;
+
+ if (!card || !card->wr_pack_stats.packing_events)
+ return;
+
+ max_num_of_packed_reqs = card->ext_csd.max_packed_writes;
+
+ spin_lock(&card->wr_pack_stats.lock);
+ memset(card->wr_pack_stats.packing_events, 0,
+ (max_num_of_packed_reqs + 1) *
+ sizeof(*card->wr_pack_stats.packing_events));
+ memset(&card->wr_pack_stats.pack_stop_reason, 0,
+ sizeof(card->wr_pack_stats.pack_stop_reason));
+ card->wr_pack_stats.enabled = true;
+ spin_unlock(&card->wr_pack_stats.lock);
+}
+EXPORT_SYMBOL(mmc_blk_init_packed_statistics);
+
+void print_mmc_packing_stats(struct mmc_card *card)
+{
+ int i;
+ int max_num_of_packed_reqs = 0;
+
+ if ((!card) || (!card->wr_pack_stats.packing_events))
+ return;
+
+ max_num_of_packed_reqs = card->ext_csd.max_packed_writes;
+
+ spin_lock(&card->wr_pack_stats.lock);
+
+ pr_info("%s: write packing statistics:\n",
+ mmc_hostname(card->host));
+
+ for (i = 1 ; i <= max_num_of_packed_reqs ; ++i) {
+ if (card->wr_pack_stats.packing_events[i] != 0)
+ pr_info("%s: Packed %d reqs - %d times\n",
+ mmc_hostname(card->host), i,
+ card->wr_pack_stats.packing_events[i]);
+ }
+
+ pr_info("%s: stopped packing due to the following reasons:\n",
+ mmc_hostname(card->host));
+
+ if (card->wr_pack_stats.pack_stop_reason[EXCEEDS_SEGMENTS])
+ pr_info("%s: %d times: exceedmax num of segments\n",
+ mmc_hostname(card->host),
+ card->wr_pack_stats.pack_stop_reason[EXCEEDS_SEGMENTS]);
+ if (card->wr_pack_stats.pack_stop_reason[EXCEEDS_SECTORS])
+ pr_info("%s: %d times: exceeding the max num of sectors\n",
+ mmc_hostname(card->host),
+ card->wr_pack_stats.pack_stop_reason[EXCEEDS_SECTORS]);
+ if (card->wr_pack_stats.pack_stop_reason[WRONG_DATA_DIR])
+ pr_info("%s: %d times: wrong data direction\n",
+ mmc_hostname(card->host),
+ card->wr_pack_stats.pack_stop_reason[WRONG_DATA_DIR]);
+ if (card->wr_pack_stats.pack_stop_reason[FLUSH_OR_DISCARD])
+ pr_info("%s: %d times: flush or discard\n",
+ mmc_hostname(card->host),
+ card->wr_pack_stats.pack_stop_reason[FLUSH_OR_DISCARD]);
+ if (card->wr_pack_stats.pack_stop_reason[EMPTY_QUEUE])
+ pr_info("%s: %d times: empty queue\n",
+ mmc_hostname(card->host),
+ card->wr_pack_stats.pack_stop_reason[EMPTY_QUEUE]);
+ if (card->wr_pack_stats.pack_stop_reason[REL_WRITE])
+ pr_info("%s: %d times: rel write\n",
+ mmc_hostname(card->host),
+ card->wr_pack_stats.pack_stop_reason[REL_WRITE]);
+ if (card->wr_pack_stats.pack_stop_reason[THRESHOLD])
+ pr_info("%s: %d times: Threshold\n",
+ mmc_hostname(card->host),
+ card->wr_pack_stats.pack_stop_reason[THRESHOLD]);
+
+ spin_unlock(&card->wr_pack_stats.lock);
+}
+EXPORT_SYMBOL(print_mmc_packing_stats);
+
static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
{
struct request_queue *q = mq->queue;
@@ -1336,6 +1542,7 @@
u8 put_back = 0;
u8 max_packed_rw = 0;
u8 reqs = 0;
+ struct mmc_wr_pack_stats *stats = &card->wr_pack_stats;
mmc_blk_clear_packed(mq->mqrq_cur);
@@ -1343,6 +1550,9 @@
!card->ext_csd.packed_event_en)
goto no_packed;
+ if (!mq->wr_packing_enabled)
+ goto no_packed;
+
if ((rq_data_dir(cur) == WRITE) &&
(card->host->caps2 & MMC_CAP2_PACKED_WR))
max_packed_rw = card->ext_csd.max_packed_writes;
@@ -1373,26 +1583,33 @@
phys_segments++;
}
+ spin_lock(&stats->lock);
+
while (reqs < max_packed_rw - 1) {
spin_lock_irq(q->queue_lock);
next = blk_fetch_request(q);
spin_unlock_irq(q->queue_lock);
- if (!next)
+ if (!next) {
+ MMC_BLK_UPDATE_STOP_REASON(stats, EMPTY_QUEUE);
break;
+ }
if (mmc_large_sec(card) &&
!IS_ALIGNED(blk_rq_sectors(next), 8)) {
+ MMC_BLK_UPDATE_STOP_REASON(stats, LARGE_SEC_ALIGN);
put_back = 1;
break;
}
if (next->cmd_flags & REQ_DISCARD ||
next->cmd_flags & REQ_FLUSH) {
+ MMC_BLK_UPDATE_STOP_REASON(stats, FLUSH_OR_DISCARD);
put_back = 1;
break;
}
if (rq_data_dir(cur) != rq_data_dir(next)) {
+ MMC_BLK_UPDATE_STOP_REASON(stats, WRONG_DATA_DIR);
put_back = 1;
break;
}
@@ -1400,22 +1617,32 @@
if (mmc_req_rel_wr(next) &&
(md->flags & MMC_BLK_REL_WR) &&
!en_rel_wr) {
+ MMC_BLK_UPDATE_STOP_REASON(stats, REL_WRITE);
put_back = 1;
break;
}
req_sectors += blk_rq_sectors(next);
if (req_sectors > max_blk_count) {
+ if (stats->enabled)
+ stats->pack_stop_reason[EXCEEDS_SECTORS]++;
put_back = 1;
break;
}
phys_segments += next->nr_phys_segments;
if (phys_segments > max_phys_segs) {
+ MMC_BLK_UPDATE_STOP_REASON(stats, EXCEEDS_SEGMENTS);
put_back = 1;
break;
}
+ if (rq_data_dir(next) == WRITE) {
+ mq->num_of_potential_packed_wr_reqs++;
+ if (card->ext_csd.bkops_en)
+ card->bkops_info.sectors_changed +=
+ blk_rq_sectors(next);
+ }
list_add_tail(&next->queuelist, &mq->mqrq_cur->packed_list);
cur = next;
reqs++;
@@ -1427,6 +1654,15 @@
spin_unlock_irq(q->queue_lock);
}
+ if (stats->enabled) {
+ if (reqs + 1 <= card->ext_csd.max_packed_writes)
+ stats->packing_events[reqs + 1]++;
+ if (reqs + 1 == max_packed_rw)
+ MMC_BLK_UPDATE_STOP_REASON(stats, THRESHOLD);
+ }
+
+ spin_unlock(&stats->lock);
+
if (reqs > 0) {
list_add(&req->queuelist, &mq->mqrq_cur->packed_list);
mq->mqrq_cur->packed_num = ++reqs;
@@ -1512,7 +1748,18 @@
brq->data.sg_len = mmc_queue_map_sg(mq, mqrq);
mqrq->mmc_active.mrq = &brq->mrq;
- mqrq->mmc_active.err_check = mmc_blk_packed_err_check;
+
+ /*
+ * This is intended for packed commands tests usage - in case these
+ * functions are not in use the respective pointers are NULL
+ */
+ if (mq->err_check_fn)
+ mqrq->mmc_active.err_check = mq->err_check_fn;
+ else
+ mqrq->mmc_active.err_check = mmc_blk_packed_err_check;
+
+ if (mq->packed_test_fn)
+ mq->packed_test_fn(mq->queue, mqrq);
mmc_queue_bounce_pre(mqrq);
}
@@ -1625,8 +1872,11 @@
if (!rqc && !mq->mqrq_prev->req)
return 0;
- if (rqc)
+ if (rqc) {
+ if ((card->ext_csd.bkops_en) && (rq_data_dir(rqc) == WRITE))
+ card->bkops_info.sectors_changed += blk_rq_sectors(rqc);
reqs = mmc_blk_prep_packed_list(mq, rqc);
+ }
do {
if (rqc) {
@@ -1797,6 +2047,8 @@
goto out;
}
+ mmc_blk_write_packing_control(mq, req);
+
if (req && req->cmd_flags & REQ_SANITIZE) {
/* complete ongoing async transfer before issuing sanitize */
if (card->host && card->host->areq)
@@ -2028,6 +2280,8 @@
if (md) {
card = md->queue.card;
+ device_remove_file(disk_to_dev(md->disk),
+ &md->num_wr_reqs_to_start_packing);
if (md->disk->flags & GENHD_FL_UP) {
device_remove_file(disk_to_dev(md->disk), &md->force_ro);
if ((md->area_type & MMC_BLK_DATA_AREA_BOOT) &&
@@ -2095,6 +2349,29 @@
goto power_ro_lock_fail;
}
+ md->num_wr_reqs_to_start_packing.show =
+ num_wr_reqs_to_start_packing_show;
+ md->num_wr_reqs_to_start_packing.store =
+ num_wr_reqs_to_start_packing_store;
+ sysfs_attr_init(&md->num_wr_reqs_to_start_packing.attr);
+ md->num_wr_reqs_to_start_packing.attr.name =
+ "num_wr_reqs_to_start_packing";
+ md->num_wr_reqs_to_start_packing.attr.mode = S_IRUGO | S_IWUSR;
+ ret = device_create_file(disk_to_dev(md->disk),
+ &md->num_wr_reqs_to_start_packing);
+ if (ret)
+ goto power_ro_lock_fail;
+
+ md->min_sectors_to_check_bkops_status.show =
+ min_sectors_to_check_bkops_status_show;
+ md->min_sectors_to_check_bkops_status.store =
+ min_sectors_to_check_bkops_status_store;
+ sysfs_attr_init(&md->min_sectors_to_check_bkops_status.attr);
+ md->min_sectors_to_check_bkops_status.attr.name =
+ "min_sectors_to_check_bkops_status";
+ md->min_sectors_to_check_bkops_status.attr.mode = S_IRUGO | S_IWUSR;
+ ret = device_create_file(disk_to_dev(md->disk),
+ &md->min_sectors_to_check_bkops_status);
if (ret)
goto power_ro_lock_fail;
diff --git a/drivers/mmc/card/mmc_block_test.c b/drivers/mmc/card/mmc_block_test.c
new file mode 100644
index 0000000..7d3ac83
--- /dev/null
+++ b/drivers/mmc/card/mmc_block_test.c
@@ -0,0 +1,2590 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/* MMC block test */
+
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/debugfs.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/delay.h>
+#include <linux/test-iosched.h>
+#include "queue.h"
+#include <linux/mmc/mmc.h>
+
+#define MODULE_NAME "mmc_block_test"
+#define TEST_MAX_SECTOR_RANGE (600*1024*1024) /* 600 MB */
+#define TEST_MAX_BIOS_PER_REQ 120
+#define CMD23_PACKED_BIT (1 << 30)
+#define LARGE_PRIME_1 1103515367
+#define LARGE_PRIME_2 35757
+#define PACKED_HDR_VER_MASK 0x000000FF
+#define PACKED_HDR_RW_MASK 0x0000FF00
+#define PACKED_HDR_NUM_REQS_MASK 0x00FF0000
+#define PACKED_HDR_BITS_16_TO_29_SET 0x3FFF0000
+#define SECTOR_SIZE 512
+#define NUM_OF_SECTORS_PER_BIO ((BIO_U32_SIZE * 4) / SECTOR_SIZE)
+#define BIO_TO_SECTOR(x) (x * NUM_OF_SECTORS_PER_BIO)
+
+#define test_pr_debug(fmt, args...) pr_debug("%s: "fmt"\n", MODULE_NAME, args)
+#define test_pr_info(fmt, args...) pr_info("%s: "fmt"\n", MODULE_NAME, args)
+#define test_pr_err(fmt, args...) pr_err("%s: "fmt"\n", MODULE_NAME, args)
+
+#define SANITIZE_TEST_TIMEOUT 240000
+#define TEST_REQUEST_NUM_OF_BIOS 3
+
+
+#define CHECK_BKOPS_STATS(stats, exp_bkops, exp_hpi, exp_suspend) \
+ ((stats.bkops != exp_bkops) || \
+ (stats.hpi != exp_hpi) || \
+ (stats.suspend != exp_suspend))
+#define BKOPS_TEST_TIMEOUT 60000
+
+enum is_random {
+ NON_RANDOM_TEST,
+ RANDOM_TEST,
+};
+
+enum mmc_block_test_testcases {
+ /* Start of send write packing test group */
+ SEND_WRITE_PACKING_MIN_TESTCASE,
+ TEST_STOP_DUE_TO_READ = SEND_WRITE_PACKING_MIN_TESTCASE,
+ TEST_STOP_DUE_TO_READ_AFTER_MAX_REQS,
+ TEST_STOP_DUE_TO_FLUSH,
+ TEST_STOP_DUE_TO_FLUSH_AFTER_MAX_REQS,
+ TEST_STOP_DUE_TO_EMPTY_QUEUE,
+ TEST_STOP_DUE_TO_MAX_REQ_NUM,
+ TEST_STOP_DUE_TO_THRESHOLD,
+ SEND_WRITE_PACKING_MAX_TESTCASE = TEST_STOP_DUE_TO_THRESHOLD,
+
+ /* Start of err check test group */
+ ERR_CHECK_MIN_TESTCASE,
+ TEST_RET_ABORT = ERR_CHECK_MIN_TESTCASE,
+ TEST_RET_PARTIAL_FOLLOWED_BY_SUCCESS,
+ TEST_RET_PARTIAL_FOLLOWED_BY_ABORT,
+ TEST_RET_PARTIAL_MULTIPLE_UNTIL_SUCCESS,
+ TEST_RET_PARTIAL_MAX_FAIL_IDX,
+ TEST_RET_RETRY,
+ TEST_RET_CMD_ERR,
+ TEST_RET_DATA_ERR,
+ ERR_CHECK_MAX_TESTCASE = TEST_RET_DATA_ERR,
+
+ /* Start of send invalid test group */
+ INVALID_CMD_MIN_TESTCASE,
+ TEST_HDR_INVALID_VERSION = INVALID_CMD_MIN_TESTCASE,
+ TEST_HDR_WRONG_WRITE_CODE,
+ TEST_HDR_INVALID_RW_CODE,
+ TEST_HDR_DIFFERENT_ADDRESSES,
+ TEST_HDR_REQ_NUM_SMALLER_THAN_ACTUAL,
+ TEST_HDR_REQ_NUM_LARGER_THAN_ACTUAL,
+ TEST_HDR_CMD23_PACKED_BIT_SET,
+ TEST_CMD23_MAX_PACKED_WRITES,
+ TEST_CMD23_ZERO_PACKED_WRITES,
+ TEST_CMD23_PACKED_BIT_UNSET,
+ TEST_CMD23_REL_WR_BIT_SET,
+ TEST_CMD23_BITS_16TO29_SET,
+ TEST_CMD23_HDR_BLK_NOT_IN_COUNT,
+ INVALID_CMD_MAX_TESTCASE = TEST_CMD23_HDR_BLK_NOT_IN_COUNT,
+
+ /*
+ * Start of packing control test group.
+ * in these next testcases the abbreviation FB = followed by
+ */
+ PACKING_CONTROL_MIN_TESTCASE,
+ TEST_PACKING_EXP_ONE_OVER_TRIGGER_FB_READ =
+ PACKING_CONTROL_MIN_TESTCASE,
+ TEST_PACKING_EXP_N_OVER_TRIGGER,
+ TEST_PACKING_EXP_N_OVER_TRIGGER_FB_READ,
+ TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N,
+ TEST_PACKING_EXP_THRESHOLD_OVER_TRIGGER,
+ TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS,
+ TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS,
+ TEST_PACKING_NOT_EXP_TRIGGER_READ_TRIGGER,
+ TEST_PACKING_NOT_EXP_TRIGGER_FLUSH_TRIGGER,
+ TEST_PACK_MIX_PACKED_NO_PACKED_PACKED,
+ TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED,
+ PACKING_CONTROL_MAX_TESTCASE = TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED,
+
+ TEST_WRITE_DISCARD_SANITIZE_READ,
+
+ /* Start of bkops test group */
+ BKOPS_MIN_TESTCASE,
+ BKOPS_DELAYED_WORK_LEVEL_1 = BKOPS_MIN_TESTCASE,
+ BKOPS_DELAYED_WORK_LEVEL_1_HPI,
+ BKOPS_CANCEL_DELAYED_WORK,
+ BKOPS_URGENT_LEVEL_2,
+ BKOPS_URGENT_LEVEL_2_TWO_REQS,
+ BKOPS_URGENT_LEVEL_3,
+ BKOPS_MAX_TESTCASE = BKOPS_URGENT_LEVEL_3,
+};
+
+enum mmc_block_test_group {
+ TEST_NO_GROUP,
+ TEST_GENERAL_GROUP,
+ TEST_SEND_WRITE_PACKING_GROUP,
+ TEST_ERR_CHECK_GROUP,
+ TEST_SEND_INVALID_GROUP,
+ TEST_PACKING_CONTROL_GROUP,
+ TEST_BKOPS_GROUP,
+};
+
+enum bkops_test_stages {
+ BKOPS_STAGE_1,
+ BKOPS_STAGE_2,
+ BKOPS_STAGE_3,
+ BKOPS_STAGE_4,
+};
+
+struct mmc_block_test_debug {
+ struct dentry *send_write_packing_test;
+ struct dentry *err_check_test;
+ struct dentry *send_invalid_packed_test;
+ struct dentry *random_test_seed;
+ struct dentry *packing_control_test;
+ struct dentry *discard_sanitize_test;
+ struct dentry *bkops_test;
+};
+
+struct mmc_block_test_data {
+ /* The number of write requests that the test will issue */
+ int num_requests;
+ /* The expected write packing statistics for the current test */
+ struct mmc_wr_pack_stats exp_packed_stats;
+ /*
+ * A user-defined seed for random choices of number of bios written in
+ * a request, and of number of requests issued in a test
+ * This field is randomly updated after each use
+ */
+ unsigned int random_test_seed;
+ /* A retry counter used in err_check tests */
+ int err_check_counter;
+ /* Can be one of the values of enum test_group */
+ enum mmc_block_test_group test_group;
+ /*
+ * Indicates if the current testcase is running with random values of
+ * num_requests and num_bios (in each request)
+ */
+ int is_random;
+ /* Data structure for debugfs dentrys */
+ struct mmc_block_test_debug debug;
+ /*
+ * Data structure containing individual test information, including
+ * self-defined specific data
+ */
+ struct test_info test_info;
+ /* mmc block device test */
+ struct blk_dev_test_type bdt;
+ /* Current BKOPs test stage */
+ enum bkops_test_stages bkops_stage;
+ /* A wait queue for BKOPs tests */
+ wait_queue_head_t bkops_wait_q;
+};
+
+static struct mmc_block_test_data *mbtd;
+
+/*
+ * A callback assigned to the packed_test_fn field.
+ * Called from block layer in mmc_blk_packed_hdr_wrq_prep.
+ * Here we alter the packed header or CMD23 in order to send an invalid
+ * packed command to the card.
+ */
+static void test_invalid_packed_cmd(struct request_queue *q,
+ struct mmc_queue_req *mqrq)
+{
+ struct mmc_queue *mq = q->queuedata;
+ u32 *packed_cmd_hdr = mqrq->packed_cmd_hdr;
+ struct request *req = mqrq->req;
+ struct request *second_rq;
+ struct test_request *test_rq;
+ struct mmc_blk_request *brq = &mqrq->brq;
+ int num_requests;
+ int max_packed_reqs;
+
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return;
+ }
+
+ test_rq = (struct test_request *)req->elv.priv[0];
+ if (!test_rq) {
+ test_pr_err("%s: NULL test_rq", __func__);
+ return;
+ }
+ max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+
+ switch (mbtd->test_info.testcase) {
+ case TEST_HDR_INVALID_VERSION:
+ test_pr_info("%s: set invalid header version", __func__);
+ /* Put 0 in header version field (1 byte, offset 0 in header) */
+ packed_cmd_hdr[0] = packed_cmd_hdr[0] & ~PACKED_HDR_VER_MASK;
+ break;
+ case TEST_HDR_WRONG_WRITE_CODE:
+ test_pr_info("%s: wrong write code", __func__);
+ /* Set R/W field with R value (1 byte, offset 1 in header) */
+ packed_cmd_hdr[0] = packed_cmd_hdr[0] & ~PACKED_HDR_RW_MASK;
+ packed_cmd_hdr[0] = packed_cmd_hdr[0] | 0x00000100;
+ break;
+ case TEST_HDR_INVALID_RW_CODE:
+ test_pr_info("%s: invalid r/w code", __func__);
+ /* Set R/W field with invalid value */
+ packed_cmd_hdr[0] = packed_cmd_hdr[0] & ~PACKED_HDR_RW_MASK;
+ packed_cmd_hdr[0] = packed_cmd_hdr[0] | 0x00000400;
+ break;
+ case TEST_HDR_DIFFERENT_ADDRESSES:
+ test_pr_info("%s: different addresses", __func__);
+ second_rq = list_entry(req->queuelist.next, struct request,
+ queuelist);
+ test_pr_info("%s: test_rq->sector=%ld, second_rq->sector=%ld",
+ __func__, (long)req->__sector,
+ (long)second_rq->__sector);
+ /*
+ * Put start sector of second write request in the first write
+ * request's cmd25 argument in the packed header
+ */
+ packed_cmd_hdr[3] = second_rq->__sector;
+ break;
+ case TEST_HDR_REQ_NUM_SMALLER_THAN_ACTUAL:
+ test_pr_info("%s: request num smaller than actual" , __func__);
+ num_requests = (packed_cmd_hdr[0] & PACKED_HDR_NUM_REQS_MASK)
+ >> 16;
+ /* num of entries is decremented by 1 */
+ num_requests = (num_requests - 1) << 16;
+ /*
+ * Set number of requests field in packed write header to be
+ * smaller than the actual number (1 byte, offset 2 in header)
+ */
+ packed_cmd_hdr[0] = (packed_cmd_hdr[0] &
+ ~PACKED_HDR_NUM_REQS_MASK) + num_requests;
+ break;
+ case TEST_HDR_REQ_NUM_LARGER_THAN_ACTUAL:
+ test_pr_info("%s: request num larger than actual" , __func__);
+ num_requests = (packed_cmd_hdr[0] & PACKED_HDR_NUM_REQS_MASK)
+ >> 16;
+ /* num of entries is incremented by 1 */
+ num_requests = (num_requests + 1) << 16;
+ /*
+ * Set number of requests field in packed write header to be
+ * larger than the actual number (1 byte, offset 2 in header).
+ */
+ packed_cmd_hdr[0] = (packed_cmd_hdr[0] &
+ ~PACKED_HDR_NUM_REQS_MASK) + num_requests;
+ break;
+ case TEST_HDR_CMD23_PACKED_BIT_SET:
+ test_pr_info("%s: header CMD23 packed bit set" , __func__);
+ /*
+ * Set packed bit (bit 30) in cmd23 argument of first and second
+ * write requests in packed write header.
+ * These are located at bytes 2 and 4 in packed write header
+ */
+ packed_cmd_hdr[2] = packed_cmd_hdr[2] | CMD23_PACKED_BIT;
+ packed_cmd_hdr[4] = packed_cmd_hdr[4] | CMD23_PACKED_BIT;
+ break;
+ case TEST_CMD23_MAX_PACKED_WRITES:
+ test_pr_info("%s: CMD23 request num > max_packed_reqs",
+ __func__);
+ /*
+ * Set the individual packed cmd23 request num to
+ * max_packed_reqs + 1
+ */
+ brq->sbc.arg = MMC_CMD23_ARG_PACKED | (max_packed_reqs + 1);
+ break;
+ case TEST_CMD23_ZERO_PACKED_WRITES:
+ test_pr_info("%s: CMD23 request num = 0", __func__);
+ /* Set the individual packed cmd23 request num to zero */
+ brq->sbc.arg = MMC_CMD23_ARG_PACKED;
+ break;
+ case TEST_CMD23_PACKED_BIT_UNSET:
+ test_pr_info("%s: CMD23 packed bit unset", __func__);
+ /*
+ * Set the individual packed cmd23 packed bit to 0,
+ * although there is a packed write request
+ */
+ brq->sbc.arg &= ~CMD23_PACKED_BIT;
+ break;
+ case TEST_CMD23_REL_WR_BIT_SET:
+ test_pr_info("%s: CMD23 REL WR bit set", __func__);
+ /* Set the individual packed cmd23 reliable write bit */
+ brq->sbc.arg = MMC_CMD23_ARG_PACKED | MMC_CMD23_ARG_REL_WR;
+ break;
+ case TEST_CMD23_BITS_16TO29_SET:
+ test_pr_info("%s: CMD23 bits [16-29] set", __func__);
+ brq->sbc.arg = MMC_CMD23_ARG_PACKED |
+ PACKED_HDR_BITS_16_TO_29_SET;
+ break;
+ case TEST_CMD23_HDR_BLK_NOT_IN_COUNT:
+ test_pr_info("%s: CMD23 hdr not in block count", __func__);
+ brq->sbc.arg = MMC_CMD23_ARG_PACKED |
+ ((rq_data_dir(req) == READ) ? 0 : mqrq->packed_blocks);
+ break;
+ default:
+ test_pr_err("%s: unexpected testcase %d",
+ __func__, mbtd->test_info.testcase);
+ break;
+ }
+}
+
+/*
+ * A callback assigned to the err_check_fn field of the mmc_request by the
+ * MMC/card/block layer.
+ * Called upon request completion by the MMC/core layer.
+ * Here we emulate an error return value from the card.
+ */
+static int test_err_check(struct mmc_card *card, struct mmc_async_req *areq)
+{
+ struct mmc_queue_req *mq_rq = container_of(areq, struct mmc_queue_req,
+ mmc_active);
+ struct request_queue *req_q = test_iosched_get_req_queue();
+ struct mmc_queue *mq;
+ int max_packed_reqs;
+ int ret = 0;
+ struct mmc_blk_request *brq;
+
+ if (req_q)
+ mq = req_q->queuedata;
+ else {
+ test_pr_err("%s: NULL request_queue", __func__);
+ return 0;
+ }
+
+ if (!mq) {
+ test_pr_err("%s: %s: NULL mq", __func__,
+ mmc_hostname(card->host));
+ return 0;
+ }
+
+ max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+
+ if (!mq_rq) {
+ test_pr_err("%s: %s: NULL mq_rq", __func__,
+ mmc_hostname(card->host));
+ return 0;
+ }
+ brq = &mq_rq->brq;
+
+ switch (mbtd->test_info.testcase) {
+ case TEST_RET_ABORT:
+ test_pr_info("%s: return abort", __func__);
+ ret = MMC_BLK_ABORT;
+ break;
+ case TEST_RET_PARTIAL_FOLLOWED_BY_SUCCESS:
+ test_pr_info("%s: return partial followed by success",
+ __func__);
+ /*
+ * Since in this testcase num_requests is always >= 2,
+ * we can be sure that packed_fail_idx is always >= 1
+ */
+ mq_rq->packed_fail_idx = (mbtd->num_requests / 2);
+ test_pr_info("%s: packed_fail_idx = %d"
+ , __func__, mq_rq->packed_fail_idx);
+ mq->err_check_fn = NULL;
+ ret = MMC_BLK_PARTIAL;
+ break;
+ case TEST_RET_PARTIAL_FOLLOWED_BY_ABORT:
+ if (!mbtd->err_check_counter) {
+ test_pr_info("%s: return partial followed by abort",
+ __func__);
+ mbtd->err_check_counter++;
+ /*
+ * Since in this testcase num_requests is always >= 3,
+ * we have that packed_fail_idx is always >= 1
+ */
+ mq_rq->packed_fail_idx = (mbtd->num_requests / 2);
+ test_pr_info("%s: packed_fail_idx = %d"
+ , __func__, mq_rq->packed_fail_idx);
+ ret = MMC_BLK_PARTIAL;
+ break;
+ }
+ mbtd->err_check_counter = 0;
+ mq->err_check_fn = NULL;
+ ret = MMC_BLK_ABORT;
+ break;
+ case TEST_RET_PARTIAL_MULTIPLE_UNTIL_SUCCESS:
+ test_pr_info("%s: return partial multiple until success",
+ __func__);
+ if (++mbtd->err_check_counter >= (mbtd->num_requests)) {
+ mq->err_check_fn = NULL;
+ mbtd->err_check_counter = 0;
+ ret = MMC_BLK_PARTIAL;
+ break;
+ }
+ mq_rq->packed_fail_idx = 1;
+ ret = MMC_BLK_PARTIAL;
+ break;
+ case TEST_RET_PARTIAL_MAX_FAIL_IDX:
+ test_pr_info("%s: return partial max fail_idx", __func__);
+ mq_rq->packed_fail_idx = max_packed_reqs - 1;
+ mq->err_check_fn = NULL;
+ ret = MMC_BLK_PARTIAL;
+ break;
+ case TEST_RET_RETRY:
+ test_pr_info("%s: return retry", __func__);
+ ret = MMC_BLK_RETRY;
+ break;
+ case TEST_RET_CMD_ERR:
+ test_pr_info("%s: return cmd err", __func__);
+ ret = MMC_BLK_CMD_ERR;
+ break;
+ case TEST_RET_DATA_ERR:
+ test_pr_info("%s: return data err", __func__);
+ ret = MMC_BLK_DATA_ERR;
+ break;
+ case BKOPS_URGENT_LEVEL_2:
+ case BKOPS_URGENT_LEVEL_3:
+ case BKOPS_URGENT_LEVEL_2_TWO_REQS:
+ if (mbtd->err_check_counter++ == 0) {
+ test_pr_info("%s: simulate an exception from the card",
+ __func__);
+ brq->cmd.resp[0] |= R1_EXCEPTION_EVENT;
+ }
+ mq->err_check_fn = NULL;
+ break;
+ default:
+ test_pr_err("%s: unexpected testcase %d",
+ __func__, mbtd->test_info.testcase);
+ }
+
+ return ret;
+}
+
+/*
+ * This is a specific implementation for the get_test_case_str_fn function
+ * pointer in the test_info data structure. Given a valid test_data instance,
+ * the function returns a string resembling the test name, based on the testcase
+ */
+static char *get_test_case_str(struct test_data *td)
+{
+ if (!td) {
+ test_pr_err("%s: NULL td", __func__);
+ return NULL;
+ }
+
+ switch (td->test_info.testcase) {
+ case TEST_STOP_DUE_TO_FLUSH:
+ return "Test stop due to flush";
+ case TEST_STOP_DUE_TO_FLUSH_AFTER_MAX_REQS:
+ return "Test stop due to flush after max-1 reqs";
+ case TEST_STOP_DUE_TO_READ:
+ return "Test stop due to read";
+ case TEST_STOP_DUE_TO_READ_AFTER_MAX_REQS:
+ return "Test stop due to read after max-1 reqs";
+ case TEST_STOP_DUE_TO_EMPTY_QUEUE:
+ return "Test stop due to empty queue";
+ case TEST_STOP_DUE_TO_MAX_REQ_NUM:
+ return "Test stop due to max req num";
+ case TEST_STOP_DUE_TO_THRESHOLD:
+ return "Test stop due to exceeding threshold";
+ case TEST_RET_ABORT:
+ return "Test err_check return abort";
+ case TEST_RET_PARTIAL_FOLLOWED_BY_SUCCESS:
+ return "Test err_check return partial followed by success";
+ case TEST_RET_PARTIAL_FOLLOWED_BY_ABORT:
+ return "Test err_check return partial followed by abort";
+ case TEST_RET_PARTIAL_MULTIPLE_UNTIL_SUCCESS:
+ return "Test err_check return partial multiple until success";
+ case TEST_RET_PARTIAL_MAX_FAIL_IDX:
+ return "Test err_check return partial max fail index";
+ case TEST_RET_RETRY:
+ return "Test err_check return retry";
+ case TEST_RET_CMD_ERR:
+ return "Test err_check return cmd error";
+ case TEST_RET_DATA_ERR:
+ return "Test err_check return data error";
+ case TEST_HDR_INVALID_VERSION:
+ return "Test invalid - wrong header version";
+ case TEST_HDR_WRONG_WRITE_CODE:
+ return "Test invalid - wrong write code";
+ case TEST_HDR_INVALID_RW_CODE:
+ return "Test invalid - wrong R/W code";
+ case TEST_HDR_DIFFERENT_ADDRESSES:
+ return "Test invalid - header different addresses";
+ case TEST_HDR_REQ_NUM_SMALLER_THAN_ACTUAL:
+ return "Test invalid - header req num smaller than actual";
+ case TEST_HDR_REQ_NUM_LARGER_THAN_ACTUAL:
+ return "Test invalid - header req num larger than actual";
+ case TEST_HDR_CMD23_PACKED_BIT_SET:
+ return "Test invalid - header cmd23 packed bit set";
+ case TEST_CMD23_MAX_PACKED_WRITES:
+ return "Test invalid - cmd23 max packed writes";
+ case TEST_CMD23_ZERO_PACKED_WRITES:
+ return "Test invalid - cmd23 zero packed writes";
+ case TEST_CMD23_PACKED_BIT_UNSET:
+ return "Test invalid - cmd23 packed bit unset";
+ case TEST_CMD23_REL_WR_BIT_SET:
+ return "Test invalid - cmd23 rel wr bit set";
+ case TEST_CMD23_BITS_16TO29_SET:
+ return "Test invalid - cmd23 bits [16-29] set";
+ case TEST_CMD23_HDR_BLK_NOT_IN_COUNT:
+ return "Test invalid - cmd23 header block not in count";
+ case TEST_PACKING_EXP_N_OVER_TRIGGER:
+ return "\nTest packing control - pack n";
+ case TEST_PACKING_EXP_N_OVER_TRIGGER_FB_READ:
+ return "\nTest packing control - pack n followed by read";
+ case TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N:
+ return "\nTest packing control - pack n followed by flush";
+ case TEST_PACKING_EXP_ONE_OVER_TRIGGER_FB_READ:
+ return "\nTest packing control - pack one followed by read";
+ case TEST_PACKING_EXP_THRESHOLD_OVER_TRIGGER:
+ return "\nTest packing control - pack threshold";
+ case TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS:
+ return "\nTest packing control - no packing";
+ case TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS:
+ return "\nTest packing control - no packing, trigger requests";
+ case TEST_PACKING_NOT_EXP_TRIGGER_READ_TRIGGER:
+ return "\nTest packing control - no pack, trigger-read-trigger";
+ case TEST_PACKING_NOT_EXP_TRIGGER_FLUSH_TRIGGER:
+ return "\nTest packing control- no pack, trigger-flush-trigger";
+ case TEST_PACK_MIX_PACKED_NO_PACKED_PACKED:
+ return "\nTest packing control - mix: pack -> no pack -> pack";
+ case TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED:
+ return "\nTest packing control - mix: no pack->pack->no pack";
+ case TEST_WRITE_DISCARD_SANITIZE_READ:
+ return "\nTest write, discard, sanitize";
+ case BKOPS_DELAYED_WORK_LEVEL_1:
+ return "\nTest delayed work BKOPS level 1";
+ case BKOPS_DELAYED_WORK_LEVEL_1_HPI:
+ return "\nTest delayed work BKOPS level 1 with HPI";
+ case BKOPS_CANCEL_DELAYED_WORK:
+ return "\nTest cancel delayed BKOPS work";
+ case BKOPS_URGENT_LEVEL_2:
+ return "\nTest urgent BKOPS level 2";
+ case BKOPS_URGENT_LEVEL_2_TWO_REQS:
+ return "\nTest urgent BKOPS level 2, followed by a request";
+ case BKOPS_URGENT_LEVEL_3:
+ return "\nTest urgent BKOPS level 3";
+ default:
+ return "Unknown testcase";
+ }
+
+ return NULL;
+}
+
+/*
+ * Compare individual testcase's statistics to the expected statistics:
+ * Compare stop reason and number of packing events
+ */
+static int check_wr_packing_statistics(struct test_data *td)
+{
+ struct mmc_wr_pack_stats *mmc_packed_stats;
+ struct mmc_queue *mq = td->req_q->queuedata;
+ int max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+ int i;
+ struct mmc_card *card = mq->card;
+ struct mmc_wr_pack_stats expected_stats;
+ int *stop_reason;
+ int ret = 0;
+
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+
+ expected_stats = mbtd->exp_packed_stats;
+
+ mmc_packed_stats = mmc_blk_get_packed_statistics(card);
+ if (!mmc_packed_stats) {
+ test_pr_err("%s: NULL mmc_packed_stats", __func__);
+ return -EINVAL;
+ }
+
+ if (!mmc_packed_stats->packing_events) {
+ test_pr_err("%s: NULL packing_events", __func__);
+ return -EINVAL;
+ }
+
+ spin_lock(&mmc_packed_stats->lock);
+
+ if (!mmc_packed_stats->enabled) {
+ test_pr_err("%s write packing statistics are not enabled",
+ __func__);
+ ret = -EINVAL;
+ goto exit_err;
+ }
+
+ stop_reason = mmc_packed_stats->pack_stop_reason;
+
+ for (i = 1; i <= max_packed_reqs; ++i) {
+ if (mmc_packed_stats->packing_events[i] !=
+ expected_stats.packing_events[i]) {
+ test_pr_err(
+ "%s: Wrong pack stats in index %d, got %d, expected %d",
+ __func__, i, mmc_packed_stats->packing_events[i],
+ expected_stats.packing_events[i]);
+ if (td->fs_wr_reqs_during_test)
+ goto cancel_round;
+ ret = -EINVAL;
+ goto exit_err;
+ }
+ }
+
+ if (mmc_packed_stats->pack_stop_reason[EXCEEDS_SEGMENTS] !=
+ expected_stats.pack_stop_reason[EXCEEDS_SEGMENTS]) {
+ test_pr_err(
+ "%s: Wrong pack stop reason EXCEEDS_SEGMENTS %d, expected %d",
+ __func__, stop_reason[EXCEEDS_SEGMENTS],
+ expected_stats.pack_stop_reason[EXCEEDS_SEGMENTS]);
+ if (td->fs_wr_reqs_during_test)
+ goto cancel_round;
+ ret = -EINVAL;
+ goto exit_err;
+ }
+
+ if (mmc_packed_stats->pack_stop_reason[EXCEEDS_SECTORS] !=
+ expected_stats.pack_stop_reason[EXCEEDS_SECTORS]) {
+ test_pr_err(
+ "%s: Wrong pack stop reason EXCEEDS_SECTORS %d, expected %d",
+ __func__, stop_reason[EXCEEDS_SECTORS],
+ expected_stats.pack_stop_reason[EXCEEDS_SECTORS]);
+ if (td->fs_wr_reqs_during_test)
+ goto cancel_round;
+ ret = -EINVAL;
+ goto exit_err;
+ }
+
+ if (mmc_packed_stats->pack_stop_reason[WRONG_DATA_DIR] !=
+ expected_stats.pack_stop_reason[WRONG_DATA_DIR]) {
+ test_pr_err(
+ "%s: Wrong pack stop reason WRONG_DATA_DIR %d, expected %d",
+ __func__, stop_reason[WRONG_DATA_DIR],
+ expected_stats.pack_stop_reason[WRONG_DATA_DIR]);
+ if (td->fs_wr_reqs_during_test)
+ goto cancel_round;
+ ret = -EINVAL;
+ goto exit_err;
+ }
+
+ if (mmc_packed_stats->pack_stop_reason[FLUSH_OR_DISCARD] !=
+ expected_stats.pack_stop_reason[FLUSH_OR_DISCARD]) {
+ test_pr_err(
+ "%s: Wrong pack stop reason FLUSH_OR_DISCARD %d, expected %d",
+ __func__, stop_reason[FLUSH_OR_DISCARD],
+ expected_stats.pack_stop_reason[FLUSH_OR_DISCARD]);
+ if (td->fs_wr_reqs_during_test)
+ goto cancel_round;
+ ret = -EINVAL;
+ goto exit_err;
+ }
+
+ if (mmc_packed_stats->pack_stop_reason[EMPTY_QUEUE] !=
+ expected_stats.pack_stop_reason[EMPTY_QUEUE]) {
+ test_pr_err(
+ "%s: Wrong pack stop reason EMPTY_QUEUE %d, expected %d",
+ __func__, stop_reason[EMPTY_QUEUE],
+ expected_stats.pack_stop_reason[EMPTY_QUEUE]);
+ if (td->fs_wr_reqs_during_test)
+ goto cancel_round;
+ ret = -EINVAL;
+ goto exit_err;
+ }
+
+ if (mmc_packed_stats->pack_stop_reason[REL_WRITE] !=
+ expected_stats.pack_stop_reason[REL_WRITE]) {
+ test_pr_err(
+ "%s: Wrong pack stop reason REL_WRITE %d, expected %d",
+ __func__, stop_reason[REL_WRITE],
+ expected_stats.pack_stop_reason[REL_WRITE]);
+ if (td->fs_wr_reqs_during_test)
+ goto cancel_round;
+ ret = -EINVAL;
+ goto exit_err;
+ }
+
+exit_err:
+ spin_unlock(&mmc_packed_stats->lock);
+ if (ret && mmc_packed_stats->enabled)
+ print_mmc_packing_stats(card);
+ return ret;
+cancel_round:
+ spin_unlock(&mmc_packed_stats->lock);
+ test_iosched_set_ignore_round(true);
+ return 0;
+}
+
+/*
+ * Pseudo-randomly choose a seed based on the last seed, and update it in
+ * seed_number. then return seed_number (mod max_val), or min_val.
+ */
+static unsigned int pseudo_random_seed(unsigned int *seed_number,
+ unsigned int min_val,
+ unsigned int max_val)
+{
+ int ret = 0;
+
+ if (!seed_number)
+ return 0;
+
+ *seed_number = ((unsigned int)(((unsigned long)*seed_number *
+ (unsigned long)LARGE_PRIME_1) + LARGE_PRIME_2));
+ ret = (unsigned int)((*seed_number) % max_val);
+
+ return (ret > min_val ? ret : min_val);
+}
+
+/*
+ * Given a pseudo-random seed, find a pseudo-random num_of_bios.
+ * Make sure that num_of_bios is not larger than TEST_MAX_SECTOR_RANGE
+ */
+static void pseudo_rnd_num_of_bios(unsigned int *num_bios_seed,
+ unsigned int *num_of_bios)
+{
+ do {
+ *num_of_bios = pseudo_random_seed(num_bios_seed, 1,
+ TEST_MAX_BIOS_PER_REQ);
+ if (!(*num_of_bios))
+ *num_of_bios = 1;
+ } while ((*num_of_bios) * BIO_U32_SIZE * 4 > TEST_MAX_SECTOR_RANGE);
+}
+
+/* Add a single read request to the given td's request queue */
+static int prepare_request_add_read(struct test_data *td)
+{
+ int ret;
+ int start_sec;
+
+ if (td)
+ start_sec = td->start_sector;
+ else {
+ test_pr_err("%s: NULL td", __func__);
+ return 0;
+ }
+
+ test_pr_info("%s: Adding a read request, first req_id=%d", __func__,
+ td->wr_rd_next_req_id);
+
+ ret = test_iosched_add_wr_rd_test_req(0, READ, start_sec, 2,
+ TEST_PATTERN_5A, NULL);
+ if (ret) {
+ test_pr_err("%s: failed to add a read request", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Add a single flush request to the given td's request queue */
+static int prepare_request_add_flush(struct test_data *td)
+{
+ int ret;
+
+ if (!td) {
+ test_pr_err("%s: NULL td", __func__);
+ return 0;
+ }
+
+ test_pr_info("%s: Adding a flush request, first req_id=%d", __func__,
+ td->unique_next_req_id);
+ ret = test_iosched_add_unique_test_req(0, REQ_UNIQUE_FLUSH,
+ 0, 0, NULL);
+ if (ret) {
+ test_pr_err("%s: failed to add a flush request", __func__);
+ return ret;
+ }
+
+ return ret;
+}
+
+/*
+ * Add num_requets amount of write requests to the given td's request queue.
+ * If random test mode is chosen we pseudo-randomly choose the number of bios
+ * for each write request, otherwise add between 1 to 5 bio per request.
+ */
+static int prepare_request_add_write_reqs(struct test_data *td,
+ int num_requests, int is_err_expected,
+ int is_random)
+{
+ int i;
+ unsigned int start_sec;
+ int num_bios;
+ int ret = 0;
+ unsigned int *bio_seed = &mbtd->random_test_seed;
+
+ if (td)
+ start_sec = td->start_sector;
+ else {
+ test_pr_err("%s: NULL td", __func__);
+ return ret;
+ }
+
+ test_pr_info("%s: Adding %d write requests, first req_id=%d", __func__,
+ num_requests, td->wr_rd_next_req_id);
+
+ for (i = 1; i <= num_requests; i++) {
+ start_sec = td->start_sector + 4096 * td->num_of_write_bios;
+ if (is_random)
+ pseudo_rnd_num_of_bios(bio_seed, &num_bios);
+ else
+ /*
+ * For the non-random case, give num_bios a value
+ * between 1 and 5, to keep a small number of BIOs
+ */
+ num_bios = (i%5)+1;
+
+ ret = test_iosched_add_wr_rd_test_req(is_err_expected, WRITE,
+ start_sec, num_bios, TEST_PATTERN_5A, NULL);
+
+ if (ret) {
+ test_pr_err("%s: failed to add a write request",
+ __func__);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Prepare the write, read and flush requests for a generic packed commands
+ * testcase
+ */
+static int prepare_packed_requests(struct test_data *td, int is_err_expected,
+ int num_requests, int is_random)
+{
+ int ret = 0;
+ struct mmc_queue *mq;
+ int max_packed_reqs;
+ struct request_queue *req_q;
+
+ if (!td) {
+ pr_err("%s: NULL td", __func__);
+ return -EINVAL;
+ }
+
+ req_q = td->req_q;
+
+ if (!req_q) {
+ pr_err("%s: NULL request queue", __func__);
+ return -EINVAL;
+ }
+
+ mq = req_q->queuedata;
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+
+ max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+
+ if (mbtd->random_test_seed <= 0) {
+ mbtd->random_test_seed =
+ (unsigned int)(get_jiffies_64() & 0xFFFF);
+ test_pr_info("%s: got seed from jiffies %d",
+ __func__, mbtd->random_test_seed);
+ }
+
+ mmc_blk_init_packed_statistics(mq->card);
+
+ ret = prepare_request_add_write_reqs(td, num_requests, is_err_expected,
+ is_random);
+ if (ret)
+ return ret;
+
+ /* Avoid memory corruption in upcoming stats set */
+ if (td->test_info.testcase == TEST_STOP_DUE_TO_THRESHOLD)
+ num_requests--;
+
+ memset((void *)mbtd->exp_packed_stats.pack_stop_reason, 0,
+ sizeof(mbtd->exp_packed_stats.pack_stop_reason));
+ memset(mbtd->exp_packed_stats.packing_events, 0,
+ (max_packed_reqs + 1) * sizeof(u32));
+ if (num_requests <= max_packed_reqs)
+ mbtd->exp_packed_stats.packing_events[num_requests] = 1;
+
+ switch (td->test_info.testcase) {
+ case TEST_STOP_DUE_TO_FLUSH:
+ case TEST_STOP_DUE_TO_FLUSH_AFTER_MAX_REQS:
+ ret = prepare_request_add_flush(td);
+ if (ret)
+ return ret;
+
+ mbtd->exp_packed_stats.pack_stop_reason[FLUSH_OR_DISCARD] = 1;
+ break;
+ case TEST_STOP_DUE_TO_READ:
+ case TEST_STOP_DUE_TO_READ_AFTER_MAX_REQS:
+ ret = prepare_request_add_read(td);
+ if (ret)
+ return ret;
+
+ mbtd->exp_packed_stats.pack_stop_reason[WRONG_DATA_DIR] = 1;
+ break;
+ case TEST_STOP_DUE_TO_THRESHOLD:
+ mbtd->exp_packed_stats.packing_events[num_requests] = 1;
+ mbtd->exp_packed_stats.packing_events[1] = 1;
+ mbtd->exp_packed_stats.pack_stop_reason[THRESHOLD] = 1;
+ mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1;
+ break;
+ case TEST_STOP_DUE_TO_MAX_REQ_NUM:
+ case TEST_RET_PARTIAL_MAX_FAIL_IDX:
+ mbtd->exp_packed_stats.pack_stop_reason[THRESHOLD] = 1;
+ break;
+ default:
+ mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1;
+ }
+ mbtd->num_requests = num_requests;
+
+ return 0;
+}
+
+/*
+ * Prepare the write, read and flush requests for the packing control
+ * testcases
+ */
+static int prepare_packed_control_tests_requests(struct test_data *td,
+ int is_err_expected, int num_requests, int is_random)
+{
+ int ret = 0;
+ struct mmc_queue *mq;
+ int max_packed_reqs;
+ int temp_num_req = num_requests;
+ struct request_queue *req_q;
+ int test_packed_trigger;
+ int num_packed_reqs;
+
+ if (!td) {
+ test_pr_err("%s: NULL td\n", __func__);
+ return -EINVAL;
+ }
+
+ req_q = td->req_q;
+
+ if (!req_q) {
+ test_pr_err("%s: NULL request queue\n", __func__);
+ return -EINVAL;
+ }
+
+ mq = req_q->queuedata;
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+
+ max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+ test_packed_trigger = mq->num_wr_reqs_to_start_packing;
+ num_packed_reqs = num_requests - test_packed_trigger;
+
+ if (mbtd->random_test_seed == 0) {
+ mbtd->random_test_seed =
+ (unsigned int)(get_jiffies_64() & 0xFFFF);
+ test_pr_info("%s: got seed from jiffies %d",
+ __func__, mbtd->random_test_seed);
+ }
+
+ mmc_blk_init_packed_statistics(mq->card);
+
+ if (td->test_info.testcase ==
+ TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED) {
+ temp_num_req = num_requests;
+ num_requests = test_packed_trigger - 1;
+ }
+
+ /* Verify that the packing is disabled before starting the test */
+ mq->wr_packing_enabled = false;
+ mq->num_of_potential_packed_wr_reqs = 0;
+
+ if (td->test_info.testcase == TEST_PACK_MIX_PACKED_NO_PACKED_PACKED) {
+ mq->num_of_potential_packed_wr_reqs = test_packed_trigger + 1;
+ mq->wr_packing_enabled = true;
+ num_requests = test_packed_trigger + 2;
+ }
+
+ ret = prepare_request_add_write_reqs(td, num_requests, is_err_expected,
+ is_random);
+ if (ret)
+ goto exit;
+
+ if (td->test_info.testcase == TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED)
+ num_requests = temp_num_req;
+
+ memset((void *)mbtd->exp_packed_stats.pack_stop_reason, 0,
+ sizeof(mbtd->exp_packed_stats.pack_stop_reason));
+ memset(mbtd->exp_packed_stats.packing_events, 0,
+ (max_packed_reqs + 1) * sizeof(u32));
+
+ switch (td->test_info.testcase) {
+ case TEST_PACKING_EXP_N_OVER_TRIGGER_FB_READ:
+ case TEST_PACKING_EXP_ONE_OVER_TRIGGER_FB_READ:
+ ret = prepare_request_add_read(td);
+ if (ret)
+ goto exit;
+
+ mbtd->exp_packed_stats.pack_stop_reason[WRONG_DATA_DIR] = 1;
+ mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1;
+ break;
+ case TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N:
+ ret = prepare_request_add_flush(td);
+ if (ret)
+ goto exit;
+
+ ret = prepare_request_add_write_reqs(td, num_packed_reqs,
+ is_err_expected, is_random);
+ if (ret)
+ goto exit;
+
+ mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1;
+ mbtd->exp_packed_stats.pack_stop_reason[FLUSH_OR_DISCARD] = 1;
+ mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 2;
+ break;
+ case TEST_PACKING_NOT_EXP_TRIGGER_READ_TRIGGER:
+ ret = prepare_request_add_read(td);
+ if (ret)
+ goto exit;
+
+ ret = prepare_request_add_write_reqs(td, test_packed_trigger,
+ is_err_expected, is_random);
+ if (ret)
+ goto exit;
+
+ mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1;
+ break;
+ case TEST_PACKING_NOT_EXP_TRIGGER_FLUSH_TRIGGER:
+ ret = prepare_request_add_flush(td);
+ if (ret)
+ goto exit;
+
+ ret = prepare_request_add_write_reqs(td, test_packed_trigger,
+ is_err_expected, is_random);
+ if (ret)
+ goto exit;
+
+ mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1;
+ break;
+ case TEST_PACK_MIX_PACKED_NO_PACKED_PACKED:
+ ret = prepare_request_add_read(td);
+ if (ret)
+ goto exit;
+
+ ret = prepare_request_add_write_reqs(td, test_packed_trigger-1,
+ is_err_expected, is_random);
+ if (ret)
+ goto exit;
+
+ ret = prepare_request_add_write_reqs(td, num_requests,
+ is_err_expected, is_random);
+ if (ret)
+ goto exit;
+
+ mbtd->exp_packed_stats.packing_events[num_requests] = 1;
+ mbtd->exp_packed_stats.packing_events[num_requests-1] = 1;
+ mbtd->exp_packed_stats.pack_stop_reason[WRONG_DATA_DIR] = 1;
+ mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1;
+ break;
+ case TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED:
+ ret = prepare_request_add_read(td);
+ if (ret)
+ goto exit;
+
+ ret = prepare_request_add_write_reqs(td, num_requests,
+ is_err_expected, is_random);
+ if (ret)
+ goto exit;
+
+ ret = prepare_request_add_read(td);
+ if (ret)
+ goto exit;
+
+ ret = prepare_request_add_write_reqs(td, test_packed_trigger-1,
+ is_err_expected, is_random);
+ if (ret)
+ goto exit;
+
+ mbtd->exp_packed_stats.pack_stop_reason[WRONG_DATA_DIR] = 1;
+ mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1;
+ break;
+ case TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS:
+ case TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS:
+ mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1;
+ break;
+ default:
+ mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1;
+ mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1;
+ }
+ mbtd->num_requests = num_requests;
+
+exit:
+ return ret;
+}
+
+/*
+ * Prepare requests for the TEST_RET_PARTIAL_FOLLOWED_BY_ABORT testcase.
+ * In this testcase we have mixed error expectations from different
+ * write requests, hence the special prepare function.
+ */
+static int prepare_partial_followed_by_abort(struct test_data *td,
+ int num_requests)
+{
+ int i, start_address;
+ int is_err_expected = 0;
+ int ret = 0;
+ struct mmc_queue *mq = test_iosched_get_req_queue()->queuedata;
+ int max_packed_reqs;
+
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+
+ max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+
+ mmc_blk_init_packed_statistics(mq->card);
+
+ for (i = 1; i <= num_requests; i++) {
+ if (i > (num_requests / 2))
+ is_err_expected = 1;
+
+ start_address = td->start_sector + 4096 * td->num_of_write_bios;
+ ret = test_iosched_add_wr_rd_test_req(is_err_expected, WRITE,
+ start_address, (i % 5) + 1, TEST_PATTERN_5A,
+ NULL);
+ if (ret) {
+ test_pr_err("%s: failed to add a write request",
+ __func__);
+ return ret;
+ }
+ }
+
+ memset((void *)&mbtd->exp_packed_stats.pack_stop_reason, 0,
+ sizeof(mbtd->exp_packed_stats.pack_stop_reason));
+ memset(mbtd->exp_packed_stats.packing_events, 0,
+ (max_packed_reqs + 1) * sizeof(u32));
+ mbtd->exp_packed_stats.packing_events[num_requests] = 1;
+ mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1;
+
+ mbtd->num_requests = num_requests;
+
+ return ret;
+}
+
+/*
+ * Get number of write requests for current testcase. If random test mode was
+ * chosen, pseudo-randomly choose the number of requests, otherwise set to
+ * two less than the packing threshold.
+ */
+static int get_num_requests(struct test_data *td)
+{
+ int *seed = &mbtd->random_test_seed;
+ struct request_queue *req_q;
+ struct mmc_queue *mq;
+ int max_num_requests;
+ int num_requests;
+ int min_num_requests = 2;
+ int is_random = mbtd->is_random;
+ int max_for_double;
+ int test_packed_trigger;
+
+ req_q = test_iosched_get_req_queue();
+ if (req_q)
+ mq = req_q->queuedata;
+ else {
+ test_pr_err("%s: NULL request queue", __func__);
+ return 0;
+ }
+
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+
+ max_num_requests = mq->card->ext_csd.max_packed_writes;
+ num_requests = max_num_requests - 2;
+ test_packed_trigger = mq->num_wr_reqs_to_start_packing;
+
+ /*
+ * Here max_for_double is intended for packed control testcases
+ * in which we issue many write requests. It's purpose is to prevent
+ * exceeding max number of req_queue requests.
+ */
+ max_for_double = max_num_requests - 10;
+
+ if (td->test_info.testcase ==
+ TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS)
+ /* Don't expect packing, so issue up to trigger-1 reqs */
+ num_requests = test_packed_trigger - 1;
+
+ if (is_random) {
+ if (td->test_info.testcase ==
+ TEST_RET_PARTIAL_FOLLOWED_BY_ABORT)
+ /*
+ * Here we don't want num_requests to be less than 1
+ * as a consequence of division by 2.
+ */
+ min_num_requests = 3;
+
+ if (td->test_info.testcase ==
+ TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS)
+ /* Don't expect packing, so issue up to trigger reqs */
+ max_num_requests = test_packed_trigger;
+
+ num_requests = pseudo_random_seed(seed, min_num_requests,
+ max_num_requests - 1);
+ }
+
+ if (td->test_info.testcase ==
+ TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS)
+ num_requests -= test_packed_trigger;
+
+ if (td->test_info.testcase == TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N)
+ num_requests =
+ num_requests > max_for_double ? max_for_double : num_requests;
+
+ if (mbtd->test_group == TEST_PACKING_CONTROL_GROUP)
+ num_requests += test_packed_trigger;
+
+ if (td->test_info.testcase == TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS)
+ num_requests = test_packed_trigger;
+
+ return num_requests;
+}
+
+/*
+ * An implementation for the prepare_test_fn pointer in the test_info
+ * data structure. According to the testcase we add the right number of requests
+ * and decide if an error is expected or not.
+ */
+static int prepare_test(struct test_data *td)
+{
+ struct mmc_queue *mq = test_iosched_get_req_queue()->queuedata;
+ int max_num_requests;
+ int num_requests = 0;
+ int ret = 0;
+ int is_random = mbtd->is_random;
+ int test_packed_trigger = mq->num_wr_reqs_to_start_packing;
+
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+
+ max_num_requests = mq->card->ext_csd.max_packed_writes;
+
+ if (is_random && mbtd->random_test_seed == 0) {
+ mbtd->random_test_seed =
+ (unsigned int)(get_jiffies_64() & 0xFFFF);
+ test_pr_info("%s: got seed from jiffies %d",
+ __func__, mbtd->random_test_seed);
+ }
+
+ num_requests = get_num_requests(td);
+
+ if (mbtd->test_group == TEST_SEND_INVALID_GROUP)
+ mq->packed_test_fn =
+ test_invalid_packed_cmd;
+
+ if (mbtd->test_group == TEST_ERR_CHECK_GROUP)
+ mq->err_check_fn = test_err_check;
+
+ switch (td->test_info.testcase) {
+ case TEST_STOP_DUE_TO_FLUSH:
+ case TEST_STOP_DUE_TO_READ:
+ case TEST_RET_PARTIAL_FOLLOWED_BY_SUCCESS:
+ case TEST_RET_PARTIAL_MULTIPLE_UNTIL_SUCCESS:
+ case TEST_STOP_DUE_TO_EMPTY_QUEUE:
+ case TEST_CMD23_PACKED_BIT_UNSET:
+ ret = prepare_packed_requests(td, 0, num_requests, is_random);
+ break;
+ case TEST_STOP_DUE_TO_FLUSH_AFTER_MAX_REQS:
+ case TEST_STOP_DUE_TO_READ_AFTER_MAX_REQS:
+ ret = prepare_packed_requests(td, 0, max_num_requests - 1,
+ is_random);
+ break;
+ case TEST_RET_PARTIAL_FOLLOWED_BY_ABORT:
+ ret = prepare_partial_followed_by_abort(td, num_requests);
+ break;
+ case TEST_STOP_DUE_TO_MAX_REQ_NUM:
+ case TEST_RET_PARTIAL_MAX_FAIL_IDX:
+ ret = prepare_packed_requests(td, 0, max_num_requests,
+ is_random);
+ break;
+ case TEST_STOP_DUE_TO_THRESHOLD:
+ ret = prepare_packed_requests(td, 0, max_num_requests + 1,
+ is_random);
+ break;
+ case TEST_RET_ABORT:
+ case TEST_RET_RETRY:
+ case TEST_RET_CMD_ERR:
+ case TEST_RET_DATA_ERR:
+ case TEST_HDR_INVALID_VERSION:
+ case TEST_HDR_WRONG_WRITE_CODE:
+ case TEST_HDR_INVALID_RW_CODE:
+ case TEST_HDR_DIFFERENT_ADDRESSES:
+ case TEST_HDR_REQ_NUM_SMALLER_THAN_ACTUAL:
+ case TEST_HDR_REQ_NUM_LARGER_THAN_ACTUAL:
+ case TEST_CMD23_MAX_PACKED_WRITES:
+ case TEST_CMD23_ZERO_PACKED_WRITES:
+ case TEST_CMD23_REL_WR_BIT_SET:
+ case TEST_CMD23_BITS_16TO29_SET:
+ case TEST_CMD23_HDR_BLK_NOT_IN_COUNT:
+ case TEST_HDR_CMD23_PACKED_BIT_SET:
+ ret = prepare_packed_requests(td, 1, num_requests, is_random);
+ break;
+ case TEST_PACKING_EXP_N_OVER_TRIGGER:
+ case TEST_PACKING_EXP_N_OVER_TRIGGER_FB_READ:
+ case TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS:
+ case TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS:
+ case TEST_PACK_MIX_PACKED_NO_PACKED_PACKED:
+ case TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED:
+ ret = prepare_packed_control_tests_requests(td, 0, num_requests,
+ is_random);
+ break;
+ case TEST_PACKING_EXP_THRESHOLD_OVER_TRIGGER:
+ ret = prepare_packed_control_tests_requests(td, 0,
+ max_num_requests, is_random);
+ break;
+ case TEST_PACKING_EXP_ONE_OVER_TRIGGER_FB_READ:
+ ret = prepare_packed_control_tests_requests(td, 0,
+ test_packed_trigger + 1,
+ is_random);
+ break;
+ case TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N:
+ ret = prepare_packed_control_tests_requests(td, 0, num_requests,
+ is_random);
+ break;
+ case TEST_PACKING_NOT_EXP_TRIGGER_READ_TRIGGER:
+ case TEST_PACKING_NOT_EXP_TRIGGER_FLUSH_TRIGGER:
+ ret = prepare_packed_control_tests_requests(td, 0,
+ test_packed_trigger, is_random);
+ break;
+ default:
+ test_pr_info("%s: Invalid test case...", __func__);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+/*
+ * An implementation for the post_test_fn in the test_info data structure.
+ * In our case we just reset the function pointers in the mmc_queue in order for
+ * the FS to be able to dispatch it's requests correctly after the test is
+ * finished.
+ */
+static int post_test(struct test_data *td)
+{
+ struct mmc_queue *mq;
+
+ if (!td)
+ return -EINVAL;
+
+ mq = td->req_q->queuedata;
+
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+
+ mq->packed_test_fn = NULL;
+ mq->err_check_fn = NULL;
+
+ return 0;
+}
+
+/*
+ * This function checks, based on the current test's test_group, that the
+ * packed commands capability and control are set right. In addition, we check
+ * if the card supports the packed command feature.
+ */
+static int validate_packed_commands_settings(void)
+{
+ struct request_queue *req_q;
+ struct mmc_queue *mq;
+ int max_num_requests;
+ struct mmc_host *host;
+
+ req_q = test_iosched_get_req_queue();
+ if (!req_q) {
+ test_pr_err("%s: test_iosched_get_req_queue failed", __func__);
+ test_iosched_set_test_result(TEST_FAILED);
+ return -EINVAL;
+ }
+
+ mq = req_q->queuedata;
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+
+ max_num_requests = mq->card->ext_csd.max_packed_writes;
+ host = mq->card->host;
+
+ if (!(host->caps2 && MMC_CAP2_PACKED_WR)) {
+ test_pr_err("%s: Packed Write capability disabled, exit test",
+ __func__);
+ test_iosched_set_test_result(TEST_NOT_SUPPORTED);
+ return -EINVAL;
+ }
+
+ if (max_num_requests == 0) {
+ test_pr_err(
+ "%s: no write packing support, ext_csd.max_packed_writes=%d",
+ __func__, mq->card->ext_csd.max_packed_writes);
+ test_iosched_set_test_result(TEST_NOT_SUPPORTED);
+ return -EINVAL;
+ }
+
+ test_pr_info("%s: max number of packed requests supported is %d ",
+ __func__, max_num_requests);
+
+ switch (mbtd->test_group) {
+ case TEST_SEND_WRITE_PACKING_GROUP:
+ case TEST_ERR_CHECK_GROUP:
+ case TEST_SEND_INVALID_GROUP:
+ /* disable the packing control */
+ host->caps2 &= ~MMC_CAP2_PACKED_WR_CONTROL;
+ break;
+ case TEST_PACKING_CONTROL_GROUP:
+ host->caps2 |= MMC_CAP2_PACKED_WR_CONTROL;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void pseudo_rnd_sector_and_size(unsigned int *seed,
+ unsigned int min_start_sector,
+ unsigned int *start_sector,
+ unsigned int *num_of_bios)
+{
+ unsigned int max_sec = min_start_sector + TEST_MAX_SECTOR_RANGE;
+ do {
+ *start_sector = pseudo_random_seed(seed,
+ 1, max_sec);
+ *num_of_bios = pseudo_random_seed(seed,
+ 1, TEST_MAX_BIOS_PER_REQ);
+ if (!(*num_of_bios))
+ *num_of_bios = 1;
+ } while ((*start_sector < min_start_sector) ||
+ (*start_sector + (*num_of_bios * BIO_U32_SIZE * 4)) > max_sec);
+}
+
+/* sanitize test functions */
+static int prepare_write_discard_sanitize_read(struct test_data *td)
+{
+ unsigned int start_sector;
+ unsigned int num_of_bios = 0;
+ static unsigned int total_bios;
+ unsigned int *num_bios_seed;
+ int i = 0;
+
+ if (mbtd->random_test_seed == 0) {
+ mbtd->random_test_seed =
+ (unsigned int)(get_jiffies_64() & 0xFFFF);
+ test_pr_info("%s: got seed from jiffies %d",
+ __func__, mbtd->random_test_seed);
+ }
+ num_bios_seed = &mbtd->random_test_seed;
+
+ do {
+ pseudo_rnd_sector_and_size(num_bios_seed, td->start_sector,
+ &start_sector, &num_of_bios);
+
+ /* DISCARD */
+ total_bios += num_of_bios;
+ test_pr_info("%s: discard req: id=%d, startSec=%d, NumBios=%d",
+ __func__, td->unique_next_req_id, start_sector,
+ num_of_bios);
+ test_iosched_add_unique_test_req(0, REQ_UNIQUE_DISCARD,
+ start_sector, BIO_TO_SECTOR(num_of_bios),
+ NULL);
+
+ } while (++i < (BLKDEV_MAX_RQ-10));
+
+ test_pr_info("%s: total discard bios = %d", __func__, total_bios);
+
+ test_pr_info("%s: add sanitize req", __func__);
+ test_iosched_add_unique_test_req(0, REQ_UNIQUE_SANITIZE, 0, 0, NULL);
+
+ return 0;
+}
+
+/*
+ * Post test operations for BKOPs test
+ * Disable the BKOPs statistics and clear the feature flags
+ */
+static int bkops_post_test(struct test_data *td)
+{
+ struct request_queue *q = td->req_q;
+ struct mmc_queue *mq = (struct mmc_queue *)q->queuedata;
+ struct mmc_card *card = mq->card;
+
+ mmc_card_clr_doing_bkops(mq->card);
+ card->ext_csd.raw_bkops_status = 0;
+
+ spin_lock(&card->bkops_info.bkops_stats.lock);
+ card->bkops_info.bkops_stats.enabled = false;
+ spin_unlock(&card->bkops_info.bkops_stats.lock);
+
+ return 0;
+}
+
+/*
+ * Verify the BKOPs statsistics
+ */
+static int check_bkops_result(struct test_data *td)
+{
+ struct request_queue *q = td->req_q;
+ struct mmc_queue *mq = (struct mmc_queue *)q->queuedata;
+ struct mmc_card *card = mq->card;
+ struct mmc_bkops_stats *bkops_stat;
+
+ if (!card)
+ goto fail;
+
+ bkops_stat = &card->bkops_info.bkops_stats;
+
+ test_pr_info("%s: Test results: bkops:(%d,%d,%d) hpi:%d, suspend:%d",
+ __func__,
+ bkops_stat->bkops_level[BKOPS_SEVERITY_1_INDEX],
+ bkops_stat->bkops_level[BKOPS_SEVERITY_2_INDEX],
+ bkops_stat->bkops_level[BKOPS_SEVERITY_3_INDEX],
+ bkops_stat->hpi,
+ bkops_stat->suspend);
+
+ switch (mbtd->test_info.testcase) {
+ case BKOPS_DELAYED_WORK_LEVEL_1:
+ if ((bkops_stat->bkops_level[BKOPS_SEVERITY_1_INDEX] == 1) &&
+ (bkops_stat->suspend == 1) &&
+ (bkops_stat->hpi == 0))
+ goto exit;
+ else
+ goto fail;
+ break;
+ case BKOPS_DELAYED_WORK_LEVEL_1_HPI:
+ if ((bkops_stat->bkops_level[BKOPS_SEVERITY_1_INDEX] == 1) &&
+ (bkops_stat->suspend == 0) &&
+ (bkops_stat->hpi == 1))
+ goto exit;
+ else
+ goto fail;
+ break;
+ case BKOPS_CANCEL_DELAYED_WORK:
+ if ((bkops_stat->bkops_level[BKOPS_SEVERITY_1_INDEX] == 0) &&
+ (bkops_stat->bkops_level[BKOPS_SEVERITY_2_INDEX] == 0) &&
+ (bkops_stat->bkops_level[BKOPS_SEVERITY_3_INDEX] == 0) &&
+ (bkops_stat->suspend == 0) &&
+ (bkops_stat->hpi == 0))
+ goto exit;
+ else
+ goto fail;
+ case BKOPS_URGENT_LEVEL_2:
+ case BKOPS_URGENT_LEVEL_2_TWO_REQS:
+ if ((bkops_stat->bkops_level[BKOPS_SEVERITY_2_INDEX] == 1) &&
+ (bkops_stat->suspend == 0) &&
+ (bkops_stat->hpi == 0))
+ goto exit;
+ else
+ goto fail;
+ case BKOPS_URGENT_LEVEL_3:
+ if ((bkops_stat->bkops_level[BKOPS_SEVERITY_3_INDEX] == 1) &&
+ (bkops_stat->suspend == 0) &&
+ (bkops_stat->hpi == 0))
+ goto exit;
+ else
+ goto fail;
+ default:
+ return -EINVAL;
+ }
+
+exit:
+ return 0;
+fail:
+ if (td->fs_wr_reqs_during_test) {
+ test_pr_info("%s: wr reqs during test, cancel the round",
+ __func__);
+ test_iosched_set_ignore_round(true);
+ return 0;
+ }
+
+ test_pr_info("%s: BKOPs statistics are not as expected, test failed",
+ __func__);
+ return -EINVAL;
+}
+
+static void bkops_end_io_final_fn(struct request *rq, int err)
+{
+ struct test_request *test_rq =
+ (struct test_request *)rq->elv.priv[0];
+ BUG_ON(!test_rq);
+
+ test_rq->req_completed = 1;
+ test_rq->req_result = err;
+
+ test_pr_info("%s: request %d completed, err=%d",
+ __func__, test_rq->req_id, err);
+
+ mbtd->bkops_stage = BKOPS_STAGE_4;
+ wake_up(&mbtd->bkops_wait_q);
+}
+
+static void bkops_end_io_fn(struct request *rq, int err)
+{
+ struct test_request *test_rq =
+ (struct test_request *)rq->elv.priv[0];
+ BUG_ON(!test_rq);
+
+ test_rq->req_completed = 1;
+ test_rq->req_result = err;
+
+ test_pr_info("%s: request %d completed, err=%d",
+ __func__, test_rq->req_id, err);
+ mbtd->bkops_stage = BKOPS_STAGE_2;
+ wake_up(&mbtd->bkops_wait_q);
+
+}
+
+static int prepare_bkops(struct test_data *td)
+{
+ int ret = 0;
+ struct request_queue *q = td->req_q;
+ struct mmc_queue *mq = (struct mmc_queue *)q->queuedata;
+ struct mmc_card *card = mq->card;
+ struct mmc_bkops_stats *bkops_stat;
+
+ if (!card)
+ return -EINVAL;
+
+ bkops_stat = &card->bkops_info.bkops_stats;
+
+ if (!card->ext_csd.bkops_en) {
+ test_pr_err("%s: BKOPS is not enabled by card or host)",
+ __func__);
+ return -ENOTSUPP;
+ }
+ if (mmc_card_doing_bkops(card)) {
+ test_pr_err("%s: BKOPS in progress, try later", __func__);
+ return -EAGAIN;
+ }
+
+ mmc_blk_init_bkops_statistics(card);
+
+ if ((mbtd->test_info.testcase == BKOPS_URGENT_LEVEL_2) ||
+ (mbtd->test_info.testcase == BKOPS_URGENT_LEVEL_2_TWO_REQS) ||
+ (mbtd->test_info.testcase == BKOPS_URGENT_LEVEL_3))
+ mq->err_check_fn = test_err_check;
+ mbtd->err_check_counter = 0;
+
+ return ret;
+}
+
+static int run_bkops(struct test_data *td)
+{
+ int ret = 0;
+ struct request_queue *q = td->req_q;
+ struct mmc_queue *mq = (struct mmc_queue *)q->queuedata;
+ struct mmc_card *card = mq->card;
+ struct mmc_bkops_stats *bkops_stat;
+
+ if (!card)
+ return -EINVAL;
+
+ bkops_stat = &card->bkops_info.bkops_stats;
+
+ switch (mbtd->test_info.testcase) {
+ case BKOPS_DELAYED_WORK_LEVEL_1:
+ bkops_stat->ignore_card_bkops_status = true;
+ card->ext_csd.raw_bkops_status = 1;
+ card->bkops_info.sectors_changed =
+ card->bkops_info.min_sectors_to_queue_delayed_work + 1;
+ mbtd->bkops_stage = BKOPS_STAGE_1;
+
+ __blk_run_queue(q);
+ /* this long sleep makes sure the host starts bkops and
+ also, gets into suspend */
+ msleep(10000);
+
+ bkops_stat->ignore_card_bkops_status = false;
+ card->ext_csd.raw_bkops_status = 0;
+
+ test_iosched_mark_test_completion();
+ break;
+
+ case BKOPS_DELAYED_WORK_LEVEL_1_HPI:
+ bkops_stat->ignore_card_bkops_status = true;
+ card->ext_csd.raw_bkops_status = 1;
+ card->bkops_info.sectors_changed =
+ card->bkops_info.min_sectors_to_queue_delayed_work + 1;
+ mbtd->bkops_stage = BKOPS_STAGE_1;
+
+ __blk_run_queue(q);
+ msleep(card->bkops_info.delay_ms);
+
+ ret = test_iosched_add_wr_rd_test_req(0, WRITE,
+ td->start_sector,
+ TEST_REQUEST_NUM_OF_BIOS,
+ TEST_PATTERN_5A,
+ bkops_end_io_final_fn);
+ if (ret) {
+ test_pr_err("%s: failed to add a write request",
+ __func__);
+ ret = -EINVAL;
+ break;
+ }
+
+ td->next_req = list_entry(td->test_queue.prev,
+ struct test_request, queuelist);
+ __blk_run_queue(q);
+ wait_event(mbtd->bkops_wait_q,
+ mbtd->bkops_stage == BKOPS_STAGE_4);
+ bkops_stat->ignore_card_bkops_status = false;
+
+ test_iosched_mark_test_completion();
+ break;
+
+ case BKOPS_CANCEL_DELAYED_WORK:
+ bkops_stat->ignore_card_bkops_status = true;
+ card->ext_csd.raw_bkops_status = 1;
+ card->bkops_info.sectors_changed =
+ card->bkops_info.min_sectors_to_queue_delayed_work + 1;
+ mbtd->bkops_stage = BKOPS_STAGE_1;
+
+ __blk_run_queue(q);
+
+ ret = test_iosched_add_wr_rd_test_req(0, WRITE,
+ td->start_sector,
+ TEST_REQUEST_NUM_OF_BIOS,
+ TEST_PATTERN_5A,
+ bkops_end_io_final_fn);
+ if (ret) {
+ test_pr_err("%s: failed to add a write request",
+ __func__);
+ ret = -EINVAL;
+ break;
+ }
+
+ td->next_req = list_entry(td->test_queue.prev,
+ struct test_request, queuelist);
+ __blk_run_queue(q);
+ wait_event(mbtd->bkops_wait_q,
+ mbtd->bkops_stage == BKOPS_STAGE_4);
+ bkops_stat->ignore_card_bkops_status = false;
+
+ test_iosched_mark_test_completion();
+ break;
+
+ case BKOPS_URGENT_LEVEL_2:
+ case BKOPS_URGENT_LEVEL_3:
+ bkops_stat->ignore_card_bkops_status = true;
+ if (mbtd->test_info.testcase == BKOPS_URGENT_LEVEL_2)
+ card->ext_csd.raw_bkops_status = 2;
+ else
+ card->ext_csd.raw_bkops_status = 3;
+ mbtd->bkops_stage = BKOPS_STAGE_1;
+
+ ret = test_iosched_add_wr_rd_test_req(0, WRITE,
+ td->start_sector,
+ TEST_REQUEST_NUM_OF_BIOS,
+ TEST_PATTERN_5A,
+ bkops_end_io_fn);
+ if (ret) {
+ test_pr_err("%s: failed to add a write request",
+ __func__);
+ ret = -EINVAL;
+ break;
+ }
+
+ td->next_req = list_entry(td->test_queue.prev,
+ struct test_request, queuelist);
+ __blk_run_queue(q);
+ wait_event(mbtd->bkops_wait_q,
+ mbtd->bkops_stage == BKOPS_STAGE_2);
+ card->ext_csd.raw_bkops_status = 0;
+
+ ret = test_iosched_add_wr_rd_test_req(0, WRITE,
+ td->start_sector,
+ TEST_REQUEST_NUM_OF_BIOS,
+ TEST_PATTERN_5A,
+ bkops_end_io_final_fn);
+ if (ret) {
+ test_pr_err("%s: failed to add a write request",
+ __func__);
+ ret = -EINVAL;
+ break;
+ }
+
+ td->next_req = list_entry(td->test_queue.prev,
+ struct test_request, queuelist);
+ __blk_run_queue(q);
+
+ wait_event(mbtd->bkops_wait_q,
+ mbtd->bkops_stage == BKOPS_STAGE_4);
+
+ bkops_stat->ignore_card_bkops_status = false;
+ test_iosched_mark_test_completion();
+ break;
+
+ case BKOPS_URGENT_LEVEL_2_TWO_REQS:
+ mq->wr_packing_enabled = false;
+ bkops_stat->ignore_card_bkops_status = true;
+ card->ext_csd.raw_bkops_status = 2;
+ mbtd->bkops_stage = BKOPS_STAGE_1;
+
+ ret = test_iosched_add_wr_rd_test_req(0, WRITE,
+ td->start_sector,
+ TEST_REQUEST_NUM_OF_BIOS,
+ TEST_PATTERN_5A,
+ NULL);
+ if (ret) {
+ test_pr_err("%s: failed to add a write request",
+ __func__);
+ ret = -EINVAL;
+ break;
+ }
+
+ ret = test_iosched_add_wr_rd_test_req(0, WRITE,
+ td->start_sector,
+ TEST_REQUEST_NUM_OF_BIOS,
+ TEST_PATTERN_5A,
+ bkops_end_io_fn);
+ if (ret) {
+ test_pr_err("%s: failed to add a write request",
+ __func__);
+ ret = -EINVAL;
+ break;
+ }
+
+ td->next_req = list_entry(td->test_queue.next,
+ struct test_request, queuelist);
+ __blk_run_queue(q);
+ wait_event(mbtd->bkops_wait_q,
+ mbtd->bkops_stage == BKOPS_STAGE_2);
+ card->ext_csd.raw_bkops_status = 0;
+
+ ret = test_iosched_add_wr_rd_test_req(0, WRITE,
+ td->start_sector,
+ TEST_REQUEST_NUM_OF_BIOS,
+ TEST_PATTERN_5A,
+ bkops_end_io_final_fn);
+ if (ret) {
+ test_pr_err("%s: failed to add a write request",
+ __func__);
+ ret = -EINVAL;
+ break;
+ }
+
+ td->next_req = list_entry(td->test_queue.prev,
+ struct test_request, queuelist);
+ __blk_run_queue(q);
+
+ wait_event(mbtd->bkops_wait_q,
+ mbtd->bkops_stage == BKOPS_STAGE_4);
+
+ bkops_stat->ignore_card_bkops_status = false;
+ test_iosched_mark_test_completion();
+
+ break;
+ default:
+ test_pr_err("%s: wrong testcase: %d", __func__,
+ mbtd->test_info.testcase);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static bool message_repeat;
+static int test_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ message_repeat = 1;
+ return 0;
+}
+
+/* send_packing TEST */
+static ssize_t send_write_packing_test_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int ret = 0;
+ int i = 0;
+ int number = -1;
+ int j = 0;
+
+ test_pr_info("%s: -- send_write_packing TEST --", __func__);
+
+ sscanf(buf, "%d", &number);
+
+ if (number <= 0)
+ number = 1;
+
+
+ mbtd->test_group = TEST_SEND_WRITE_PACKING_GROUP;
+
+ if (validate_packed_commands_settings())
+ return count;
+
+ if (mbtd->random_test_seed > 0)
+ test_pr_info("%s: Test seed: %d", __func__,
+ mbtd->random_test_seed);
+
+ memset(&mbtd->test_info, 0, sizeof(struct test_info));
+
+ mbtd->test_info.data = mbtd;
+ mbtd->test_info.prepare_test_fn = prepare_test;
+ mbtd->test_info.check_test_result_fn = check_wr_packing_statistics;
+ mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+ mbtd->test_info.post_test_fn = post_test;
+
+ for (i = 0; i < number; ++i) {
+ test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+ test_pr_info("%s: ====================", __func__);
+
+ for (j = SEND_WRITE_PACKING_MIN_TESTCASE;
+ j <= SEND_WRITE_PACKING_MAX_TESTCASE; j++) {
+
+ mbtd->test_info.testcase = j;
+ mbtd->is_random = RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret)
+ break;
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+ mbtd->test_info.testcase = j;
+ mbtd->is_random = NON_RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret)
+ break;
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+ }
+ }
+
+ test_pr_info("%s: Completed all the test cases.", __func__);
+
+ return count;
+}
+
+static ssize_t send_write_packing_test_read(struct file *file,
+ char __user *buffer,
+ size_t count,
+ loff_t *offset)
+{
+ memset((void *)buffer, 0, count);
+
+ snprintf(buffer, count,
+ "\nsend_write_packing_test\n"
+ "=========\n"
+ "Description:\n"
+ "This test checks the following scenarios\n"
+ "- Pack due to FLUSH message\n"
+ "- Pack due to FLUSH after threshold writes\n"
+ "- Pack due to READ message\n"
+ "- Pack due to READ after threshold writes\n"
+ "- Pack due to empty queue\n"
+ "- Pack due to threshold writes\n"
+ "- Pack due to one over threshold writes\n");
+
+ if (message_repeat == 1) {
+ message_repeat = 0;
+ return strnlen(buffer, count);
+ } else {
+ return 0;
+ }
+}
+
+const struct file_operations send_write_packing_test_ops = {
+ .open = test_open,
+ .write = send_write_packing_test_write,
+ .read = send_write_packing_test_read,
+};
+
+/* err_check TEST */
+static ssize_t err_check_test_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int ret = 0;
+ int i = 0;
+ int number = -1;
+ int j = 0;
+
+ test_pr_info("%s: -- err_check TEST --", __func__);
+
+ sscanf(buf, "%d", &number);
+
+ if (number <= 0)
+ number = 1;
+
+ mbtd->test_group = TEST_ERR_CHECK_GROUP;
+
+ if (validate_packed_commands_settings())
+ return count;
+
+ if (mbtd->random_test_seed > 0)
+ test_pr_info("%s: Test seed: %d", __func__,
+ mbtd->random_test_seed);
+
+ memset(&mbtd->test_info, 0, sizeof(struct test_info));
+
+ mbtd->test_info.data = mbtd;
+ mbtd->test_info.prepare_test_fn = prepare_test;
+ mbtd->test_info.check_test_result_fn = check_wr_packing_statistics;
+ mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+ mbtd->test_info.post_test_fn = post_test;
+
+ for (i = 0; i < number; ++i) {
+ test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+ test_pr_info("%s: ====================", __func__);
+
+ for (j = ERR_CHECK_MIN_TESTCASE;
+ j <= ERR_CHECK_MAX_TESTCASE ; j++) {
+ mbtd->test_info.testcase = j;
+ mbtd->is_random = RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret)
+ break;
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+ mbtd->test_info.testcase = j;
+ mbtd->is_random = NON_RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret)
+ break;
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+ }
+ }
+
+ test_pr_info("%s: Completed all the test cases.", __func__);
+
+ return count;
+}
+
+static ssize_t err_check_test_read(struct file *file,
+ char __user *buffer,
+ size_t count,
+ loff_t *offset)
+{
+ memset((void *)buffer, 0, count);
+
+ snprintf(buffer, count,
+ "\nerr_check_TEST\n"
+ "=========\n"
+ "Description:\n"
+ "This test checks the following scenarios\n"
+ "- Return ABORT\n"
+ "- Return PARTIAL followed by success\n"
+ "- Return PARTIAL followed by abort\n"
+ "- Return PARTIAL multiple times until success\n"
+ "- Return PARTIAL with fail index = threshold\n"
+ "- Return RETRY\n"
+ "- Return CMD_ERR\n"
+ "- Return DATA_ERR\n");
+
+ if (message_repeat == 1) {
+ message_repeat = 0;
+ return strnlen(buffer, count);
+ } else {
+ return 0;
+ }
+}
+
+const struct file_operations err_check_test_ops = {
+ .open = test_open,
+ .write = err_check_test_write,
+ .read = err_check_test_read,
+};
+
+/* send_invalid_packed TEST */
+static ssize_t send_invalid_packed_test_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int ret = 0;
+ int i = 0;
+ int number = -1;
+ int j = 0;
+ int num_of_failures = 0;
+
+ test_pr_info("%s: -- send_invalid_packed TEST --", __func__);
+
+ sscanf(buf, "%d", &number);
+
+ if (number <= 0)
+ number = 1;
+
+ mbtd->test_group = TEST_SEND_INVALID_GROUP;
+
+ if (validate_packed_commands_settings())
+ return count;
+
+ if (mbtd->random_test_seed > 0)
+ test_pr_info("%s: Test seed: %d", __func__,
+ mbtd->random_test_seed);
+
+ memset(&mbtd->test_info, 0, sizeof(struct test_info));
+
+ mbtd->test_info.data = mbtd;
+ mbtd->test_info.prepare_test_fn = prepare_test;
+ mbtd->test_info.check_test_result_fn = check_wr_packing_statistics;
+ mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+ mbtd->test_info.post_test_fn = post_test;
+
+ for (i = 0; i < number; ++i) {
+ test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+ test_pr_info("%s: ====================", __func__);
+
+ for (j = INVALID_CMD_MIN_TESTCASE;
+ j <= INVALID_CMD_MAX_TESTCASE ; j++) {
+
+ mbtd->test_info.testcase = j;
+ mbtd->is_random = RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret)
+ num_of_failures++;
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+
+ mbtd->test_info.testcase = j;
+ mbtd->is_random = NON_RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret)
+ num_of_failures++;
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+ }
+ }
+
+ test_pr_info("%s: Completed all the test cases.", __func__);
+
+ if (num_of_failures > 0) {
+ test_iosched_set_test_result(TEST_FAILED);
+ test_pr_err(
+ "There were %d failures during the test, TEST FAILED",
+ num_of_failures);
+ }
+ return count;
+}
+
+static ssize_t send_invalid_packed_test_read(struct file *file,
+ char __user *buffer,
+ size_t count,
+ loff_t *offset)
+{
+ memset((void *)buffer, 0, count);
+
+ snprintf(buffer, count,
+ "\nsend_invalid_packed_TEST\n"
+ "=========\n"
+ "Description:\n"
+ "This test checks the following scenarios\n"
+ "- Send an invalid header version\n"
+ "- Send the wrong write code\n"
+ "- Send an invalid R/W code\n"
+ "- Send wrong start address in header\n"
+ "- Send header with block_count smaller than actual\n"
+ "- Send header with block_count larger than actual\n"
+ "- Send header CMD23 packed bit set\n"
+ "- Send CMD23 with block count over threshold\n"
+ "- Send CMD23 with block_count equals zero\n"
+ "- Send CMD23 packed bit unset\n"
+ "- Send CMD23 reliable write bit set\n"
+ "- Send CMD23 bits [16-29] set\n"
+ "- Send CMD23 header block not in block_count\n");
+
+ if (message_repeat == 1) {
+ message_repeat = 0;
+ return strnlen(buffer, count);
+ } else {
+ return 0;
+ }
+}
+
+const struct file_operations send_invalid_packed_test_ops = {
+ .open = test_open,
+ .write = send_invalid_packed_test_write,
+ .read = send_invalid_packed_test_read,
+};
+
+/* packing_control TEST */
+static ssize_t write_packing_control_test_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int ret = 0;
+ int i = 0;
+ int number = -1;
+ int j = 0;
+ struct mmc_queue *mq = test_iosched_get_req_queue()->queuedata;
+ int max_num_requests = mq->card->ext_csd.max_packed_writes;
+ int test_successful = 1;
+
+ test_pr_info("%s: -- write_packing_control TEST --", __func__);
+
+ sscanf(buf, "%d", &number);
+
+ if (number <= 0)
+ number = 1;
+
+ test_pr_info("%s: max_num_requests = %d ", __func__,
+ max_num_requests);
+
+ memset(&mbtd->test_info, 0, sizeof(struct test_info));
+ mbtd->test_group = TEST_PACKING_CONTROL_GROUP;
+
+ if (validate_packed_commands_settings())
+ return count;
+
+ mbtd->test_info.data = mbtd;
+ mbtd->test_info.prepare_test_fn = prepare_test;
+ mbtd->test_info.check_test_result_fn = check_wr_packing_statistics;
+ mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+
+ for (i = 0; i < number; ++i) {
+ test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+ test_pr_info("%s: ====================", __func__);
+
+ for (j = PACKING_CONTROL_MIN_TESTCASE;
+ j <= PACKING_CONTROL_MAX_TESTCASE; j++) {
+
+ test_successful = 1;
+ mbtd->test_info.testcase = j;
+ mbtd->is_random = RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret) {
+ test_successful = 0;
+ break;
+ }
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+
+ mbtd->test_info.testcase = j;
+ mbtd->is_random = NON_RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret) {
+ test_successful = 0;
+ break;
+ }
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+ }
+
+ if (!test_successful)
+ break;
+ }
+
+ test_pr_info("%s: Completed all the test cases.", __func__);
+
+ return count;
+}
+
+static ssize_t write_packing_control_test_read(struct file *file,
+ char __user *buffer,
+ size_t count,
+ loff_t *offset)
+{
+ memset((void *)buffer, 0, count);
+
+ snprintf(buffer, count,
+ "\nwrite_packing_control_test\n"
+ "=========\n"
+ "Description:\n"
+ "This test checks the following scenarios\n"
+ "- Packing expected - one over trigger\n"
+ "- Packing expected - N over trigger\n"
+ "- Packing expected - N over trigger followed by read\n"
+ "- Packing expected - N over trigger followed by flush\n"
+ "- Packing expected - threshold over trigger FB by flush\n"
+ "- Packing not expected - less than trigger\n"
+ "- Packing not expected - trigger requests\n"
+ "- Packing not expected - trigger, read, trigger\n"
+ "- Mixed state - packing -> no packing -> packing\n"
+ "- Mixed state - no packing -> packing -> no packing\n");
+
+ if (message_repeat == 1) {
+ message_repeat = 0;
+ return strnlen(buffer, count);
+ } else {
+ return 0;
+ }
+}
+
+const struct file_operations write_packing_control_test_ops = {
+ .open = test_open,
+ .write = write_packing_control_test_write,
+ .read = write_packing_control_test_read,
+};
+
+static ssize_t write_discard_sanitize_test_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int ret = 0;
+ int i = 0;
+ int number = -1;
+
+ sscanf(buf, "%d", &number);
+ if (number <= 0)
+ number = 1;
+
+ test_pr_info("%s: -- write_discard_sanitize TEST --\n", __func__);
+
+ memset(&mbtd->test_info, 0, sizeof(struct test_info));
+
+ mbtd->test_group = TEST_GENERAL_GROUP;
+
+ mbtd->test_info.data = mbtd;
+ mbtd->test_info.prepare_test_fn = prepare_write_discard_sanitize_read;
+ mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+ mbtd->test_info.timeout_msec = SANITIZE_TEST_TIMEOUT;
+
+ for (i = 0 ; i < number ; ++i) {
+ test_pr_info("%s: Cycle # %d / %d\n", __func__, i+1, number);
+ test_pr_info("%s: ===================", __func__);
+
+ mbtd->test_info.testcase = TEST_WRITE_DISCARD_SANITIZE_READ;
+ ret = test_iosched_start_test(&mbtd->test_info);
+
+ if (ret)
+ break;
+ }
+
+ return count;
+}
+
+const struct file_operations write_discard_sanitize_test_ops = {
+ .open = test_open,
+ .write = write_discard_sanitize_test_write,
+};
+
+static ssize_t bkops_test_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int ret = 0;
+ int i = 0, j;
+ int number = -1;
+
+ test_pr_info("%s: -- bkops_test TEST --", __func__);
+
+ sscanf(buf, "%d", &number);
+
+ if (number <= 0)
+ number = 1;
+
+ mbtd->test_group = TEST_BKOPS_GROUP;
+
+ memset(&mbtd->test_info, 0, sizeof(struct test_info));
+
+ mbtd->test_info.data = mbtd;
+ mbtd->test_info.prepare_test_fn = prepare_bkops;
+ mbtd->test_info.check_test_result_fn = check_bkops_result;
+ mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+ mbtd->test_info.run_test_fn = run_bkops;
+ mbtd->test_info.timeout_msec = BKOPS_TEST_TIMEOUT;
+ mbtd->test_info.post_test_fn = bkops_post_test;
+
+ for (i = 0 ; i < number ; ++i) {
+ test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+ test_pr_info("%s: ===================", __func__);
+ for (j = BKOPS_MIN_TESTCASE ;
+ j <= BKOPS_MAX_TESTCASE ; j++) {
+ mbtd->test_info.testcase = j;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret)
+ break;
+ }
+ }
+
+ test_pr_info("%s: Completed all the test cases.", __func__);
+
+ return count;
+}
+
+static ssize_t bkops_test_read(struct file *file,
+ char __user *buffer,
+ size_t count,
+ loff_t *offset)
+{
+ memset((void *)buffer, 0, count);
+
+ snprintf(buffer, count,
+ "\nbkops_test\n========================\n"
+ "Description:\n"
+ "This test simulates BKOPS status from card\n"
+ "and verifies that:\n"
+ " - Starting BKOPS delayed work, level 1\n"
+ " - Starting BKOPS delayed work, level 1, with HPI\n"
+ " - Cancel starting BKOPS delayed work, "
+ " when a request is received\n"
+ " - Starting BKOPS urgent, level 2,3\n"
+ " - Starting BKOPS urgent with 2 requests\n");
+ return strnlen(buffer, count);
+}
+
+const struct file_operations bkops_test_ops = {
+ .open = test_open,
+ .write = bkops_test_write,
+ .read = bkops_test_read,
+};
+
+static void mmc_block_test_debugfs_cleanup(void)
+{
+ debugfs_remove(mbtd->debug.random_test_seed);
+ debugfs_remove(mbtd->debug.send_write_packing_test);
+ debugfs_remove(mbtd->debug.err_check_test);
+ debugfs_remove(mbtd->debug.send_invalid_packed_test);
+ debugfs_remove(mbtd->debug.packing_control_test);
+ debugfs_remove(mbtd->debug.discard_sanitize_test);
+ debugfs_remove(mbtd->debug.bkops_test);
+}
+
+static int mmc_block_test_debugfs_init(void)
+{
+ struct dentry *utils_root, *tests_root;
+
+ utils_root = test_iosched_get_debugfs_utils_root();
+ tests_root = test_iosched_get_debugfs_tests_root();
+
+ if (!utils_root || !tests_root)
+ return -EINVAL;
+
+ mbtd->debug.random_test_seed = debugfs_create_u32(
+ "random_test_seed",
+ S_IRUGO | S_IWUGO,
+ utils_root,
+ &mbtd->random_test_seed);
+
+ if (!mbtd->debug.random_test_seed)
+ goto err_nomem;
+
+ mbtd->debug.send_write_packing_test =
+ debugfs_create_file("send_write_packing_test",
+ S_IRUGO | S_IWUGO,
+ tests_root,
+ NULL,
+ &send_write_packing_test_ops);
+
+ if (!mbtd->debug.send_write_packing_test)
+ goto err_nomem;
+
+ mbtd->debug.err_check_test =
+ debugfs_create_file("err_check_test",
+ S_IRUGO | S_IWUGO,
+ tests_root,
+ NULL,
+ &err_check_test_ops);
+
+ if (!mbtd->debug.err_check_test)
+ goto err_nomem;
+
+ mbtd->debug.send_invalid_packed_test =
+ debugfs_create_file("send_invalid_packed_test",
+ S_IRUGO | S_IWUGO,
+ tests_root,
+ NULL,
+ &send_invalid_packed_test_ops);
+
+ if (!mbtd->debug.send_invalid_packed_test)
+ goto err_nomem;
+
+ mbtd->debug.packing_control_test = debugfs_create_file(
+ "packing_control_test",
+ S_IRUGO | S_IWUGO,
+ tests_root,
+ NULL,
+ &write_packing_control_test_ops);
+
+ if (!mbtd->debug.packing_control_test)
+ goto err_nomem;
+
+ mbtd->debug.discard_sanitize_test =
+ debugfs_create_file("write_discard_sanitize_test",
+ S_IRUGO | S_IWUGO,
+ tests_root,
+ NULL,
+ &write_discard_sanitize_test_ops);
+ if (!mbtd->debug.discard_sanitize_test) {
+ mmc_block_test_debugfs_cleanup();
+ return -ENOMEM;
+ }
+
+ mbtd->debug.bkops_test =
+ debugfs_create_file("bkops_test",
+ S_IRUGO | S_IWUGO,
+ tests_root,
+ NULL,
+ &bkops_test_ops);
+
+ if (!mbtd->debug.bkops_test)
+ goto err_nomem;
+
+ return 0;
+
+err_nomem:
+ mmc_block_test_debugfs_cleanup();
+ return -ENOMEM;
+}
+
+static void mmc_block_test_probe(void)
+{
+ struct request_queue *q = test_iosched_get_req_queue();
+ struct mmc_queue *mq;
+ int max_packed_reqs;
+
+ if (!q) {
+ test_pr_err("%s: NULL request queue", __func__);
+ return;
+ }
+
+ mq = q->queuedata;
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return;
+ }
+
+ max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+ mbtd->exp_packed_stats.packing_events =
+ kzalloc((max_packed_reqs + 1) *
+ sizeof(*mbtd->exp_packed_stats.packing_events),
+ GFP_KERNEL);
+
+ mmc_block_test_debugfs_init();
+}
+
+static void mmc_block_test_remove(void)
+{
+ mmc_block_test_debugfs_cleanup();
+}
+
+static int __init mmc_block_test_init(void)
+{
+ mbtd = kzalloc(sizeof(struct mmc_block_test_data), GFP_KERNEL);
+ if (!mbtd) {
+ test_pr_err("%s: failed to allocate mmc_block_test_data",
+ __func__);
+ return -ENODEV;
+ }
+
+ init_waitqueue_head(&mbtd->bkops_wait_q);
+ mbtd->bdt.init_fn = mmc_block_test_probe;
+ mbtd->bdt.exit_fn = mmc_block_test_remove;
+ INIT_LIST_HEAD(&mbtd->bdt.list);
+ test_iosched_register(&mbtd->bdt);
+
+ return 0;
+}
+
+static void __exit mmc_block_test_exit(void)
+{
+ test_iosched_unregister(&mbtd->bdt);
+ kfree(mbtd);
+}
+
+module_init(mmc_block_test_init);
+module_exit(mmc_block_test_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MMC block test");
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index d818fc4..cc91646 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -25,6 +25,13 @@
#define MMC_QUEUE_SUSPENDED (1 << 0)
/*
+ * Based on benchmark tests the default num of requests to trigger the write
+ * packing was determined, to keep the read latency as low as possible and
+ * manage to keep the high write throughput.
+ */
+#define DEFAULT_NUM_REQS_TO_START_PACK 17
+
+/*
* Prepare a MMC request. This just filters out odd stuff.
*/
static int mmc_prep_request(struct request_queue *q, struct request *req)
@@ -52,6 +59,7 @@
struct mmc_queue *mq = d;
struct request_queue *q = mq->queue;
struct request *req;
+ struct mmc_card *card = mq->card;
current->flags |= PF_MEMALLOC;
@@ -67,6 +75,17 @@
spin_unlock_irq(q->queue_lock);
if (req || mq->mqrq_prev->req) {
+ /*
+ * If this is the first request, BKOPs might be in
+ * progress and needs to be stopped before issuing the
+ * request
+ */
+ if (card->ext_csd.bkops_en &&
+ card->bkops_info.started_delayed_bkops) {
+ card->bkops_info.started_delayed_bkops = false;
+ mmc_stop_bkops(card);
+ }
+
set_current_state(TASK_RUNNING);
mq->issue_fn(mq, req);
} else {
@@ -74,6 +93,7 @@
set_current_state(TASK_RUNNING);
break;
}
+ mmc_start_delayed_bkops(card);
up(&mq->thread_sem);
schedule();
down(&mq->thread_sem);
@@ -189,6 +209,7 @@
mq->mqrq_cur = mqrq_cur;
mq->mqrq_prev = mqrq_prev;
mq->queue->queuedata = mq;
+ mq->num_wr_reqs_to_start_packing = DEFAULT_NUM_REQS_TO_START_PACK;
blk_queue_prep_rq(mq->queue, mmc_prep_request);
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq->queue);
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index 5e04938..a8c104e 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -12,6 +12,17 @@
struct mmc_data data;
};
+enum mmc_blk_status {
+ MMC_BLK_SUCCESS = 0,
+ MMC_BLK_PARTIAL,
+ MMC_BLK_CMD_ERR,
+ MMC_BLK_RETRY,
+ MMC_BLK_ABORT,
+ MMC_BLK_DATA_ERR,
+ MMC_BLK_ECC_ERR,
+ MMC_BLK_NOMEDIUM,
+};
+
enum mmc_packed_cmd {
MMC_PACKED_NONE = 0,
MMC_PACKED_WRITE,
@@ -45,6 +56,11 @@
struct mmc_queue_req mqrq[2];
struct mmc_queue_req *mqrq_cur;
struct mmc_queue_req *mqrq_prev;
+ bool wr_packing_enabled;
+ int num_of_potential_packed_wr_reqs;
+ int num_wr_reqs_to_start_packing;
+ int (*err_check_fn) (struct mmc_card *, struct mmc_async_req *);
+ void (*packed_test_fn) (struct request_queue *, struct mmc_queue_req *);
};
extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,
@@ -58,4 +74,6 @@
extern void mmc_queue_bounce_pre(struct mmc_queue_req *);
extern void mmc_queue_bounce_post(struct mmc_queue_req *);
+extern void print_mmc_packing_stats(struct mmc_card *card);
+
#endif
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 33f7d29..b24620b 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -254,6 +254,9 @@
card->dev.release = mmc_release_card;
card->dev.type = type;
+ spin_lock_init(&card->bkops_info.bkops_stats.lock);
+ spin_lock_init(&card->wr_pack_stats.lock);
+
return card;
}
@@ -356,6 +359,8 @@
device_del(&card->dev);
}
+ kfree(card->wr_pack_stats.packing_events);
+
put_device(&card->dev);
}
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 9b316bb..b91f3d1 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -77,6 +77,30 @@
removable,
"MMC/SD cards are removable and may be removed during suspend");
+#define MMC_UPDATE_BKOPS_STATS_HPI(stats) \
+ do { \
+ spin_lock(&stats.lock); \
+ if (stats.enabled) \
+ stats.hpi++; \
+ spin_unlock(&stats.lock); \
+ } while (0);
+#define MMC_UPDATE_BKOPS_STATS_SUSPEND(stats) \
+ do { \
+ spin_lock(&stats.lock); \
+ if (stats.enabled) \
+ stats.suspend++; \
+ spin_unlock(&stats.lock); \
+ } while (0);
+#define MMC_UPDATE_STATS_BKOPS_SEVERITY_LEVEL(stats, level) \
+ do { \
+ if (level <= 0 || level > BKOPS_NUM_OF_SEVERITY_LEVELS) \
+ break; \
+ spin_lock(&stats.lock); \
+ if (stats.enabled) \
+ stats.bkops_level[level-1]++; \
+ spin_unlock(&stats.lock); \
+ } while (0);
+
/*
* Internal function. Schedule delayed work in the MMC work queue.
*/
@@ -279,10 +303,66 @@
host->ops->request(host, mrq);
}
+void mmc_blk_init_bkops_statistics(struct mmc_card *card)
+{
+ int i;
+ struct mmc_bkops_stats *bkops_stats;
+
+ if (!card)
+ return;
+
+ bkops_stats = &card->bkops_info.bkops_stats;
+
+ spin_lock(&bkops_stats->lock);
+
+ for (i = 0 ; i < BKOPS_NUM_OF_SEVERITY_LEVELS ; ++i)
+ bkops_stats->bkops_level[i] = 0;
+
+ bkops_stats->suspend = 0;
+ bkops_stats->hpi = 0;
+ bkops_stats->enabled = true;
+
+ spin_unlock(&bkops_stats->lock);
+}
+EXPORT_SYMBOL(mmc_blk_init_bkops_statistics);
+
+/**
+ * mmc_start_delayed_bkops() - Start a delayed work to check for
+ * the need of non urgent BKOPS
+ *
+ * @card: MMC card to start BKOPS on
+ */
+void mmc_start_delayed_bkops(struct mmc_card *card)
+{
+ if (!card || !card->ext_csd.bkops_en || mmc_card_doing_bkops(card))
+ return;
+
+ if (card->bkops_info.sectors_changed <
+ card->bkops_info.min_sectors_to_queue_delayed_work)
+ return;
+
+ pr_debug("%s: %s: queueing delayed_bkops_work\n",
+ mmc_hostname(card->host), __func__);
+
+ card->bkops_info.sectors_changed = 0;
+
+ /*
+ * cancel_delayed_bkops_work will prevent a race condition between
+ * fetching a request by the mmcqd and the delayed work, in case
+ * it was removed from the queue work but not started yet
+ */
+ card->bkops_info.cancel_delayed_work = false;
+ card->bkops_info.started_delayed_bkops = true;
+ queue_delayed_work(system_nrt_wq, &card->bkops_info.dw,
+ msecs_to_jiffies(
+ card->bkops_info.delay_ms));
+}
+EXPORT_SYMBOL(mmc_start_delayed_bkops);
+
/**
* mmc_start_bkops - start BKOPS for supported cards
* @card: MMC card to start BKOPS
- * @form_exception: A flag to indicate if this function was
+ * @from_exception: A flag to indicate if this function was
* called due to an exception raised by the card
*
* Start background operations whenever requested.
@@ -296,25 +376,50 @@
bool use_busy_signal;
BUG_ON(!card);
-
- if (!card->ext_csd.bkops_en || mmc_card_doing_bkops(card))
- return;
-
- err = mmc_read_bkops_status(card);
- if (err) {
- pr_err("%s: Failed to read bkops status: %d\n",
- mmc_hostname(card->host), err);
- return;
- }
-
- if (!card->ext_csd.raw_bkops_status)
- return;
-
- if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2 &&
- from_exception)
+ if (!card->ext_csd.bkops_en)
return;
mmc_claim_host(card->host);
+
+ if ((card->bkops_info.cancel_delayed_work) && !from_exception) {
+ pr_debug("%s: %s: cancel_delayed_work was set, exit\n",
+ mmc_hostname(card->host), __func__);
+ card->bkops_info.cancel_delayed_work = false;
+ goto out;
+ }
+
+ if (mmc_card_doing_bkops(card)) {
+ pr_debug("%s: %s: already doing bkops, exit\n",
+ mmc_hostname(card->host), __func__);
+ goto out;
+ }
+
+ err = mmc_read_bkops_status(card);
+ if (err) {
+ pr_err("%s: %s: Failed to read bkops status: %d\n",
+ mmc_hostname(card->host), __func__, err);
+ goto out;
+ }
+
+ if (!card->ext_csd.raw_bkops_status)
+ goto out;
+
+ pr_info("%s: %s: card->ext_csd.raw_bkops_status = 0x%x\n",
+ mmc_hostname(card->host), __func__,
+ card->ext_csd.raw_bkops_status);
+
+ /*
+ * If the function was called due to exception but there is no need
+ * for urgent BKOPS, BKOPs will be performed by the delayed BKOPs
+ * work, before going to suspend
+ */
+ if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2 &&
+ from_exception) {
+ pr_debug("%s: %s: Level 1 from exception, exit",
+ mmc_hostname(card->host), __func__);
+ goto out;
+ }
+
if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) {
timeout = MMC_BKOPS_MAX_TIMEOUT;
use_busy_signal = true;
@@ -326,23 +431,120 @@
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BKOPS_START, 1, timeout, use_busy_signal);
if (err) {
- pr_warn("%s: Error %d starting bkops\n",
- mmc_hostname(card->host), err);
+ pr_warn("%s: %s: Error %d when starting bkops\n",
+ mmc_hostname(card->host), __func__, err);
goto out;
}
+ MMC_UPDATE_STATS_BKOPS_SEVERITY_LEVEL(card->bkops_info.bkops_stats,
+ card->ext_csd.raw_bkops_status);
/*
* For urgent bkops status (LEVEL_2 and more)
* bkops executed synchronously, otherwise
* the operation is in progress
*/
- if (!use_busy_signal)
+ if (!use_busy_signal) {
mmc_card_set_doing_bkops(card);
+ pr_debug("%s: %s: starting the polling thread\n",
+ mmc_hostname(card->host), __func__);
+ queue_work(system_nrt_wq,
+ &card->bkops_info.poll_for_completion);
+ }
+
out:
mmc_release_host(card->host);
}
EXPORT_SYMBOL(mmc_start_bkops);
+/**
+ * mmc_bkops_completion_polling() - Poll on the card status to
+ * wait for the non-blocking BKOPS completion
+ * @work: The completion polling work
+ *
+ * The on-going reading of the card status will prevent the card
+ * from getting into suspend while it is in the middle of
+ * performing BKOPS.
+ * Since the non blocking BKOPS can be interrupted by a fetched
+ * request we also check IF mmc_card_doing_bkops in each
+ * iteration.
+ */
+void mmc_bkops_completion_polling(struct work_struct *work)
+{
+ struct mmc_card *card = container_of(work, struct mmc_card,
+ bkops_info.poll_for_completion);
+ unsigned long timeout_jiffies = jiffies +
+ msecs_to_jiffies(BKOPS_COMPLETION_POLLING_TIMEOUT_MS);
+ u32 status;
+ int err;
+
+ /*
+ * Wait for the BKOPs to complete. Keep reading the status to prevent
+ * the host from getting into suspend
+ */
+ do {
+ mmc_claim_host(card->host);
+
+ if (!mmc_card_doing_bkops(card))
+ goto out;
+
+ err = mmc_send_status(card, &status);
+ if (err) {
+ pr_err("%s: error %d requesting status\n",
+ mmc_hostname(card->host), err);
+ goto out;
+ }
+
+ /*
+ * Some cards mishandle the status bits, so make sure to check
+ * both the busy indication and the card state.
+ */
+ if ((status & R1_READY_FOR_DATA) &&
+ (R1_CURRENT_STATE(status) != R1_STATE_PRG)) {
+ pr_debug("%s: %s: completed BKOPs, exit polling\n",
+ mmc_hostname(card->host), __func__);
+ mmc_card_clr_doing_bkops(card);
+ card->bkops_info.started_delayed_bkops = false;
+ goto out;
+ }
+
+ mmc_release_host(card->host);
+
+ /*
+ * Sleep before checking the card status again to allow the
+ * card to complete the BKOPs operation
+ */
+ msleep(BKOPS_COMPLETION_POLLING_INTERVAL_MS);
+ } while (time_before(jiffies, timeout_jiffies));
+
+ pr_err("%s: %s: exit polling due to timeout\n",
+ mmc_hostname(card->host), __func__);
+
+ return;
+out:
+ mmc_release_host(card->host);
+}
+
+/**
+ * mmc_start_idle_time_bkops() - check if a non urgent BKOPS is
+ * needed
+ * @work: The idle time BKOPS work
+ */
+void mmc_start_idle_time_bkops(struct work_struct *work)
+{
+ struct mmc_card *card = container_of(work, struct mmc_card,
+ bkops_info.dw.work);
+
+ /*
+ * Prevent a race condition between mmc_stop_bkops and the delayed
+ * BKOPS work in case the delayed work is executed on another CPU
+ */
+ if (card->bkops_info.cancel_delayed_work)
+ return;
+
+ mmc_start_bkops(card, false);
+}
+EXPORT_SYMBOL(mmc_start_idle_time_bkops);
+
static void mmc_wait_done(struct mmc_request *mrq)
{
complete(&mrq->completion);
@@ -458,8 +660,11 @@
if (host->card && mmc_card_mmc(host->card) &&
((mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1) ||
(mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1B)) &&
- (host->areq->mrq->cmd->resp[0] & R1_EXCEPTION_EVENT))
+ (host->areq->mrq->cmd->resp[0] & R1_EXCEPTION_EVENT)) {
mmc_start_bkops(host->card, true);
+ pr_debug("%s: %s: completed BKOPs due to exception",
+ mmc_hostname(host), __func__);
+ }
}
if (!err && areq)
@@ -470,7 +675,7 @@
/* Cancel a prepared request if it was not started. */
if ((err || start_err) && areq)
- mmc_post_req(host, areq->mrq, -EINVAL);
+ mmc_post_req(host, areq->mrq, -EINVAL);
if (err)
host->areq = NULL;
@@ -599,6 +804,19 @@
int err = 0;
BUG_ON(!card);
+
+ mmc_claim_host(card->host);
+
+ /*
+ * Notify the delayed work to be cancelled, in case it was already
+ * removed from the queue, but was not started yet
+ */
+ card->bkops_info.cancel_delayed_work = true;
+ if (delayed_work_pending(&card->bkops_info.dw))
+ cancel_delayed_work_sync(&card->bkops_info.dw);
+ if (!mmc_card_doing_bkops(card))
+ goto out;
+
err = mmc_interrupt_hpi(card);
/*
@@ -610,6 +828,10 @@
err = 0;
}
+ MMC_UPDATE_BKOPS_STATS_HPI(card->bkops_info.bkops_stats);
+
+out:
+ mmc_release_host(card->host);
return err;
}
EXPORT_SYMBOL(mmc_stop_bkops);
@@ -629,6 +851,12 @@
return -ENOMEM;
}
+ if (card->bkops_info.bkops_stats.ignore_card_bkops_status) {
+ pr_debug("%s: skipping read raw_bkops_status in unittest mode",
+ __func__);
+ return 0;
+ }
+
mmc_claim_host(card->host);
err = mmc_send_ext_csd(card, ext_csd);
mmc_release_host(card->host);
@@ -1273,6 +1501,49 @@
mmc_set_ios(host);
mmc_host_clk_release(host);
}
+
+static void mmc_poweroff_notify(struct mmc_host *host)
+{
+ struct mmc_card *card;
+ unsigned int timeout;
+ unsigned int notify_type = EXT_CSD_NO_POWER_NOTIFICATION;
+ int err = 0;
+
+ card = host->card;
+ mmc_claim_host(host);
+
+ /*
+ * Send power notify command only if card
+ * is mmc and notify state is powered ON
+ */
+ if (card && mmc_card_mmc(card) &&
+ (card->poweroff_notify_state == MMC_POWERED_ON)) {
+
+ if (host->power_notify_type == MMC_HOST_PW_NOTIFY_SHORT) {
+ notify_type = EXT_CSD_POWER_OFF_SHORT;
+ timeout = card->ext_csd.generic_cmd6_time;
+ card->poweroff_notify_state = MMC_POWEROFF_SHORT;
+ } else {
+ notify_type = EXT_CSD_POWER_OFF_LONG;
+ timeout = card->ext_csd.power_off_longtime;
+ card->poweroff_notify_state = MMC_POWEROFF_LONG;
+ }
+
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_POWER_OFF_NOTIFICATION,
+ notify_type, timeout);
+
+ if (err && err != -EBADMSG)
+ pr_err("Device failed to respond within %d poweroff time.",
+ timeout);
+ pr_err("Forcefully powering down the device\n");
+
+ /* Set the card state to no notification after the poweroff */
+ card->poweroff_notify_state = MMC_NO_POWER_NOTIFICATION;
+ }
+ mmc_release_host(host);
+}
+
/*
* Apply power to the MMC stack. This is a two-stage process.
* First, we enable power to the card without the clock running.
@@ -1330,11 +1601,29 @@
void mmc_power_off(struct mmc_host *host)
{
+ int err = 0;
mmc_host_clk_hold(host);
host->ios.clock = 0;
host->ios.vdd = 0;
+ mmc_poweroff_notify(host);
+ /*
+ * For eMMC 4.5 device send AWAKE command before
+ * POWER_OFF_NOTIFY command, because in sleep state
+ * eMMC 4.5 devices respond to only RESET and AWAKE cmd
+ */
+ if (host->card && mmc_card_is_sleep(host->card) &&
+ host->bus_ops->resume) {
+ err = host->bus_ops->resume(host);
+
+ if (!err)
+ mmc_poweroff_notify(host);
+ else
+ pr_warning("%s: error %d during resume",
+ mmc_hostname(host), err);
+ pr_warning(" (continue with poweroff sequence)\n");
+ }
/*
* Reset ocr mask to be the highest possible voltage supported for
@@ -1865,15 +2154,6 @@
}
EXPORT_SYMBOL(mmc_can_secure_erase_trim);
-int mmc_can_poweroff_notify(const struct mmc_card *card)
-{
- return card &&
- mmc_card_mmc(card) &&
- card->host->bus_ops->poweroff_notify &&
- (card->poweroff_notify_state == MMC_POWERED_ON);
-}
-EXPORT_SYMBOL(mmc_can_poweroff_notify);
-
int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
unsigned int nr)
{
@@ -2266,15 +2546,6 @@
mmc_bus_get(host);
if (host->bus_ops && !host->bus_dead) {
- mmc_claim_host(host);
- if (mmc_can_poweroff_notify(host->card)) {
- int err = host->bus_ops->poweroff_notify(host,
- MMC_PW_OFF_NOTIFY_LONG);
- if (err)
- pr_info("%s: error [%d] in poweroff notify\n",
- mmc_hostname(host), err);
- }
- mmc_release_host(host);
/* Calling bus_ops->remove() with a claimed host can deadlock */
if (host->bus_ops->remove)
host->bus_ops->remove(host);
@@ -2310,15 +2581,6 @@
if (host->bus_ops->power_save)
ret = host->bus_ops->power_save(host);
- mmc_claim_host(host);
- if (mmc_can_poweroff_notify(host->card)) {
- int err = host->bus_ops->poweroff_notify(host,
- MMC_PW_OFF_NOTIFY_SHORT);
- if (err)
- pr_info("%s: error [%d] in poweroff notify\n",
- mmc_hostname(host), err);
- }
- mmc_release_host(host);
mmc_bus_put(host);
@@ -2361,11 +2623,8 @@
mmc_bus_get(host);
- if (host->bus_ops && !host->bus_dead && host->bus_ops->awake) {
+ if (host->bus_ops && !host->bus_dead && host->bus_ops->awake)
err = host->bus_ops->awake(host);
- if (!err)
- mmc_card_clr_sleep(host->card);
- }
mmc_bus_put(host);
@@ -2382,11 +2641,8 @@
mmc_bus_get(host);
- if (host->bus_ops && !host->bus_dead && host->bus_ops->sleep) {
+ if (host->bus_ops && !host->bus_dead && host->bus_ops->sleep)
err = host->bus_ops->sleep(host);
- if (!err)
- mmc_card_set_sleep(host->card);
- }
mmc_bus_put(host);
@@ -2514,6 +2770,8 @@
goto stop_bkops_err;
}
err = host->bus_ops->suspend(host);
+ MMC_UPDATE_BKOPS_STATS_SUSPEND(host->
+ card->bkops_info.bkops_stats);
}
if (!(host->card && mmc_card_sdio(host->card)))
mmc_release_host(host);
@@ -2615,15 +2873,13 @@
switch (mode) {
case PM_HIBERNATION_PREPARE:
case PM_SUSPEND_PREPARE:
- if (host->card && mmc_card_mmc(host->card) &&
- mmc_card_doing_bkops(host->card)) {
+ if (host->card && mmc_card_mmc(host->card)) {
err = mmc_stop_bkops(host->card);
if (err) {
pr_err("%s: didn't stop bkops\n",
mmc_hostname(host));
return err;
}
- mmc_card_clr_doing_bkops(host->card);
}
spin_lock_irqsave(&host->lock, flags);
@@ -2632,21 +2888,13 @@
break;
}
host->rescan_disable = 1;
+ host->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT;
spin_unlock_irqrestore(&host->lock, flags);
if (cancel_delayed_work_sync(&host->detect))
wake_unlock(&host->detect_wake_lock);
if (!host->bus_ops || host->bus_ops->suspend)
break;
- mmc_claim_host(host);
- if (mmc_can_poweroff_notify(host->card)) {
- int err = host->bus_ops->poweroff_notify(host,
- MMC_PW_OFF_NOTIFY_SHORT);
- if (err)
- pr_info("%s: error [%d] in poweroff notify\n",
- mmc_hostname(host), err);
- }
- mmc_release_host(host);
/* Calling bus_ops->remove() with a claimed host can deadlock */
if (host->bus_ops->remove)
@@ -2669,6 +2917,7 @@
break;
}
host->rescan_disable = 0;
+ host->power_notify_type = MMC_HOST_PW_NOTIFY_LONG;
spin_unlock_irqrestore(&host->lock, flags);
mmc_detect_change(host, 0);
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index 22f6043..85d2737 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -25,7 +25,6 @@
int (*power_save)(struct mmc_host *);
int (*power_restore)(struct mmc_host *);
int (*alive)(struct mmc_host *);
- int (*poweroff_notify)(struct mmc_host *, int notify);
};
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index 9ab5b17..ddb562e 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -318,6 +318,279 @@
.llseek = default_llseek,
};
+static int mmc_wr_pack_stats_open(struct inode *inode, struct file *filp)
+{
+ struct mmc_card *card = inode->i_private;
+
+ filp->private_data = card;
+ card->wr_pack_stats.print_in_read = 1;
+ return 0;
+}
+
+#define TEMP_BUF_SIZE 256
+static ssize_t mmc_wr_pack_stats_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct mmc_card *card = filp->private_data;
+ struct mmc_wr_pack_stats *pack_stats;
+ int i;
+ int max_num_of_packed_reqs = 0;
+ char *temp_buf;
+
+ if (!card)
+ return cnt;
+
+ if (!card->wr_pack_stats.print_in_read)
+ return 0;
+
+ if (!card->wr_pack_stats.enabled) {
+ pr_info("%s: write packing statistics are disabled\n",
+ mmc_hostname(card->host));
+ goto exit;
+ }
+
+ pack_stats = &card->wr_pack_stats;
+
+ if (!pack_stats->packing_events) {
+ pr_info("%s: NULL packing_events\n", mmc_hostname(card->host));
+ goto exit;
+ }
+
+ max_num_of_packed_reqs = card->ext_csd.max_packed_writes;
+
+ temp_buf = kmalloc(TEMP_BUF_SIZE, GFP_KERNEL);
+ if (!temp_buf)
+ goto exit;
+
+ spin_lock(&pack_stats->lock);
+
+ snprintf(temp_buf, TEMP_BUF_SIZE, "%s: write packing statistics:\n",
+ mmc_hostname(card->host));
+ strlcat(ubuf, temp_buf, cnt);
+
+ for (i = 1 ; i <= max_num_of_packed_reqs ; ++i) {
+ if (pack_stats->packing_events[i]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: Packed %d reqs - %d times\n",
+ mmc_hostname(card->host), i,
+ pack_stats->packing_events[i]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+ }
+
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: stopped packing due to the following reasons:\n",
+ mmc_hostname(card->host));
+ strlcat(ubuf, temp_buf, cnt);
+
+ if (pack_stats->pack_stop_reason[EXCEEDS_SEGMENTS]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: exceed max num of segments\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[EXCEEDS_SEGMENTS]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+ if (pack_stats->pack_stop_reason[EXCEEDS_SECTORS]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: exceed max num of sectors\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[EXCEEDS_SECTORS]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+ if (pack_stats->pack_stop_reason[WRONG_DATA_DIR]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: wrong data direction\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[WRONG_DATA_DIR]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+ if (pack_stats->pack_stop_reason[FLUSH_OR_DISCARD]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: flush or discard\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[FLUSH_OR_DISCARD]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+ if (pack_stats->pack_stop_reason[EMPTY_QUEUE]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: empty queue\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[EMPTY_QUEUE]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+ if (pack_stats->pack_stop_reason[REL_WRITE]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: rel write\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[REL_WRITE]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+ if (pack_stats->pack_stop_reason[THRESHOLD]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: Threshold\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[THRESHOLD]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+
+ if (pack_stats->pack_stop_reason[LARGE_SEC_ALIGN]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: Large sector alignment\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[LARGE_SEC_ALIGN]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+
+ spin_unlock(&pack_stats->lock);
+
+ kfree(temp_buf);
+
+ pr_info("%s", ubuf);
+
+exit:
+ if (card->wr_pack_stats.print_in_read == 1) {
+ card->wr_pack_stats.print_in_read = 0;
+ return strnlen(ubuf, cnt);
+ }
+
+ return 0;
+}
+
+static ssize_t mmc_wr_pack_stats_write(struct file *filp,
+ const char __user *ubuf, size_t cnt,
+ loff_t *ppos)
+{
+ struct mmc_card *card = filp->private_data;
+ int value;
+
+ if (!card)
+ return cnt;
+
+ sscanf(ubuf, "%d", &value);
+ if (value) {
+ mmc_blk_init_packed_statistics(card);
+ } else {
+ spin_lock(&card->wr_pack_stats.lock);
+ card->wr_pack_stats.enabled = false;
+ spin_unlock(&card->wr_pack_stats.lock);
+ }
+
+ return cnt;
+}
+
+static const struct file_operations mmc_dbg_wr_pack_stats_fops = {
+ .open = mmc_wr_pack_stats_open,
+ .read = mmc_wr_pack_stats_read,
+ .write = mmc_wr_pack_stats_write,
+};
+
+static int mmc_bkops_stats_open(struct inode *inode, struct file *filp)
+{
+ struct mmc_card *card = inode->i_private;
+
+ filp->private_data = card;
+
+ card->bkops_info.bkops_stats.print_stats = 1;
+ return 0;
+}
+
+static ssize_t mmc_bkops_stats_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct mmc_card *card = filp->private_data;
+ struct mmc_bkops_stats *bkops_stats;
+ int i;
+ char *temp_buf;
+
+ if (!card)
+ return cnt;
+
+ bkops_stats = &card->bkops_info.bkops_stats;
+
+ if (!bkops_stats->print_stats)
+ return 0;
+
+ if (!bkops_stats->enabled) {
+ pr_info("%s: bkops statistics are disabled\n",
+ mmc_hostname(card->host));
+ goto exit;
+ }
+
+ temp_buf = kmalloc(TEMP_BUF_SIZE, GFP_KERNEL);
+ if (!temp_buf)
+ goto exit;
+
+ spin_lock(&bkops_stats->lock);
+
+ memset(ubuf, 0, cnt);
+
+ snprintf(temp_buf, TEMP_BUF_SIZE, "%s: bkops statistics:\n",
+ mmc_hostname(card->host));
+ strlcat(ubuf, temp_buf, cnt);
+
+ for (i = 0 ; i < BKOPS_NUM_OF_SEVERITY_LEVELS ; ++i) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: BKOPS: due to level %d: %u\n",
+ mmc_hostname(card->host), i, bkops_stats->bkops_level[i]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: BKOPS: stopped due to HPI: %u\n",
+ mmc_hostname(card->host), bkops_stats->hpi);
+ strlcat(ubuf, temp_buf, cnt);
+
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: BKOPS: how many time host was suspended: %u\n",
+ mmc_hostname(card->host), bkops_stats->suspend);
+ strlcat(ubuf, temp_buf, cnt);
+
+ spin_unlock(&bkops_stats->lock);
+
+ kfree(temp_buf);
+
+ pr_info("%s", ubuf);
+
+exit:
+ if (bkops_stats->print_stats == 1) {
+ bkops_stats->print_stats = 0;
+ return strnlen(ubuf, cnt);
+ }
+
+ return 0;
+}
+
+static ssize_t mmc_bkops_stats_write(struct file *filp,
+ const char __user *ubuf, size_t cnt,
+ loff_t *ppos)
+{
+ struct mmc_card *card = filp->private_data;
+ int value;
+ struct mmc_bkops_stats *bkops_stats;
+
+ if (!card)
+ return cnt;
+
+ bkops_stats = &card->bkops_info.bkops_stats;
+
+ sscanf(ubuf, "%d", &value);
+ if (value) {
+ mmc_blk_init_bkops_statistics(card);
+ } else {
+ spin_lock(&bkops_stats->lock);
+ bkops_stats->enabled = false;
+ spin_unlock(&bkops_stats->lock);
+ }
+
+ return cnt;
+}
+
+static const struct file_operations mmc_dbg_bkops_stats_fops = {
+ .open = mmc_bkops_stats_open,
+ .read = mmc_bkops_stats_read,
+ .write = mmc_bkops_stats_write,
+};
+
void mmc_add_card_debugfs(struct mmc_card *card)
{
struct mmc_host *host = card->host;
@@ -350,6 +623,18 @@
&mmc_dbg_ext_csd_fops))
goto err;
+ if (mmc_card_mmc(card) && (card->ext_csd.rev >= 6) &&
+ (card->host->caps2 & MMC_CAP2_PACKED_WR))
+ if (!debugfs_create_file("wr_pack_stats", S_IRUSR, root, card,
+ &mmc_dbg_wr_pack_stats_fops))
+ goto err;
+
+ if (mmc_card_mmc(card) && (card->ext_csd.rev >= 6) &&
+ card->ext_csd.bkops_en)
+ if (!debugfs_create_file("bkops_stats", S_IRUSR, root, card,
+ &mmc_dbg_bkops_stats_fops))
+ goto err;
+
return;
err:
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index e2172c5..50af7fa 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1297,6 +1297,48 @@
} else {
card->ext_csd.packed_event_en = 1;
}
+
+ }
+
+ if (!oldcard) {
+ if ((host->caps2 & MMC_CAP2_PACKED_CMD) &&
+ (card->ext_csd.max_packed_writes > 0)) {
+ /*
+ * We would like to keep the statistics in an index
+ * that equals the num of packed requests
+ * (1 to max_packed_writes)
+ */
+ card->wr_pack_stats.packing_events = kzalloc(
+ (card->ext_csd.max_packed_writes + 1) *
+ sizeof(*card->wr_pack_stats.packing_events),
+ GFP_KERNEL);
+ if (!card->wr_pack_stats.packing_events)
+ goto free_card;
+ }
+
+ if (card->ext_csd.bkops_en) {
+ INIT_DELAYED_WORK(&card->bkops_info.dw,
+ mmc_start_idle_time_bkops);
+ INIT_WORK(&card->bkops_info.poll_for_completion,
+ mmc_bkops_completion_polling);
+
+ /*
+ * Calculate the time to start the BKOPs checking.
+ * The idle time of the host controller should be taken
+ * into account in order to prevent a race condition
+ * before starting BKOPs and going into suspend.
+ * If the host controller didn't set its idle time,
+ * a default value is used.
+ */
+ card->bkops_info.delay_ms = MMC_IDLE_BKOPS_TIME_MS;
+ if (card->bkops_info.host_suspend_tout_ms)
+ card->bkops_info.delay_ms = min(
+ card->bkops_info.delay_ms,
+ card->bkops_info.host_suspend_tout_ms/2);
+
+ card->bkops_info.min_sectors_to_queue_delayed_work =
+ BKOPS_MIN_SECTORS_TO_QUEUE_DELAYED_WORK;
+ }
}
if (!oldcard)
@@ -1314,40 +1356,6 @@
return err;
}
-static int mmc_poweroff_notify(struct mmc_host *host, int notify)
-{
- struct mmc_card *card;
- unsigned int timeout;
- unsigned int notify_type = EXT_CSD_NO_POWER_NOTIFICATION;
- int err;
-
- card = host->card;
-
- if (notify == MMC_PW_OFF_NOTIFY_SHORT) {
- notify_type = EXT_CSD_POWER_OFF_SHORT;
- timeout = card->ext_csd.generic_cmd6_time;
- } else if (notify == MMC_PW_OFF_NOTIFY_LONG) {
- notify_type = EXT_CSD_POWER_OFF_LONG;
- timeout = card->ext_csd.power_off_longtime;
- } else {
- pr_info("%s: mmc_poweroff_notify called "
- "with notify type %d\n", mmc_hostname(host), notify);
- return -EINVAL;
- }
-
- err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_POWER_OFF_NOTIFICATION,
- notify_type, timeout);
-
- if (err)
- pr_err("%s: Device failed to respond within %d "
- "poweroff timeout.\n", mmc_hostname(host), timeout);
- else
- card->poweroff_notify_state =
- MMC_NO_POWER_NOTIFICATION;
-
- return err;
-}
/*
* Host is being removed. Free up the current card.
*/
@@ -1411,26 +1419,13 @@
BUG_ON(!host->card);
mmc_claim_host(host);
- if (mmc_can_poweroff_notify(host->card) &&
- (host->caps2 & MMC_CAP2_POWER_OFF_VCCQ_DURING_SUSPEND)) {
- err = mmc_poweroff_notify(host, MMC_PW_OFF_NOTIFY_SHORT);
- } else {
- if (mmc_card_can_sleep(host))
- /*
- * If sleep command has error it doesn't mean host
- * cannot suspend, but a deeper low power state
- * transition for the card has failed. Ignore
- * sleep errors so that the suspend is not aborted.
- * In error case, mmc_resume() takes care of
- * complete intialization of the card.
- */
- mmc_card_sleep(host);
- else if (!mmc_host_is_spi(host))
- mmc_deselect_cards(host);
- }
- if (!err)
- host->card->state &=
- ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
+ if (mmc_card_can_sleep(host)) {
+ err = mmc_card_sleep(host);
+ if (!err)
+ mmc_card_set_sleep(host->card);
+ } else if (!mmc_host_is_spi(host))
+ mmc_deselect_cards(host);
+ host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
mmc_release_host(host);
return err;
@@ -1508,7 +1503,6 @@
.resume = NULL,
.power_restore = mmc_power_restore,
.alive = mmc_alive,
- .poweroff_notify = mmc_poweroff_notify,
};
static const struct mmc_bus_ops mmc_ops_unsafe = {
@@ -1520,7 +1514,6 @@
.resume = mmc_resume,
.power_restore = mmc_power_restore,
.alive = mmc_alive,
- .poweroff_notify = mmc_poweroff_notify,
};
static void mmc_attach_bus_ops(struct mmc_host *host)
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 0c3f994..da38122 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -500,6 +500,10 @@
help
Select Y to enable Slot 3.
+config MMC_MSM_SDC3_POLLING
+ boolean "Qualcomm SDC3 support"
+ depends on MMC_MSM
+
config MMC_MSM_SDC3_8_BIT_SUPPORT
boolean "Qualcomm SDC3 8bit support"
depends on MMC_MSM_SDC3_SUPPORT
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index bd374f6..ab3fc46 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -1790,6 +1790,11 @@
if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED)
mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
+ if (mmc->caps2 & MMC_CAP2_POWEROFF_NOTIFY)
+ mmc->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT;
+ else
+ mmc->power_notify_type = MMC_HOST_PW_NOTIFY_NONE;
+
if (host->pdata->blk_settings) {
mmc->max_segs = host->pdata->blk_settings->max_segs;
mmc->max_blk_size = host->pdata->blk_settings->max_blk_size;
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index d910d6c..29413ab 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -3,7 +3,8 @@
*
* Copyright (C) 2007 Google Inc,
* Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
- * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
+ *
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -1821,7 +1822,7 @@
*/
wake_lock(&host->sdio_wlock);
} else {
- if (!mmc->card || !mmc_card_sdio(mmc->card)) {
+ if (mmc->card && !mmc_card_sdio(mmc->card)) {
WARN(1, "%s: SDCC core interrupt received for non-SDIO cards when SDCC clocks are off\n",
mmc_hostname(mmc));
ret = 1;
@@ -1855,7 +1856,7 @@
#endif
if (status & MCI_SDIOINTROPE) {
- if (!mmc->card || mmc_card_sdio(mmc->card)) {
+ if (mmc->card && !mmc_card_sdio(mmc->card)) {
WARN(1, "%s: SDIO interrupt received for non-SDIO card\n",
mmc_hostname(mmc));
ret = 1;
@@ -2410,7 +2411,8 @@
curr_slot = host->plat->vreg_data;
if (!curr_slot) {
- rc = -EINVAL;
+ pr_debug("%s: vreg info unavailable, assuming the slot is powered by always on domain\n",
+ mmc_hostname(host->mmc));
goto out;
}
@@ -3487,7 +3489,6 @@
{
struct device *dev = mmc->parent;
struct msmsdcc_host *host = mmc_priv(mmc);
- unsigned long flags;
int rc = 0;
msmsdcc_pm_qos_update_latency(host, 1);
@@ -3521,7 +3522,6 @@
static int msmsdcc_disable(struct mmc_host *mmc)
{
struct msmsdcc_host *host = mmc_priv(mmc);
- unsigned long flags;
int rc = 0;
msmsdcc_pm_qos_update_latency(host, 0);
@@ -4146,25 +4146,35 @@
.hw_reset = msmsdcc_hw_reset,
};
+static void msmsdcc_enable_status_gpio(struct msmsdcc_host *host)
+{
+ unsigned int gpio_no = host->plat->status_gpio;
+ int status;
+
+ if (!gpio_is_valid(gpio_no))
+ return;
+
+ status = gpio_request(gpio_no, "SD_HW_Detect");
+ if (status)
+ pr_err("%s: %s: gpio_request(%d) failed\n",
+ mmc_hostname(host->mmc), __func__, gpio_no);
+}
+
+static void msmsdcc_disable_status_gpio(struct msmsdcc_host *host)
+{
+ if (gpio_is_valid(host->plat->status_gpio))
+ gpio_free(host->plat->status_gpio);
+}
+
static unsigned int
msmsdcc_slot_status(struct msmsdcc_host *host)
{
int status;
- unsigned int gpio_no = host->plat->status_gpio;
- status = gpio_request(gpio_no, "SD_HW_Detect");
- if (status) {
- pr_err("%s: %s: Failed to request GPIO %d\n",
- mmc_hostname(host->mmc), __func__, gpio_no);
- } else {
- status = gpio_direction_input(gpio_no);
- if (!status) {
- status = gpio_get_value_cansleep(gpio_no);
- if (host->plat->is_status_gpio_active_low)
- status = !status;
- }
- gpio_free(gpio_no);
- }
+ status = gpio_get_value_cansleep(host->plat->status_gpio);
+ if (host->plat->is_status_gpio_active_low)
+ status = !status;
+
return status;
}
@@ -5441,7 +5451,7 @@
struct mmc_platform_data *pdata;
struct device_node *np = dev->of_node;
u32 bus_width = 0, current_limit = 0;
- u32 *clk_table, *sup_voltages;
+ u32 *clk_table = NULL, *sup_voltages = NULL;
int clk_table_len, sup_volt_len, len;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
@@ -5825,6 +5835,8 @@
mmc->caps |= (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
MMC_CAP_SET_XPC_180);
+ mmc->caps2 |= MMC_CAP2_PACKED_WR;
+ mmc->caps2 |= MMC_CAP2_PACKED_WR_CONTROL;
mmc->caps2 |= (MMC_CAP2_BOOTPART_NOACC | MMC_CAP2_DETECT_ON_ERR);
mmc->caps2 |= MMC_CAP2_SANITIZE;
mmc->caps2 |= MMC_CAP2_CACHE_CTRL;
@@ -5911,11 +5923,12 @@
plat->wpswitch_gpio = -ENOENT;
if (plat->status || gpio_is_valid(plat->status_gpio)) {
- if (plat->status)
+ if (plat->status) {
host->oldstat = plat->status(mmc_dev(host->mmc));
- else
+ } else {
+ msmsdcc_enable_status_gpio(host);
host->oldstat = msmsdcc_slot_status(host);
-
+ }
host->eject = !host->oldstat;
}
@@ -6098,6 +6111,7 @@
if (plat->status_irq)
free_irq(plat->status_irq, host);
+ msmsdcc_disable_status_gpio(host);
sdiowakeup_irq_free:
wake_lock_destroy(&host->sdio_suspend_wlock);
if (plat->sdiowakeup_irq)
@@ -6194,6 +6208,7 @@
if (plat->status_irq)
free_irq(plat->status_irq, host);
+ msmsdcc_disable_status_gpio(host);
wake_lock_destroy(&host->sdio_suspend_wlock);
if (plat->sdiowakeup_irq) {
@@ -6522,8 +6537,10 @@
rc = 0;
goto out;
}
- if (host->plat->status_irq)
+ if (host->plat->status_irq) {
disable_irq(host->plat->status_irq);
+ msmsdcc_disable_status_gpio(host);
+ }
if (!pm_runtime_suspended(dev))
rc = msmsdcc_runtime_suspend(dev);
@@ -6579,6 +6596,7 @@
host->pending_resume = true;
if (host->plat->status_irq) {
+ msmsdcc_enable_status_gpio(host);
msmsdcc_check_status((unsigned long)host);
enable_irq(host->plat->status_irq);
}
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 2f1a0dc..2f7a2f3 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2582,6 +2582,19 @@
mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
/*
+ * If Power Off Notify capability is enabled by the host,
+ * set notify to short power off notify timeout value.
+ */
+ if (mmc->caps2 & MMC_CAP2_POWEROFF_NOTIFY)
+ mmc->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT;
+ else
+ mmc->power_notify_type = MMC_HOST_PW_NOTIFY_NONE;
+
+ /* Initial value for re-tuning timer count */
+ host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
+ SDHCI_RETUNING_TIMER_COUNT_SHIFT;
+
+ /*
* In case Re-tuning Timer is not disabled, the actual value of
* re-tuning timer will be 2 ^ (n - 1).
*/
diff --git a/drivers/net/ethernet/msm/msm_rmnet.c b/drivers/net/ethernet/msm/msm_rmnet.c
index 61df241..41ad8af 100644
--- a/drivers/net/ethernet/msm/msm_rmnet.c
+++ b/drivers/net/ethernet/msm/msm_rmnet.c
@@ -501,16 +501,10 @@
static int __rmnet_close(struct net_device *dev)
{
struct rmnet_private *p = netdev_priv(dev);
- int rc;
- unsigned long flags;
- if (p->ch) {
- rc = smd_close(p->ch);
- spin_lock_irqsave(&p->lock, flags);
- p->ch = 0;
- spin_unlock_irqrestore(&p->lock, flags);
- return rc;
- } else
+ if (p->ch)
+ return 0;
+ else
return -EBADF;
}
@@ -529,12 +523,9 @@
static int rmnet_stop(struct net_device *dev)
{
- struct rmnet_private *p = netdev_priv(dev);
-
DBG0("[%s] rmnet_stop()\n", dev->name);
netif_stop_queue(dev);
- tasklet_kill(&p->tsklt);
/* TODO: unload modem safely,
currently, this causes unnecessary unloads */
diff --git a/drivers/net/usb/rmnet_usb_data.c b/drivers/net/usb/rmnet_usb_data.c
index 17ff067..fdfe468 100644
--- a/drivers/net/usb/rmnet_usb_data.c
+++ b/drivers/net/usb/rmnet_usb_data.c
@@ -556,10 +556,6 @@
/* allow modem and roothub to wake up suspended system */
device_set_wakeup_enable(&udev->dev, 1);
device_set_wakeup_enable(&udev->parent->dev, 1);
-
- /* set default autosuspend timeout for modem and roothub */
- pm_runtime_set_autosuspend_delay(&udev->dev, 1000);
- pm_runtime_set_autosuspend_delay(&udev->parent->dev, 200);
}
out:
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 6de0a77..740c717 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -1168,6 +1168,7 @@
usb_anchor_urb(urb, &dev->deferred);
/* no use to process more packets */
netif_stop_queue(net);
+ usb_put_urb(urb);
spin_unlock_irqrestore(&dev->txq.lock, flags);
netdev_dbg(dev->net, "Delaying transmission for resumption\n");
goto deferred;
@@ -1317,6 +1318,8 @@
cancel_work_sync(&dev->kevent);
+ usb_scuttle_anchored_urbs(&dev->deferred);
+
if (dev->driver_info->unbind)
dev->driver_info->unbind (dev, intf);
diff --git a/drivers/platform/msm/qpnp-power-on.c b/drivers/platform/msm/qpnp-power-on.c
index 33b12ae..76a758b 100644
--- a/drivers/platform/msm/qpnp-power-on.c
+++ b/drivers/platform/msm/qpnp-power-on.c
@@ -39,8 +39,10 @@
#define QPNP_PON_RESIN_PULL_UP BIT(0)
#define QPNP_PON_KPDPWR_PULL_UP BIT(1)
+#define QPNP_PON_CBLPWR_PULL_UP BIT(2)
#define QPNP_PON_S2_CNTL_EN BIT(7)
#define QPNP_PON_S2_RESET_ENABLE BIT(7)
+#define QPNP_PON_DELAY_BIT_SHIFT 6
#define QPNP_PON_S1_TIMER_MASK (0xF)
#define QPNP_PON_S2_TIMER_MASK (0x7)
@@ -49,6 +51,7 @@
#define QPNP_PON_DBC_DELAY_MASK (0x7)
#define QPNP_PON_KPDPWR_N_SET BIT(0)
#define QPNP_PON_RESIN_N_SET BIT(1)
+#define QPNP_PON_CBLPWR_N_SET BIT(2)
#define QPNP_PON_RESIN_BARK_N_SET BIT(4)
#define QPNP_PON_RESET_EN BIT(7)
@@ -66,6 +69,7 @@
enum pon_type {
PON_KPDPWR,
PON_RESIN,
+ PON_CBLPWR,
};
struct qpnp_pon_config {
@@ -214,6 +218,9 @@
case PON_RESIN:
pon_rt_bit = QPNP_PON_RESIN_N_SET;
break;
+ case PON_CBLPWR:
+ pon_rt_bit = QPNP_PON_CBLPWR_N_SET;
+ break;
default:
return -EINVAL;
}
@@ -253,6 +260,18 @@
return IRQ_HANDLED;
}
+static irqreturn_t qpnp_cblpwr_irq(int irq, void *_pon)
+{
+ int rc;
+ struct qpnp_pon *pon = _pon;
+
+ rc = qpnp_pon_input_dispatch(pon, PON_CBLPWR);
+ if (rc)
+ dev_err(&pon->spmi->dev, "Unable to send input event\n");
+
+ return IRQ_HANDLED;
+}
+
static void bark_work_func(struct work_struct *work)
{
int rc;
@@ -351,6 +370,9 @@
case PON_RESIN:
pull_bit = QPNP_PON_RESIN_PULL_UP;
break;
+ case PON_CBLPWR:
+ pull_bit = QPNP_PON_CBLPWR_PULL_UP;
+ break;
default:
return -EINVAL;
}
@@ -489,6 +511,17 @@
}
}
break;
+ case PON_CBLPWR:
+ rc = devm_request_irq(&pon->spmi->dev, cfg->state_irq,
+ qpnp_cblpwr_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "qpnp_cblpwr_status", pon);
+ if (rc < 0) {
+ dev_err(&pon->spmi->dev, "Can't request %d IRQ\n",
+ cfg->state_irq);
+ return rc;
+ }
+ break;
default:
return -EINVAL;
}
@@ -595,6 +628,15 @@
}
}
break;
+ case PON_CBLPWR:
+ cfg->state_irq = spmi_get_irq_byname(pon->spmi,
+ NULL, "cblpwr");
+ if (cfg->state_irq < 0) {
+ dev_err(&pon->spmi->dev,
+ "Unable to get cblpwr irq\n");
+ return rc;
+ }
+ break;
default:
dev_err(&pon->spmi->dev, "PON RESET %d not supported",
cfg->pon_type);
@@ -763,11 +805,13 @@
rc = of_property_read_u32(pon->spmi->dev.of_node,
"qcom,pon-dbc-delay", &delay);
- if (rc && rc != -EINVAL) {
- dev_err(&spmi->dev, "Unable to read debounce delay\n");
- return rc;
+ if (rc) {
+ if (rc != -EINVAL) {
+ dev_err(&spmi->dev, "Unable to read debounce delay\n");
+ return rc;
+ }
} else {
- delay = (delay << 6) / USEC_PER_SEC;
+ delay = (delay << QPNP_PON_DELAY_BIT_SHIFT) / USEC_PER_SEC;
delay = ilog2(delay);
rc = qpnp_pon_masked_write(pon, QPNP_PON_DBC_CTL(pon->base),
QPNP_PON_DBC_DELAY_MASK, delay);
diff --git a/drivers/platform/msm/qpnp-pwm.c b/drivers/platform/msm/qpnp-pwm.c
index 6f9af36..1729b49 100644
--- a/drivers/platform/msm/qpnp-pwm.c
+++ b/drivers/platform/msm/qpnp-pwm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -107,8 +107,10 @@
/* LPG Control for RAMP_CONTROL */
#define QPNP_RAMP_START_MASK 0x01
-#define QPNP_ENABLE_LUT(value) (value |= QPNP_RAMP_START_MASK)
-#define QPNP_DISABLE_LUT(value) (value &= ~QPNP_RAMP_START_MASK)
+#define QPNP_ENABLE_LUT_V0(value) (value |= QPNP_RAMP_START_MASK)
+#define QPNP_DISABLE_LUT_V0(value) (value &= ~QPNP_RAMP_START_MASK)
+#define QPNP_ENABLE_LUT_V1(value, id) (value |= BIT(id))
+#define QPNP_DISABLE_LUT_V1(value, id) (value &= ~BIT(id))
/* LPG Control for RAMP_STEP_DURATION_LSB */
#define QPNP_RAMP_STEP_DURATION_LSB_MASK 0xFF
@@ -154,10 +156,30 @@
#define PRE_DIVIDE_5 5
#define PRE_DIVIDE_6 6
-#define SPMI_LPG_REG_ADDR_BASE 0x40
-#define SPMI_LPG_REG_ADDR(b, n) (b + SPMI_LPG_REG_ADDR_BASE + (n))
+#define SPMI_LPG_REG_BASE_OFFSET 0x40
+#define SPMI_LPG_REVISION2_OFFSET 0x1
+#define SPMI_LPG_REV1_RAMP_CONTROL_OFFSET 0x86
+#define SPMI_LPG_REG_ADDR(b, n) (b + SPMI_LPG_REG_BASE_OFFSET + (n))
#define SPMI_MAX_BUF_LEN 8
+/* LPG revisions */
+enum qpnp_lpg_revision {
+ QPNP_LPG_REVISION_0 = 0x0,
+ QPNP_LPG_REVISION_1 = 0x1,
+};
+
+/* LPG LUT MODE STATE */
+enum qpnp_lut_state {
+ QPNP_LUT_ENABLE = 0x0,
+ QPNP_LUT_DISABLE = 0x1,
+};
+
+/* PWM MODE STATE */
+enum qpnp_pwm_state {
+ QPNP_PWM_ENABLE = 0x0,
+ QPNP_PWM_DISABLE = 0x1,
+};
+
/* SPMI LPG registers */
enum qpnp_lpg_registers_list {
QPNP_LPG_PATTERN_CONFIG,
@@ -251,9 +273,10 @@
struct qpnp_lpg_chip {
struct spmi_device *spmi_dev;
struct pwm_device pwm_dev;
- struct mutex lpg_mutex;
+ spinlock_t lpg_lock;
struct qpnp_lpg_config lpg_config;
u8 qpnp_lpg_registers[QPNP_TOTAL_LPG_SPMI_REGISTERS];
+ enum qpnp_lpg_revision revision;
};
/* Internal functions */
@@ -283,21 +306,22 @@
QPNP_EN_GLITCH_REMOVAL_MASK;
}
-static inline void qpnp_set_control(u8 *val, bool pwm_hi, bool pwm_lo,
- bool pwm_out, bool pwm_src, bool ramp_gen)
+static int qpnp_set_control(bool pwm_hi, bool pwm_lo, bool pwm_out,
+ bool pwm_src, bool ramp_gen)
{
- *val = (ramp_gen << QPNP_PWM_EN_RAMP_GEN_SHIFT) &
- QPNP_PWM_EN_RAMP_GEN_MASK;
- *val |= (pwm_src << QPNP_PWM_SRC_SELECT_SHIFT) &
- QPNP_PWM_SRC_SELECT_MASK;
- *val |= (pwm_out << QPNP_EN_PWM_OUTPUT_SHIFT) &
- QPNP_EN_PWM_OUTPUT_MASK;
- *val |= (pwm_lo << QPNP_EN_PWM_LO_SHIFT) & QPNP_EN_PWM_LO_MASK;
- *val |= (pwm_hi << QPNP_EN_PWM_HIGH_SHIFT) & QPNP_EN_PWM_HIGH_MASK;
+ return (ramp_gen << QPNP_PWM_EN_RAMP_GEN_SHIFT)
+ | (pwm_src << QPNP_PWM_SRC_SELECT_SHIFT)
+ | (pwm_out << QPNP_EN_PWM_OUTPUT_SHIFT)
+ | (pwm_lo << QPNP_EN_PWM_LO_SHIFT)
+ | (pwm_hi << QPNP_EN_PWM_HIGH_SHIFT);
}
-#define QPNP_ENABLE_LUT_CONTROL(p_val) qpnp_set_control(p_val, 1, 1, 1, 0, 1)
-#define QPNP_ENABLE_PWM_CONTROL(p_val) qpnp_set_control(p_val, 1, 1, 0, 1, 0)
+#define QPNP_ENABLE_LUT_CONTROL qpnp_set_control(0, 0, 0, 0, 1)
+#define QPNP_ENABLE_PWM_CONTROL qpnp_set_control(0, 0, 0, 1, 0)
+#define QPNP_ENABLE_PWM_MODE qpnp_set_control(1, 1, 1, 1, 0)
+#define QPNP_ENABLE_LPG_MODE qpnp_set_control(1, 1, 1, 0, 1)
+#define QPNP_DISABLE_PWM_MODE qpnp_set_control(0, 0, 0, 1, 0)
+#define QPNP_DISABLE_LPG_MODE qpnp_set_control(0, 0, 0, 0, 1)
#define QPNP_IS_PWM_CONFIG_SELECTED(val) (val & QPNP_PWM_SRC_SELECT_MASK)
@@ -328,13 +352,13 @@
*u8p |= val & mask;
}
-static int qpnp_lpg_save_and_write(u8 value, u8 mask, u8 *reg, u16 base_addr,
- u16 offset, u16 size, struct qpnp_lpg_chip *chip)
+static int qpnp_lpg_save_and_write(u8 value, u8 mask, u8 *reg, u16 addr,
+ u16 size, struct qpnp_lpg_chip *chip)
{
qpnp_lpg_save(reg, mask, value);
return spmi_ext_register_writel(chip->spmi_dev->ctrl,
- chip->spmi_dev->sid, SPMI_LPG_REG_ADDR(base_addr, offset), reg, size);
+ chip->spmi_dev->sid, addr, reg, size);
}
/*
@@ -439,7 +463,7 @@
int i, pwm_size, rc = 0;
int burst_size = SPMI_MAX_BUF_LEN;
int list_len = lut->list_len << 1;
- int offset = lut->lo_index << 2;
+ int offset = lut->lo_index << 1;
pwm_size = QPNP_GET_PWM_SIZE(
chip->qpnp_lpg_registers[QPNP_LPG_PWM_SIZE_CLK]) &
@@ -455,7 +479,7 @@
return -EINVAL;
}
- for (i = 0; i <= lut->list_len; i++) {
+ for (i = 0; i < lut->list_len; i++) {
if (raw_value)
pwm_value = duty_pct[i];
else
@@ -530,7 +554,8 @@
rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_PWM_VALUE_LSB],
- lpg_config->base_addr, QPNP_PWM_VALUE_LSB, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_PWM_VALUE_LSB), 1, chip);
if (rc)
return rc;
@@ -541,7 +566,8 @@
return qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_PWM_VALUE_MSB],
- lpg_config->base_addr, QPNP_PWM_VALUE_MSB, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_PWM_VALUE_MSB), 1, chip);
}
static int qpnp_lpg_configure_pattern(struct pwm_device *pwm)
@@ -559,7 +585,8 @@
return qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_LPG_PATTERN_CONFIG],
- lpg_config->base_addr, QPNP_LPG_PATTERN_CONFIG, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_LPG_PATTERN_CONFIG), 1, chip);
}
static int qpnp_lpg_configure_pwm(struct pwm_device *pwm)
@@ -590,7 +617,8 @@
return qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_LPG_PWM_TYPE_CONFIG],
- lpg_config->base_addr, QPNP_LPG_PWM_TYPE_CONFIG, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_LPG_PWM_TYPE_CONFIG), 1, chip);
}
static int qpnp_configure_pwm_control(struct pwm_device *pwm)
@@ -599,7 +627,7 @@
struct qpnp_lpg_chip *chip = pwm->chip;
u8 value, mask;
- QPNP_ENABLE_PWM_CONTROL(&value);
+ value = QPNP_ENABLE_PWM_CONTROL;
mask = QPNP_EN_PWM_HIGH_MASK | QPNP_EN_PWM_LO_MASK |
QPNP_EN_PWM_OUTPUT_MASK | QPNP_PWM_SRC_SELECT_MASK |
@@ -607,7 +635,8 @@
return qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL],
- lpg_config->base_addr, QPNP_ENABLE_CONTROL, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_ENABLE_CONTROL), 1, chip);
}
@@ -617,7 +646,7 @@
struct qpnp_lpg_chip *chip = pwm->chip;
u8 value, mask;
- QPNP_ENABLE_LUT_CONTROL(&value);
+ value = QPNP_ENABLE_LUT_CONTROL;
mask = QPNP_EN_PWM_HIGH_MASK | QPNP_EN_PWM_LO_MASK |
QPNP_EN_PWM_OUTPUT_MASK | QPNP_PWM_SRC_SELECT_MASK |
@@ -625,7 +654,8 @@
return qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL],
- lpg_config->base_addr, QPNP_ENABLE_CONTROL, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_ENABLE_CONTROL), 1, chip);
}
@@ -643,7 +673,8 @@
rc = qpnp_lpg_save_and_write(val, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_RAMP_STEP_DURATION_LSB],
- lpg_config->base_addr, QPNP_RAMP_STEP_DURATION_LSB, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_RAMP_STEP_DURATION_LSB), 1, chip);
if (rc)
return rc;
@@ -654,7 +685,8 @@
return qpnp_lpg_save_and_write(val, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_RAMP_STEP_DURATION_MSB],
- lpg_config->base_addr, QPNP_RAMP_STEP_DURATION_MSB, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_RAMP_STEP_DURATION_MSB), 1, chip);
}
static int qpnp_lpg_configure_pause(struct pwm_device *pwm)
@@ -671,7 +703,8 @@
rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_PAUSE_HI_MULTIPLIER_LSB],
- lpg_config->base_addr, QPNP_PAUSE_HI_MULTIPLIER_LSB, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_PAUSE_HI_MULTIPLIER_LSB), 1, chip);
if (rc)
return rc;
@@ -683,14 +716,16 @@
rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_PAUSE_HI_MULTIPLIER_MSB],
- lpg_config->base_addr, QPNP_PAUSE_HI_MULTIPLIER_MSB, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_PAUSE_HI_MULTIPLIER_MSB), 1, chip);
} else {
value = 0;
mask = QPNP_PAUSE_HI_MULTIPLIER_LSB_MASK;
rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_PAUSE_HI_MULTIPLIER_LSB],
- lpg_config->base_addr, QPNP_PAUSE_HI_MULTIPLIER_LSB, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_PAUSE_HI_MULTIPLIER_LSB), 1, chip);
if (rc)
return rc;
@@ -698,7 +733,8 @@
rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_PAUSE_HI_MULTIPLIER_MSB],
- lpg_config->base_addr, QPNP_PAUSE_HI_MULTIPLIER_MSB, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_PAUSE_HI_MULTIPLIER_MSB), 1, chip);
if (rc)
return rc;
@@ -710,7 +746,8 @@
rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_PAUSE_LO_MULTIPLIER_LSB],
- lpg_config->base_addr, QPNP_PAUSE_LO_MULTIPLIER_LSB, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_PAUSE_LO_MULTIPLIER_LSB), 1, chip);
if (rc)
return rc;
@@ -722,14 +759,16 @@
rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_PAUSE_LO_MULTIPLIER_MSB],
- lpg_config->base_addr, QPNP_PAUSE_LO_MULTIPLIER_MSB, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_PAUSE_LO_MULTIPLIER_MSB), 1, chip);
} else {
value = 0;
mask = QPNP_PAUSE_LO_MULTIPLIER_LSB_MASK;
rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_PAUSE_LO_MULTIPLIER_LSB],
- lpg_config->base_addr, QPNP_PAUSE_LO_MULTIPLIER_LSB, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_PAUSE_LO_MULTIPLIER_LSB), 1, chip);
if (rc)
return rc;
@@ -737,7 +776,8 @@
rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_PAUSE_LO_MULTIPLIER_MSB],
- lpg_config->base_addr, QPNP_PAUSE_LO_MULTIPLIER_MSB, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_PAUSE_LO_MULTIPLIER_MSB), 1, chip);
return rc;
}
@@ -757,7 +797,8 @@
rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_HI_INDEX],
- lpg_config->base_addr, QPNP_HI_INDEX, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_HI_INDEX), 1, chip);
if (rc)
return rc;
@@ -766,7 +807,8 @@
rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_LO_INDEX],
- lpg_config->base_addr, QPNP_LO_INDEX, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_LO_INDEX), 1, chip);
return rc;
}
@@ -808,72 +850,97 @@
return rc;
}
-static int qpnp_lpg_enable_lut(struct pwm_device *pwm)
+static int qpnp_lpg_configure_lut_state(struct pwm_device *pwm,
+ enum qpnp_lut_state state)
{
struct qpnp_lpg_config *lpg_config = &pwm->chip->lpg_config;
struct qpnp_lpg_chip *chip = pwm->chip;
- u8 value, mask;
+ u8 value1, value2, mask1, mask2;
+ u8 *reg1, *reg2;
+ u16 addr;
+ int rc;
- value = pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
+ value1 = pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
+ reg1 = &pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
+ reg2 = &pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL];
+ mask2 = QPNP_EN_PWM_HIGH_MASK | QPNP_EN_PWM_LO_MASK |
+ QPNP_EN_PWM_OUTPUT_MASK | QPNP_PWM_SRC_SELECT_MASK |
+ QPNP_PWM_EN_RAMP_GEN_MASK;
- QPNP_ENABLE_LUT(value);
+ switch (chip->revision) {
+ case QPNP_LPG_REVISION_0:
+ if (state == QPNP_LUT_ENABLE) {
+ QPNP_ENABLE_LUT_V0(value1);
+ value2 = QPNP_ENABLE_LPG_MODE;
+ } else {
+ QPNP_DISABLE_LUT_V0(value1);
+ value2 = QPNP_DISABLE_LPG_MODE;
+ }
+ mask1 = QPNP_RAMP_START_MASK;
+ addr = SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_RAMP_CONTROL);
+ break;
+ case QPNP_LPG_REVISION_1:
+ if (state == QPNP_LUT_ENABLE) {
+ QPNP_ENABLE_LUT_V1(value1, pwm->pwm_config.channel_id);
+ value2 = QPNP_ENABLE_LPG_MODE;
+ } else {
+ QPNP_DISABLE_LUT_V1(value1, pwm->pwm_config.channel_id);
+ value2 = QPNP_DISABLE_LPG_MODE;
+ }
+ mask1 = BIT(pwm->pwm_config.channel_id);
+ addr = lpg_config->lut_base_addr +
+ SPMI_LPG_REV1_RAMP_CONTROL_OFFSET;
+ break;
+ default:
+ pr_err("Invalid LPG revision\n");
+ return -EINVAL;
+ }
- mask = QPNP_RAMP_START_MASK;
+ rc = qpnp_lpg_save_and_write(value1, mask1, reg1,
+ addr, 1, chip);
+ if (rc)
+ return rc;
+ addr = SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_ENABLE_CONTROL);
+ return qpnp_lpg_save_and_write(value2, mask2, reg2,
+ addr, 1, chip);
- return qpnp_lpg_save_and_write(value, mask,
- &pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL],
- lpg_config->base_addr, QPNP_RAMP_CONTROL, 1, chip);
}
-static int qpnp_disable_lut(struct pwm_device *pwm)
+static int qpnp_lpg_configure_pwm_state(struct pwm_device *pwm,
+ enum qpnp_pwm_state state)
{
struct qpnp_lpg_config *lpg_config = &pwm->chip->lpg_config;
struct qpnp_lpg_chip *chip = pwm->chip;
u8 value, mask;
+ int rc;
- value = pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
+ if (state == QPNP_PWM_ENABLE)
+ value = QPNP_ENABLE_PWM_MODE;
+ else
+ value = QPNP_DISABLE_PWM_MODE;
- QPNP_DISABLE_LUT(value);
+ mask = QPNP_EN_PWM_HIGH_MASK | QPNP_EN_PWM_LO_MASK |
+ QPNP_EN_PWM_OUTPUT_MASK | QPNP_PWM_SRC_SELECT_MASK |
+ QPNP_PWM_EN_RAMP_GEN_MASK;
- mask = QPNP_RAMP_START_MASK;
-
- return qpnp_lpg_save_and_write(value, mask,
- &pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL],
- lpg_config->base_addr, QPNP_RAMP_CONTROL, 1, chip);
-}
-
-static int qpnp_lpg_enable_pwm(struct pwm_device *pwm)
-{
- struct qpnp_lpg_config *lpg_config = &pwm->chip->lpg_config;
- struct qpnp_lpg_chip *chip = pwm->chip;
- u8 value, mask;
-
- value = pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL];
-
- QPNP_ENABLE_PWM(value);
-
- mask = QPNP_EN_PWM_OUTPUT_MASK;
-
- return qpnp_lpg_save_and_write(value, mask,
+ rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL],
- lpg_config->base_addr, QPNP_RAMP_CONTROL, 1, chip);
-}
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_ENABLE_CONTROL), 1, chip);
+ if (rc)
+ goto out;
-static int qpnp_disable_pwm(struct pwm_device *pwm)
-{
- struct qpnp_lpg_config *lpg_config = &pwm->chip->lpg_config;
- struct qpnp_lpg_chip *chip = pwm->chip;
- u8 value, mask;
+ /*
+ * Due to LPG hardware bug, in the PWM mode, having enabled PWM,
+ * We have to write PWM values one more time.
+ */
+ if (state == QPNP_PWM_ENABLE)
+ return qpnp_lpg_save_pwm_value(pwm);
- value = pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL];
-
- QPNP_DISABLE_PWM(value);
-
- mask = QPNP_EN_PWM_OUTPUT_MASK;
-
- return qpnp_lpg_save_and_write(value, mask,
- &pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL],
- lpg_config->base_addr, QPNP_RAMP_CONTROL, 1, chip);
+out:
+ return rc;
}
static int _pwm_config(struct pwm_device *pwm, int duty_us, int period_us)
@@ -999,18 +1066,23 @@
{
int rc;
struct qpnp_lpg_chip *chip;
+ unsigned long flags;
chip = pwm->chip;
- mutex_lock(&pwm->chip->lpg_mutex);
+ spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
if (QPNP_IS_PWM_CONFIG_SELECTED(
chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL]))
- rc = qpnp_lpg_enable_pwm(pwm);
+ rc = qpnp_lpg_configure_pwm_state(pwm, QPNP_PWM_ENABLE);
else
- rc = qpnp_lpg_enable_lut(pwm);
+ rc = qpnp_lpg_configure_lut_state(pwm, QPNP_LUT_ENABLE);
- mutex_unlock(&pwm->chip->lpg_mutex);
+ spin_unlock_irqrestore(&pwm->chip->lpg_lock, flags);
+
+ if (rc)
+ pr_err("Failed to enable PWM channel: %d\n",
+ pwm->pwm_config.channel_id);
return rc;
}
@@ -1025,6 +1097,7 @@
{
struct qpnp_lpg_chip *chip;
struct pwm_device *pwm;
+ unsigned long flags;
chip = radix_tree_lookup(&lpg_dev_tree, pwm_id);
@@ -1034,7 +1107,7 @@
return ERR_PTR(-EINVAL);
}
- mutex_lock(&chip->lpg_mutex);
+ spin_lock_irqsave(&chip->lpg_lock, flags);
pwm = &chip->pwm_dev;
@@ -1048,7 +1121,7 @@
pwm->pwm_config.lable = lable;
}
- mutex_unlock(&chip->lpg_mutex);
+ spin_unlock_irqrestore(&chip->lpg_lock, flags);
return pwm;
}
@@ -1061,24 +1134,25 @@
void pwm_free(struct pwm_device *pwm)
{
struct qpnp_pwm_config *pwm_config;
+ unsigned long flags;
if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL) {
pr_err("Invalid pwm handle or no pwm_chip\n");
return;
}
- mutex_lock(&pwm->chip->lpg_mutex);
+ spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
pwm_config = &pwm->pwm_config;
if (pwm_config->in_use) {
- qpnp_disable_pwm(pwm);
- qpnp_disable_lut(pwm);
+ qpnp_lpg_configure_pwm_state(pwm, QPNP_PWM_DISABLE);
+ qpnp_lpg_configure_lut_state(pwm, QPNP_LUT_DISABLE);
pwm_config->in_use = 0;
pwm_config->lable = NULL;
}
- mutex_unlock(&pwm->chip->lpg_mutex);
+ spin_unlock_irqrestore(&pwm->chip->lpg_lock, flags);
}
EXPORT_SYMBOL_GPL(pwm_free);
@@ -1091,6 +1165,7 @@
int pwm_config(struct pwm_device *pwm, int duty_us, int period_us)
{
int rc;
+ unsigned long flags;
if (pwm == NULL || IS_ERR(pwm) ||
duty_us > period_us ||
@@ -1103,9 +1178,12 @@
if (!pwm->pwm_config.in_use)
return -EINVAL;
- mutex_lock(&pwm->chip->lpg_mutex);
+ spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
rc = _pwm_config(pwm, duty_us, period_us);
- mutex_unlock(&pwm->chip->lpg_mutex);
+ spin_unlock_irqrestore(&pwm->chip->lpg_lock, flags);
+
+ if (rc)
+ pr_err("Failed to configure PWM mode\n");
return rc;
}
@@ -1143,13 +1221,15 @@
{
struct qpnp_pwm_config *pwm_config;
struct qpnp_lpg_chip *chip;
+ unsigned long flags;
+ int rc = 0;
if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL) {
pr_err("Invalid pwm handle or no pwm_chip\n");
return;
}
- mutex_lock(&pwm->chip->lpg_mutex);
+ spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
chip = pwm->chip;
pwm_config = &pwm->pwm_config;
@@ -1157,12 +1237,18 @@
if (pwm_config->in_use) {
if (QPNP_IS_PWM_CONFIG_SELECTED(
chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL]))
- qpnp_disable_pwm(pwm);
+ rc = qpnp_lpg_configure_pwm_state(pwm,
+ QPNP_PWM_DISABLE);
else
- qpnp_disable_lut(pwm);
+ rc = qpnp_lpg_configure_lut_state(pwm,
+ QPNP_LUT_DISABLE);
}
- mutex_unlock(&pwm->chip->lpg_mutex);
+ spin_unlock_irqrestore(&pwm->chip->lpg_lock, flags);
+
+ if (rc)
+ pr_err("Failed to disable PWM channel: %d\n",
+ pwm_config->channel_id);
}
EXPORT_SYMBOL_GPL(pwm_disable);
@@ -1174,6 +1260,7 @@
int pwm_change_mode(struct pwm_device *pwm, enum pm_pwm_mode mode)
{
int rc;
+ unsigned long flags;
if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL) {
pr_err("Invalid pwm handle or no pwm_chip\n");
@@ -1185,15 +1272,17 @@
return -EINVAL;
}
- mutex_lock(&pwm->chip->lpg_mutex);
+ spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
if (mode)
rc = qpnp_configure_lpg_control(pwm);
else
rc = qpnp_configure_pwm_control(pwm);
- mutex_unlock(&pwm->chip->lpg_mutex);
+ spin_unlock_irqrestore(&pwm->chip->lpg_lock, flags);
+ if (rc)
+ pr_err("Failed to change the mode\n");
return rc;
}
EXPORT_SYMBOL_GPL(pwm_change_mode);
@@ -1210,6 +1299,7 @@
struct qpnp_pwm_config *pwm_config;
struct qpnp_lpg_config *lpg_config;
struct qpnp_lpg_chip *chip;
+ unsigned long flags;
int rc = 0;
if (pwm == NULL || IS_ERR(pwm) || period == NULL)
@@ -1217,7 +1307,7 @@
if (pwm->chip == NULL)
return -ENODEV;
- mutex_lock(&pwm->chip->lpg_mutex);
+ spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
chip = pwm->chip;
pwm_config = &pwm->pwm_config;
@@ -1256,7 +1346,7 @@
}
out_unlock:
- mutex_unlock(&pwm->chip->lpg_mutex);
+ spin_unlock_irqrestore(&pwm->chip->lpg_lock, flags);
return rc;
}
EXPORT_SYMBOL(pwm_config_period);
@@ -1270,21 +1360,27 @@
{
struct qpnp_lpg_config *lpg_config;
struct qpnp_pwm_config *pwm_config;
+ unsigned long flags;
int rc = 0;
- if (pwm == NULL || IS_ERR(pwm))
+ if (pwm == NULL || IS_ERR(pwm)) {
+ pr_err("Invalid parameter passed\n");
return -EINVAL;
+ }
- if (pwm->chip == NULL)
+ if (pwm->chip == NULL) {
+ pr_err("Invalid device handle\n");
return -ENODEV;
+ }
lpg_config = &pwm->chip->lpg_config;
pwm_config = &pwm->pwm_config;
- mutex_lock(&pwm->chip->lpg_mutex);
+ spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
if (!pwm_config->in_use || !pwm_config->pwm_period) {
rc = -EINVAL;
+ pr_err("PWM channel isn't in use or period value missing\n");
goto out_unlock;
}
@@ -1300,7 +1396,7 @@
pwm_config->channel_id, rc);
out_unlock:
- mutex_unlock(&pwm->chip->lpg_mutex);
+ spin_unlock_irqrestore(&pwm->chip->lpg_lock, flags);
return rc;
}
EXPORT_SYMBOL_GPL(pwm_config_pwm_value);
@@ -1315,6 +1411,7 @@
int pwm_lut_config(struct pwm_device *pwm, int period_us,
int duty_pct[], struct lut_params lut_params)
{
+ unsigned long flags;
int rc = 0;
if (pwm == NULL || IS_ERR(pwm) || !lut_params.idx_len) {
@@ -1348,11 +1445,14 @@
return -EINVAL;
}
- mutex_lock(&pwm->chip->lpg_mutex);
+ spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
rc = _pwm_lut_config(pwm, period_us, duty_pct, lut_params);
- mutex_unlock(&pwm->chip->lpg_mutex);
+ spin_unlock_irqrestore(&pwm->chip->lpg_lock, flags);
+
+ if (rc)
+ pr_err("Failed to configure LUT\n");
return rc;
}
@@ -1583,7 +1683,7 @@
return -ENOMEM;
}
- mutex_init(&chip->lpg_mutex);
+ spin_lock_init(&chip->lpg_lock);
chip->spmi_dev = spmi;
chip->pwm_dev.chip = chip;
@@ -1596,6 +1696,19 @@
id = chip->pwm_dev.pwm_config.channel_id;
+ spmi_ext_register_readl(chip->spmi_dev->ctrl,
+ chip->spmi_dev->sid,
+ chip->lpg_config.base_addr + SPMI_LPG_REVISION2_OFFSET,
+ (u8 *) &chip->revision, 1);
+
+ if (chip->revision < QPNP_LPG_REVISION_0 ||
+ chip->revision > QPNP_LPG_REVISION_1) {
+ pr_err("Unknown LPG revision detected, rev:%d\n",
+ chip->revision);
+ rc = -EINVAL;
+ goto failed_insert;
+ }
+
rc = radix_tree_insert(&lpg_dev_tree, id, chip);
if (rc) {
@@ -1610,7 +1723,6 @@
kfree(chip->lpg_config.lut_config.duty_pct_list);
failed_config:
dev_set_drvdata(&spmi->dev, NULL);
- mutex_destroy(&chip->lpg_mutex);
kfree(chip);
return rc;
}
@@ -1627,7 +1739,6 @@
if (chip) {
lpg_config = &chip->lpg_config;
kfree(lpg_config->lut_config.duty_pct_list);
- mutex_destroy(&chip->lpg_mutex);
kfree(chip);
}
diff --git a/drivers/platform/msm/sps/sps.c b/drivers/platform/msm/sps/sps.c
index 5bbcc84..25febff 100644
--- a/drivers/platform/msm/sps/sps.c
+++ b/drivers/platform/msm/sps/sps.c
@@ -1828,19 +1828,6 @@
if (virt_addr != NULL)
bam->props.virt_addr = virt_addr;
- if ((bam_props->manage & SPS_BAM_MGR_DEVICE_REMOTE) != 0 &&
- (bam_props->manage & SPS_BAM_MGR_MULTI_EE) != 0 &&
- bam_props->ee == 0) {
- /*
- * BAM global is owned by a remote processor, so force EE index
- * to a non-zero value to insure EE zero globals are not
- * modified.
- */
- SPS_DBG2("sps:Setting EE for BAM %x to non-zero",
- bam_props->phys_addr);
- bam->props.ee = 1;
- }
-
ok = sps_bam_device_init(bam);
mutex_unlock(&bam->lock);
if (ok) {
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index c474e36..376750f 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -290,8 +290,7 @@
config BATTERY_MSM
tristate "MSM battery"
- depends on ARCH_MSM
- default m
+ depends on ARCH_MSM && MSM_ONCRPCROUTER
help
Say Y to enable support for the battery in Qualcomm MSM.
diff --git a/drivers/power/msm_battery.c b/drivers/power/msm_battery.c
index 5abc032..f8186b1 100644
--- a/drivers/power/msm_battery.c
+++ b/drivers/power/msm_battery.c
@@ -1405,8 +1405,10 @@
msm_batt_info.msm_psy_batt = &msm_psy_batt;
#ifndef CONFIG_BATTERY_MSM_FAKE
- rc = msm_batt_register(BATTERY_LOW, BATTERY_ALL_ACTIVITY,
- BATTERY_CB_ID_ALL_ACTIV, BATTERY_ALL_ACTIVITY);
+ rc = msm_batt_register(msm_batt_info.voltage_fail_safe,
+ BATTERY_ALL_ACTIVITY,
+ BATTERY_CB_ID_ALL_ACTIV,
+ BATTERY_ALL_ACTIVITY);
if (rc < 0) {
dev_err(&pdev->dev,
"%s: msm_batt_register failed rc = %d\n", __func__, rc);
diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c
index b107040..23903df 100644
--- a/drivers/power/pm8921-bms.c
+++ b/drivers/power/pm8921-bms.c
@@ -148,8 +148,9 @@
int ibat_at_cv_ua;
int soc_at_cv;
int prev_chg_soc;
-
struct power_supply *batt_psy;
+ bool low_voltage_wake_lock_held;
+ struct wake_lock low_voltage_wake_lock;
};
/*
@@ -639,6 +640,26 @@
raw->last_good_ocv_raw -= MBG_TRANSIENT_ERROR_RAW;
}
+#define SEL_ALT_OREG_BIT BIT(2)
+static int ocv_ir_compensation(struct pm8921_bms_chip *chip, int ocv)
+{
+ int compensated_ocv;
+ int ibatt_ua;
+ int rbatt_mohm = chip->default_rbatt_mohm + chip->rconn_mohm;
+
+ pm_bms_masked_write(chip, BMS_TEST1,
+ SEL_ALT_OREG_BIT, SEL_ALT_OREG_BIT);
+
+ /* since the SEL_ALT_OREG_BIT is set this will give us VSENSE_OCV */
+ pm8921_bms_get_battery_current(&ibatt_ua);
+ compensated_ocv = ocv + div_s64((s64)ibatt_ua * rbatt_mohm, 1000);
+ pr_debug("comp ocv = %d, ocv = %d, ibatt_ua = %d, rbatt_mohm = %d\n",
+ compensated_ocv, ocv, ibatt_ua, rbatt_mohm);
+
+ pm_bms_masked_write(chip, BMS_TEST1, SEL_ALT_OREG_BIT, 0);
+ return compensated_ocv;
+}
+
static int read_soc_params_raw(struct pm8921_bms_chip *chip,
struct pm8921_soc_params *raw)
{
@@ -661,6 +682,8 @@
adjust_pon_ocv_raw(chip, raw);
convert_vbatt_raw_to_uv(chip, usb_chg,
raw->last_good_ocv_raw, &raw->last_good_ocv_uv);
+ raw->last_good_ocv_uv = ocv_ir_compensation(chip,
+ raw->last_good_ocv_uv);
chip->last_ocv_uv = raw->last_good_ocv_uv;
pr_debug("PON_OCV_UV = %d\n", chip->last_ocv_uv);
} else if (chip->prev_last_good_ocv_raw != raw->last_good_ocv_raw) {
@@ -1011,7 +1034,7 @@
* samples with the the shutdown_iavg_ua
*/
if (firsttime && chip->shutdown_iavg_ua != 0) {
- pr_emerg("Using shutdown_iavg_ua = %d in all samples\n",
+ pr_debug("Using shutdown_iavg_ua = %d in all samples\n",
chip->shutdown_iavg_ua);
for (i = 0; i < IAVG_SAMPLES; i++)
iavg_samples[i] = chip->shutdown_iavg_ua;
@@ -1319,6 +1342,28 @@
return chip->prev_chg_soc;
}
+static void very_low_voltage_check(struct pm8921_bms_chip *chip,
+ int ibat_ua, int vbat_uv)
+{
+ /*
+ * if battery is very low (v_cutoff voltage + 20mv) hold
+ * a wakelock untill soc = 0%
+ */
+ if (vbat_uv <= (chip->v_cutoff + 20) * 1000
+ && !chip->low_voltage_wake_lock_held) {
+ pr_debug("voltage = %d low holding wakelock\n", vbat_uv);
+ wake_lock(&chip->low_voltage_wake_lock);
+ chip->low_voltage_wake_lock_held = 1;
+ }
+
+ if (vbat_uv > (chip->v_cutoff + 20) * 1000
+ && chip->low_voltage_wake_lock_held) {
+ pr_debug("voltage = %d releasing wakelock\n", vbat_uv);
+ chip->low_voltage_wake_lock_held = 0;
+ wake_unlock(&chip->low_voltage_wake_lock);
+ }
+}
+
static int last_soc_est = -EINVAL;
static int adjust_soc(struct pm8921_bms_chip *chip, int soc,
int batt_temp, int chargecycles,
@@ -1343,6 +1388,7 @@
goto out;
}
+ very_low_voltage_check(chip, ibat_ua, vbat_uv);
delta_ocv_uv_limit = DIV_ROUND_CLOSEST(ibat_ua, 1000);
@@ -2308,12 +2354,6 @@
return IRQ_HANDLED;
}
-static irqreturn_t pm8921_bms_vsense_avg_handler(int irq, void *data)
-{
- pr_debug("irq = %d triggered", irq);
- return IRQ_HANDLED;
-}
-
struct pm_bms_irq_init_data {
unsigned int irq_id;
char *name;
@@ -2342,8 +2382,6 @@
pm8921_bms_ocv_for_r_handler),
BMS_IRQ(PM8921_BMS_GOOD_OCV, IRQF_TRIGGER_RISING,
pm8921_bms_good_ocv_handler),
- BMS_IRQ(PM8921_BMS_VSENSE_AVG, IRQF_TRIGGER_RISING,
- pm8921_bms_vsense_avg_handler),
};
static void free_irqs(struct pm8921_bms_chip *chip)
@@ -2897,6 +2935,9 @@
goto free_chip;
}
+ wake_lock_init(&chip->low_voltage_wake_lock,
+ WAKE_LOCK_SUSPEND, "pm8921_bms_low");
+
rc = pm8921_bms_hw_init(chip);
if (rc) {
pr_err("couldn't init hardware rc = %d\n", rc);
@@ -2954,12 +2995,33 @@
return 0;
}
+static int pm8921_bms_resume(struct device *dev)
+{
+ int rc, ibat_ua, vbat_uv;
+
+ rc = pm8921_bms_get_simultaneous_battery_voltage_and_current(
+ &ibat_ua,
+ &vbat_uv);
+ if (rc < 0) {
+ pr_err("simultaneous vbat ibat failed err = %d\n", rc);
+ return 0;
+ }
+
+ very_low_voltage_check(the_chip, ibat_ua, vbat_uv);
+ return 0;
+}
+
+static const struct dev_pm_ops pm8921_bms_pm_ops = {
+ .resume = pm8921_bms_resume,
+};
+
static struct platform_driver pm8921_bms_driver = {
.probe = pm8921_bms_probe,
.remove = __devexit_p(pm8921_bms_remove),
.driver = {
.name = PM8921_BMS_DEV_NAME,
.owner = THIS_MODULE,
+ .pm = &pm8921_bms_pm_ops,
},
};
diff --git a/drivers/power/pm8921-charger.c b/drivers/power/pm8921-charger.c
index e983081..3977f17 100644
--- a/drivers/power/pm8921-charger.c
+++ b/drivers/power/pm8921-charger.c
@@ -27,6 +27,8 @@
#include <linux/workqueue.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
+#include <linux/mfd/pm8xxx/batt-alarm.h>
+#include <linux/ratelimit.h>
#include <mach/msm_xo.h>
#include <mach/msm_hsusb.h>
@@ -115,6 +117,13 @@
int batt_state;
};
+static int pm8921_battery_gauge_alarm_notify(struct notifier_block *nb,
+ unsigned long status, void *unused);
+
+static struct notifier_block alarm_notifier = {
+ .notifier_call = pm8921_battery_gauge_alarm_notify,
+};
+
static struct fsm_state_to_batt_status map[] = {
{FSM_STATE_OFF_0, POWER_SUPPLY_STATUS_UNKNOWN},
{FSM_STATE_BATFETDET_START_12, POWER_SUPPLY_STATUS_UNKNOWN},
@@ -205,6 +214,8 @@
* @max_voltage_mv: the max volts the batt should be charged up to
* @min_voltage_mv: the min battery voltage before turning the FETon
* @uvd_voltage_mv: (PM8917 only) the falling UVD threshold voltage
+ * @alarm_low_mv: the battery alarm voltage low
+ * @alarm_high_mv: the battery alarm voltage high
* @cool_temp_dc: the cool temp threshold in deciCelcius
* @warm_temp_dc: the warm temp threshold in deciCelcius
* @resume_voltage_delta: the voltage delta from vdd max at which the
@@ -225,6 +236,8 @@
unsigned int max_voltage_mv;
unsigned int min_voltage_mv;
unsigned int uvd_voltage_mv;
+ unsigned int alarm_low_mv;
+ unsigned int alarm_high_mv;
int cool_temp_dc;
int warm_temp_dc;
unsigned int temp_check_period;
@@ -235,6 +248,7 @@
unsigned int is_bat_cool;
unsigned int is_bat_warm;
unsigned int resume_voltage_delta;
+ int resume_charge_percent;
unsigned int term_current;
unsigned int vbat_channel;
unsigned int batt_temp_channel;
@@ -250,6 +264,7 @@
bool ext_charge_done;
bool iusb_fine_res;
bool dc_unplug_check;
+ bool disable_hw_clock_switching;
DECLARE_BITMAP(enabled_irqs, PM_CHG_MAX_INTS);
struct work_struct battery_id_valid_work;
int64_t batt_id_min;
@@ -1414,7 +1429,24 @@
percent_soc = voltage_based_capacity(chip);
if (percent_soc <= 10)
- pr_warn("low battery charge = %d%%\n", percent_soc);
+ pr_warn_ratelimited("low battery charge = %d%%\n",
+ percent_soc);
+
+ if (chip->recent_reported_soc == (chip->resume_charge_percent + 1)
+ && percent_soc == chip->resume_charge_percent) {
+ pr_debug("soc fell below %d. charging enabled.\n",
+ chip->resume_charge_percent);
+ if (chip->is_bat_warm)
+ pr_warn_ratelimited("battery is warm = %d, do not resume charging at %d%%.\n",
+ chip->is_bat_warm,
+ chip->resume_charge_percent);
+ else if (chip->is_bat_cool)
+ pr_warn_ratelimited("battery is cool = %d, do not resume charging at %d%%.\n",
+ chip->is_bat_cool,
+ chip->resume_charge_percent);
+ else
+ pm_chg_vbatdet_set(the_chip, PM8921_CHG_VBATDET_MAX);
+ }
chip->recent_reported_soc = percent_soc;
return percent_soc;
@@ -2003,6 +2035,73 @@
return get_prop_batt_temp(the_chip);
}
+static int pm8921_charger_enable_batt_alarm(struct pm8921_chg_chip *chip)
+{
+ int rc = 0;
+
+ rc = pm8xxx_batt_alarm_disable(PM8XXX_BATT_ALARM_UPPER_COMPARATOR);
+ if (!rc)
+ rc = pm8xxx_batt_alarm_enable(
+ PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
+ if (rc) {
+ pr_err("unable to set batt alarm state rc=%d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+static int pm8921_charger_configure_batt_alarm(struct pm8921_chg_chip *chip)
+{
+ int rc = 0;
+
+ rc = pm8xxx_batt_alarm_disable(PM8XXX_BATT_ALARM_UPPER_COMPARATOR);
+ if (!rc)
+ rc = pm8xxx_batt_alarm_disable(
+ PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
+ if (rc) {
+ pr_err("unable to set batt alarm state rc=%d\n", rc);
+ return rc;
+ }
+
+ /*
+ * The batt-alarm driver requires sane values for both min / max,
+ * regardless of whether they're both activated.
+ */
+ rc = pm8xxx_batt_alarm_threshold_set(
+ PM8XXX_BATT_ALARM_LOWER_COMPARATOR,
+ chip->alarm_low_mv);
+ if (!rc)
+ rc = pm8xxx_batt_alarm_threshold_set(
+ PM8XXX_BATT_ALARM_UPPER_COMPARATOR,
+ chip->alarm_high_mv);
+ if (rc) {
+ pr_err("unable to set batt alarm threshold rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = pm8xxx_batt_alarm_hold_time_set(
+ PM8XXX_BATT_ALARM_HOLD_TIME_16_MS);
+ if (rc) {
+ pr_err("unable to set batt alarm hold time rc=%d\n", rc);
+ return rc;
+ }
+
+ /* PWM enabled at 2Hz */
+ rc = pm8xxx_batt_alarm_pwm_rate_set(1, 7, 4);
+ if (rc) {
+ pr_err("unable to set batt alarm pwm rate rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = pm8xxx_batt_alarm_register_notifier(&alarm_notifier);
+ if (rc) {
+ pr_err("unable to register alarm notifier rc=%d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
static void handle_usb_insertion_removal(struct pm8921_chg_chip *chip)
{
int usb_present;
@@ -2138,6 +2237,61 @@
}
}
+static int pm8921_battery_gauge_alarm_notify(struct notifier_block *nb,
+ unsigned long status, void *unused)
+{
+ int rc;
+
+ pr_info("status: %lu\n", status);
+
+ /* Check if called before init */
+
+ switch (status) {
+ case 0:
+ pr_err("spurious interrupt\n");
+ break;
+ /* expected case - trip of low threshold */
+ case 1:
+ if (!the_chip) {
+ pr_err("not initialized\n");
+ return -EINVAL;
+ }
+
+ the_chip->disable_hw_clock_switching = 1;
+
+ rc = pm8xxx_batt_alarm_disable(
+ PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
+ if (!rc)
+ rc = pm8xxx_batt_alarm_enable(
+ PM8XXX_BATT_ALARM_UPPER_COMPARATOR);
+ if (rc)
+ pr_err("unable to set alarm state rc=%d\n", rc);
+ break;
+ case 2:
+ if (!the_chip) {
+ pr_err("not initialized\n");
+ return -EINVAL;
+ }
+
+ the_chip->disable_hw_clock_switching = 0;
+
+ rc = pm8xxx_batt_alarm_disable(
+ PM8XXX_BATT_ALARM_UPPER_COMPARATOR);
+ if (!rc)
+ rc = pm8xxx_batt_alarm_enable(
+ PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
+ if (rc)
+ pr_err("unable to set alarm state rc=%d\n", rc);
+
+ pr_err("trip of high threshold\n");
+ break;
+ default:
+ pr_err("error received\n");
+ };
+
+ return 0;
+}
+
static void turn_on_ovp_fet(struct pm8921_chg_chip *chip, u16 ovptestreg)
{
u8 temp;
@@ -2486,6 +2640,11 @@
pr_debug("USB charger active\n");
pm_chg_iusbmax_get(chip, &usb_ma);
+ if (usb_ma == 500 && !usb_target_ma) {
+ pr_debug("Stopping Unplug Check Worker USB == 500mA\n");
+ disable_input_voltage_regulation(chip);
+ return;
+ }
if (usb_ma <= 100) {
pr_debug(
@@ -2763,6 +2922,12 @@
handle_start_ext_chg(chip);
else
handle_stop_ext_chg(chip);
+
+ if (!chip->ext_psy) {
+ power_supply_changed(&chip->dc_psy);
+ power_supply_changed(&chip->batt_psy);
+ }
+
return IRQ_HANDLED;
}
@@ -2845,12 +3010,10 @@
module_param(ichg_threshold_ua, int, 0644);
#define PM8921_CHG_VDDMAX_RES_MV 10
-static void adjust_vdd_max_for_fastchg(struct pm8921_chg_chip *chip)
+static void adjust_vdd_max_for_fastchg(struct pm8921_chg_chip *chip,
+ int vbat_batt_terminal_uv)
{
- int ichg_meas_ua, vbat_uv;
- int ichg_meas_ma;
int adj_vdd_max_mv, programmed_vdd_max;
- int vbat_batt_terminal_uv;
int vbat_batt_terminal_mv;
int reg_loop;
int delta_mv = 0;
@@ -2872,18 +3035,6 @@
reg_loop);
return;
}
-
- pm8921_bms_get_simultaneous_battery_voltage_and_current(&ichg_meas_ua,
- &vbat_uv);
- if (ichg_meas_ua >= 0) {
- pr_debug("Exiting ichg_meas_ua = %d > 0\n", ichg_meas_ua);
- return;
- }
-
- ichg_meas_ma = ichg_meas_ua / 1000;
-
- /* rconn_mohm is in milliOhms */
- vbat_batt_terminal_uv = vbat_uv + ichg_meas_ma * the_chip->rconn_mohm;
vbat_batt_terminal_mv = vbat_batt_terminal_uv/1000;
pm_chg_vddmax_get(the_chip, &programmed_vdd_max);
@@ -2919,10 +3070,10 @@
#define VBAT_TOLERANCE_MV 70
#define CHG_DISABLE_MSLEEP 100
-static int is_charging_finished(struct pm8921_chg_chip *chip)
+static int is_charging_finished(struct pm8921_chg_chip *chip,
+ int vbat_batt_terminal_uv, int ichg_meas_ma)
{
- int vbat_meas_uv, vbat_meas_mv, vbat_programmed, vbatdet_low;
- int ichg_meas_ma, iterm_programmed;
+ int vbat_programmed, iterm_programmed, vbat_intended;
int regulation_loop, fast_chg, vcp;
int rc;
static int last_vbat_programmed = -EINVAL;
@@ -2939,30 +3090,19 @@
if (vcp == 1)
return CHG_IN_PROGRESS;
- vbatdet_low = pm_chg_get_rt_status(chip, VBATDET_LOW_IRQ);
- pr_debug("vbatdet_low = %d\n", vbatdet_low);
- if (vbatdet_low == 1)
- return CHG_IN_PROGRESS;
-
/* reset count if battery is hot/cold */
rc = pm_chg_get_rt_status(chip, BAT_TEMP_OK_IRQ);
pr_debug("batt_temp_ok = %d\n", rc);
if (rc == 0)
return CHG_IN_PROGRESS;
- /* reset count if battery voltage is less than vddmax */
- vbat_meas_uv = get_prop_battery_uvolts(chip);
- if (vbat_meas_uv < 0)
- return CHG_IN_PROGRESS;
- vbat_meas_mv = vbat_meas_uv / 1000;
-
rc = pm_chg_vddmax_get(chip, &vbat_programmed);
if (rc) {
pr_err("couldnt read vddmax rc = %d\n", rc);
return CHG_IN_PROGRESS;
}
- pr_debug("vddmax = %d vbat_meas_mv=%d\n",
- vbat_programmed, vbat_meas_mv);
+ pr_debug("vddmax = %d vbat_batt_terminal_uv=%d\n",
+ vbat_programmed, vbat_batt_terminal_uv);
if (last_vbat_programmed == -EINVAL)
last_vbat_programmed = vbat_programmed;
@@ -2974,6 +3114,20 @@
return CHG_IN_PROGRESS;
}
+ if (chip->is_bat_cool)
+ vbat_intended = chip->cool_bat_voltage;
+ else if (chip->is_bat_warm)
+ vbat_intended = chip->warm_bat_voltage;
+ else
+ vbat_intended = chip->max_voltage_mv;
+
+ if (vbat_batt_terminal_uv / 1000 < vbat_intended) {
+ pr_debug("terminal_uv:%d < vbat_intended:%d.\n",
+ vbat_batt_terminal_uv,
+ vbat_intended);
+ return CHG_IN_PROGRESS;
+ }
+
regulation_loop = pm_chg_get_regulation_loop(chip);
if (regulation_loop < 0) {
pr_err("couldnt read the regulation loop err=%d\n",
@@ -2993,7 +3147,6 @@
return CHG_IN_PROGRESS;
}
- ichg_meas_ma = (get_prop_batt_current(chip)) / 1000;
pr_debug("iterm_programmed = %d ichg_meas_ma=%d\n",
iterm_programmed, ichg_meas_ma);
/*
@@ -3028,9 +3181,22 @@
struct pm8921_chg_chip, eoc_work);
static int count;
int end;
+ int vbat_meas_uv, vbat_meas_mv;
+ int ichg_meas_ua, ichg_meas_ma;
+ int vbat_batt_terminal_uv;
pm_chg_failed_clear(chip, 1);
- end = is_charging_finished(chip);
+
+ pm8921_bms_get_simultaneous_battery_voltage_and_current(
+ &ichg_meas_ua, &vbat_meas_uv);
+ vbat_meas_mv = vbat_meas_uv / 1000;
+ /* rconn_mohm is in milliOhms */
+ ichg_meas_ma = ichg_meas_ua / 1000;
+ vbat_batt_terminal_uv = vbat_meas_uv
+ + ichg_meas_ma
+ * the_chip->rconn_mohm;
+
+ end = is_charging_finished(chip, vbat_batt_terminal_uv, ichg_meas_ma);
if (end == CHG_NOT_IN_PROGRESS) {
count = 0;
@@ -3038,6 +3204,18 @@
return;
}
+ /* If the disable hw clock switching
+ * flag was set it can now be unset. Also, re-enable
+ * the battery alarm to set the flag again when needed
+ */
+ if (chip->disable_hw_clock_switching) {
+ /* Unset the hw clock switching flag */
+ chip->disable_hw_clock_switching = 0;
+
+ if (pm8921_charger_enable_batt_alarm(chip))
+ pr_err("couldn't set up batt alarm!\n");
+ }
+
if (end == CHG_FINISHED) {
count++;
} else {
@@ -3047,6 +3225,21 @@
if (count == CONSECUTIVE_COUNT) {
count = 0;
pr_info("End of Charging\n");
+ /* set the vbatdet back, in case it was changed
+ * to trigger charging */
+ if (chip->is_bat_cool) {
+ pm_chg_vbatdet_set(the_chip,
+ the_chip->cool_bat_voltage
+ - the_chip->resume_voltage_delta);
+ } else if (chip->is_bat_warm) {
+ pm_chg_vbatdet_set(the_chip,
+ the_chip->warm_bat_voltage
+ - the_chip->resume_voltage_delta);
+ } else {
+ pm_chg_vbatdet_set(the_chip,
+ the_chip->max_voltage_mv
+ - the_chip->resume_voltage_delta);
+ }
pm_chg_auto_enable(chip, 0);
@@ -3061,7 +3254,7 @@
chgdone_irq_handler(chip->pmic_chg_irq[CHGDONE_IRQ], chip);
wake_unlock(&chip->eoc_wake_lock);
} else {
- adjust_vdd_max_for_fastchg(chip);
+ adjust_vdd_max_for_fastchg(chip, vbat_batt_terminal_uv);
pr_debug("EOC count = %d\n", count);
schedule_delayed_work(&chip->eoc_work,
round_jiffies_relative(msecs_to_jiffies
@@ -3297,6 +3490,7 @@
{
unsigned long flags;
int fsm_state;
+ int is_fast_chg;
chip->dc_present = !!is_dc_chg_plugged_in(chip);
chip->usb_present = !!is_usb_chg_plugged_in(chip);
@@ -3324,9 +3518,17 @@
if (usb_chg_current) {
/* reissue a vbus draw call */
__pm8921_charger_vbus_draw(usb_chg_current);
- fastchg_irq_handler(chip->pmic_chg_irq[FASTCHG_IRQ], chip);
}
spin_unlock_irqrestore(&vbus_lock, flags);
+ /*
+ * The bootloader could have started charging, a fastchg interrupt
+ * might not happen. Check the real time status and if it is fast
+ * charging invoke the handler so that the eoc worker could be
+ * started
+ */
+ is_fast_chg = pm_chg_get_rt_status(chip, FASTCHG_IRQ);
+ if (is_fast_chg)
+ fastchg_irq_handler(chip->pmic_chg_irq[FASTCHG_IRQ], chip);
fsm_state = pm_chg_get_fsm_state(chip);
if (is_battery_charging(fsm_state)) {
@@ -3546,10 +3748,12 @@
#define CHG_VCP_EN BIT(0)
#define CHG_BAT_TEMP_DIS_BIT BIT(2)
#define SAFE_CURRENT_MA 1500
+#define PM_SUB_REV 0x001
static int __devinit pm8921_chg_hw_init(struct pm8921_chg_chip *chip)
{
int rc;
int vdd_safe;
+ u8 subrev;
/* forcing 19p2mhz before accessing any charger registers */
pm8921_chg_force_19p2mhz_clk(chip);
@@ -3728,8 +3932,21 @@
/* Workarounds for die 3.0 */
if (pm8xxx_get_revision(chip->dev->parent) == PM8XXX_REVISION_8921_3p0
- && pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8921)
- pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0xAC);
+ && pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8921) {
+ rc = pm8xxx_readb(chip->dev->parent, PM_SUB_REV, &subrev);
+ if (rc) {
+ pr_err("read failed: addr=%03X, rc=%d\n",
+ PM_SUB_REV, rc);
+ return rc;
+ }
+ /* Check if die 3.0.1 is present */
+ if (subrev == 0x1)
+ pm8xxx_writeb(chip->dev->parent,
+ CHG_BUCK_CTRL_TEST3, 0xA4);
+ else
+ pm8xxx_writeb(chip->dev->parent,
+ CHG_BUCK_CTRL_TEST3, 0xAC);
+ }
/* Enable isub_fine resolution AICL for PM8917 */
if (pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8917) {
@@ -3944,7 +4161,8 @@
rc = pm_chg_masked_write(chip, CHG_CNTRL, VREF_BATT_THERM_FORCE_ON, 0);
if (rc)
pr_err("Failed to Force Vref therm off rc=%d\n", rc);
- pm8921_chg_set_hw_clk_switching(chip);
+ if (!(chip->disable_hw_clock_switching))
+ pm8921_chg_set_hw_clk_switching(chip);
return 0;
}
@@ -4027,9 +4245,12 @@
chip->ttrkl_time = pdata->ttrkl_time;
chip->update_time = pdata->update_time;
chip->max_voltage_mv = pdata->max_voltage;
+ chip->alarm_low_mv = pdata->alarm_low_mv;
+ chip->alarm_high_mv = pdata->alarm_high_mv;
chip->min_voltage_mv = pdata->min_voltage;
chip->uvd_voltage_mv = pdata->uvd_thresh_voltage;
chip->resume_voltage_delta = pdata->resume_voltage_delta;
+ chip->resume_charge_percent = pdata->resume_charge_percent;
chip->term_current = pdata->term_current;
chip->vbat_channel = pdata->charger_cdata.vbat_channel;
chip->batt_temp_channel = pdata->charger_cdata.batt_temp_channel;
@@ -4147,6 +4368,17 @@
}
}
+ rc = pm8921_charger_configure_batt_alarm(chip);
+ if (rc) {
+ pr_err("Couldn't configure battery alarm! rc=%d\n", rc);
+ goto free_irq;
+ }
+
+ rc = pm8921_charger_enable_batt_alarm(chip);
+ if (rc) {
+ pr_err("Couldn't enable battery alarm! rc=%d\n", rc);
+ goto free_irq;
+ }
create_debugfs_entries(chip);
INIT_WORK(&chip->bms_notify.work, bms_notify);
diff --git a/drivers/power/pm8xxx-ccadc.c b/drivers/power/pm8xxx-ccadc.c
index e48257a..1b9426a 100644
--- a/drivers/power/pm8xxx-ccadc.c
+++ b/drivers/power/pm8xxx-ccadc.c
@@ -480,6 +480,9 @@
struct pm8xxx_ccadc_chip *chip = data;
int rc;
+ if (!the_chip)
+ goto out;
+
pr_debug("irq = %d triggered\n", irq);
data_msb = chip->ccadc_offset >> 8;
data_lsb = chip->ccadc_offset;
@@ -488,6 +491,7 @@
data_msb, data_lsb, 0);
disable_irq_nosync(chip->eoc_irq);
+out:
return IRQ_HANDLED;
}
@@ -685,7 +689,6 @@
goto free_chip;
}
-
disable_irq_nosync(chip->eoc_irq);
platform_set_drvdata(pdev, chip);
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
index 352e60e..61f4946 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -47,6 +47,41 @@
EXPORT_SYMBOL_GPL(power_supply_set_current_limit);
/**
+ * power_supply_set_charging_enabled - enable or disable charging
+ * @psy: the power supply to control
+ * @enable: sets enable property of power supply
+ */
+int power_supply_set_charging_enabled(struct power_supply *psy, bool enable)
+{
+ const union power_supply_propval ret = {enable,};
+
+ if (psy->set_property)
+ return psy->set_property(psy,
+ POWER_SUPPLY_PROP_CHARGING_ENABLED,
+ &ret);
+
+ return -ENXIO;
+}
+EXPORT_SYMBOL_GPL(power_supply_set_charging_enabled);
+
+/**
+ * power_supply_set_present - set present state of the power supply
+ * @psy: the power supply to control
+ * @enable: sets present property of power supply
+ */
+int power_supply_set_present(struct power_supply *psy, bool enable)
+{
+ const union power_supply_propval ret = {enable,};
+
+ if (psy->set_property)
+ return psy->set_property(psy, POWER_SUPPLY_PROP_PRESENT,
+ &ret);
+
+ return -ENXIO;
+}
+EXPORT_SYMBOL_GPL(power_supply_set_present);
+
+/**
* power_supply_set_online - set online state of the power supply
* @psy: the power supply to control
* @enable: sets online property of power supply
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index 4368e7d..7eb285b 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -138,6 +138,7 @@
POWER_SUPPLY_ATTR(health),
POWER_SUPPLY_ATTR(present),
POWER_SUPPLY_ATTR(online),
+ POWER_SUPPLY_ATTR(charging_enabled),
POWER_SUPPLY_ATTR(technology),
POWER_SUPPLY_ATTR(cycle_count),
POWER_SUPPLY_ATTR(voltage_max),
diff --git a/drivers/power/qpnp-charger.c b/drivers/power/qpnp-charger.c
index dda8976..8d5244b 100644
--- a/drivers/power/qpnp-charger.c
+++ b/drivers/power/qpnp-charger.c
@@ -20,6 +20,7 @@
#include <linux/of_device.h>
#include <linux/radix-tree.h>
#include <linux/interrupt.h>
+#include <linux/delay.h>
#include <linux/qpnp/qpnp-adc.h>
#include <linux/power_supply.h>
#include <linux/bitops.h>
@@ -69,14 +70,17 @@
#define CHGR_CHG_WDOG_PET 0x64
#define CHGR_CHG_WDOG_EN 0x65
#define CHGR_USB_IUSB_MAX 0x44
+#define CHGR_USB_USB_SUSP 0x47
#define CHGR_USB_ENUM_T_STOP 0x4E
#define CHGR_CHG_TEMP_THRESH 0x66
#define CHGR_BAT_IF_PRES_STATUS 0x08
-#define CHGR_BAT_TEMP_STATUS 0x09
+#define CHGR_STATUS 0x09
#define CHGR_BAT_IF_VCP 0x42
#define CHGR_BAT_IF_BATFET_CTRL1 0x90
#define CHGR_MISC_BOOT_DONE 0x42
+#define CHGR_BUCK_COMPARATOR_OVRIDE_3 0xED
#define MISC_REVISION2 0x01
+#define SEC_ACCESS 0xD0
/* SMBB peripheral subtype values */
#define REG_OFFSET_PERP_SUBTYPE 0x05
@@ -90,6 +94,11 @@
#define QPNP_CHARGER_DEV_NAME "qcom,qpnp-charger"
+/* Status bits and masks */
+#define CHGR_BOOT_DONE BIT(7)
+#define CHGR_CHG_EN BIT(7)
+#define CHGR_ON_BAT_FORCE_BIT BIT(0)
+
/* Interrupt definitions */
/* smbb_chg_interrupts */
#define CHG_DONE_IRQ BIT(7)
@@ -133,6 +142,9 @@
/* smbb_misc_interrupts */
#define TFTWDOG_IRQ BIT(0)
+/* Workaround flags */
+#define CHG_FLAGS_VCP_WA BIT(0)
+
/**
* struct qpnp_chg_chip - device information
* @dev: device pointer to access the parent
@@ -156,6 +168,8 @@
* @usb_psy power supply to export information to userspace
* @bms_psy power supply to export information to userspace
* @batt_psy: power supply to export information to userspace
+ * @flags: flags to activate specific workarounds
+ * throughout the driver
*
*/
struct qpnp_chg_chip {
@@ -185,10 +199,11 @@
struct power_supply *usb_psy;
struct power_supply *bms_psy;
struct power_supply batt_psy;
+ uint32_t flags;
};
static struct qpnp_chg_chip *the_chip;
-static int charging_disabled;
+static bool charging_disabled;
static struct of_device_id qpnp_charger_match_table[] = {
{ .compatible = QPNP_CHARGER_DEV_NAME, },
@@ -258,6 +273,7 @@
return 0;
}
+#define USB_VALID_BIT BIT(7)
static int
qpnp_chg_is_usb_chg_plugged_in(struct qpnp_chg_chip *chip)
{
@@ -265,16 +281,16 @@
int rc;
rc = qpnp_chg_read(chip, &usbin_valid_rt_sts,
- INT_RT_STS(chip->usb_chgpth_base), 1);
+ chip->usb_chgpth_base + CHGR_STATUS , 1);
if (rc) {
pr_err("spmi read failed: addr=%03X, rc=%d\n",
- INT_RT_STS(chip->usb_chgpth_base), rc);
+ chip->usb_chgpth_base + CHGR_STATUS, rc);
return rc;
}
pr_debug("chgr usb sts 0x%x\n", usbin_valid_rt_sts);
- return (usbin_valid_rt_sts & USBIN_VALID_IRQ) ? 1 : 0;
+ return (usbin_valid_rt_sts & USB_VALID_BIT) ? 1 : 0;
}
static int
@@ -294,7 +310,6 @@
return (dcin_valid_rt_sts & DCIN_VALID_IRQ) ? 1 : 0;
}
-#define VCP_IUSBMAX_SETTING_MA 2000
#define QPNP_CHG_IUSB_MAX_MIN_100 100
#define QPNP_CHG_IUSB_MAX_MIN_150 150
#define QPNP_CHG_IUSB_MAX_MIN_MA 200
@@ -303,7 +318,8 @@
static int
qpnp_chg_iusbmax_set(struct qpnp_chg_chip *chip, int mA)
{
- u8 usb_reg = 0;
+ int rc = 0;
+ u8 usb_reg = 0, temp = 8;
if (mA == QPNP_CHG_IUSB_MAX_MIN_100) {
usb_reg = 0x00;
@@ -323,17 +339,42 @@
return -EINVAL;
}
- /* Hack for VCP issue make sure IUSBMAX setting
- * is at least 2 A to not brown out device */
- mA = VCP_IUSBMAX_SETTING_MA;
-
usb_reg = mA / QPNP_CHG_IUSB_MAX_STEP_MA;
+ if (chip->flags & CHG_FLAGS_VCP_WA) {
+ temp = 0xA5;
+ rc = qpnp_chg_write(chip, &temp,
+ chip->buck_base + SEC_ACCESS, 1);
+ rc = qpnp_chg_masked_write(chip,
+ chip->buck_base + CHGR_BUCK_COMPARATOR_OVRIDE_3,
+ 0x0C, 0x0C, 1);
+ }
+
pr_debug("current=%d setting 0x%x\n", mA, usb_reg);
- return qpnp_chg_write(chip, &usb_reg,
+ rc = qpnp_chg_write(chip, &usb_reg,
chip->usb_chgpth_base + CHGR_USB_IUSB_MAX, 1);
- pr_debug("done\n");
- return 0;
+
+ if (chip->flags & CHG_FLAGS_VCP_WA) {
+ temp = 0xA5;
+ udelay(200);
+ rc = qpnp_chg_write(chip, &temp,
+ chip->buck_base + SEC_ACCESS, 1);
+ rc = qpnp_chg_masked_write(chip,
+ chip->buck_base + CHGR_BUCK_COMPARATOR_OVRIDE_3,
+ 0x0C, 0x00, 1);
+ }
+
+ return rc;
+}
+
+#define USB_SUSPEND_BIT BIT(0)
+static int
+qpnp_chg_usb_suspend_enable(struct qpnp_chg_chip *chip, int enable)
+{
+ return qpnp_chg_masked_write(chip,
+ chip->usb_chgpth_base + CHGR_USB_USB_SUSP,
+ USB_SUSPEND_BIT,
+ enable ? USB_SUSPEND_BIT : 0, 1);
}
#define ENUM_T_STOP_BIT BIT(0)
@@ -350,11 +391,6 @@
chip->usb_present = usb_present;
power_supply_set_present(chip->usb_psy,
chip->usb_present);
- } else if (!(chip->usb_present && usb_present)) {
- qpnp_chg_masked_write(chip,
- chip->usb_chgpth_base + CHGR_USB_ENUM_T_STOP,
- ENUM_T_STOP_BIT,
- ENUM_T_STOP_BIT, 1);
}
return IRQ_HANDLED;
@@ -365,23 +401,15 @@
qpnp_chg_chgr_chg_failed_irq_handler(int irq, void *_chip)
{
struct qpnp_chg_chip *chip = _chip;
- int rc, usb_present;
+ int rc;
rc = qpnp_chg_masked_write(chip,
- chip->usb_chgpth_base + CHGR_CHG_FAILED,
+ chip->chgr_base + CHGR_CHG_FAILED,
CHGR_CHG_FAILED_BIT,
CHGR_CHG_FAILED_BIT, 1);
if (rc)
pr_err("Failed to write chg_fail clear bit!\n");
- /* Hack: recheck usbin_valid status after chg_fail triggered */
- usb_present = qpnp_chg_is_usb_chg_plugged_in(chip);
- pr_debug("usb_status: %d\n", usb_present);
- if (usb_present)
- qpnp_chg_usb_usbin_valid_irq_handler(chip->usbin_valid_irq,
- _chip);
-
-
return IRQ_HANDLED;
}
@@ -402,6 +430,7 @@
};
static enum power_supply_property msm_batt_power_props[] = {
+ POWER_SUPPLY_PROP_CHARGING_ENABLED,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_HEALTH,
@@ -488,7 +517,7 @@
int rc;
rc = qpnp_chg_read(chip, &batt_health,
- chip->bat_if_base + CHGR_BAT_TEMP_STATUS, 1);
+ chip->bat_if_base + CHGR_STATUS, 1);
if (rc) {
pr_err("Couldn't read battery health read failed rc=%d\n", rc);
return POWER_SUPPLY_HEALTH_UNKNOWN;
@@ -647,8 +676,13 @@
chip->usb_psy->get_property(chip->usb_psy,
POWER_SUPPLY_PROP_CURRENT_MAX, &ret);
qpnp_chg_iusbmax_set(chip, ret.intval / 1000);
+ if ((ret.intval / 1000) <= QPNP_CHG_IUSB_MAX_MIN_MA)
+ qpnp_chg_usb_suspend_enable(chip, 1);
+ else
+ qpnp_chg_usb_suspend_enable(chip, 0);
} else {
- qpnp_chg_iusbmax_set(chip, QPNP_CHG_IUSB_MAX_MIN_MA);
+ qpnp_chg_iusbmax_set(chip, QPNP_CHG_IUSB_MAX_MIN_100);
+ qpnp_chg_usb_suspend_enable(chip, 0);
}
pr_debug("end of power supply changed\n");
@@ -656,6 +690,24 @@
}
static int
+qpnp_chg_charge_dis(struct qpnp_chg_chip *chip, int disable)
+{
+ /* This bit forces the charger to run off of the battery rather
+ * than a connected charger */
+ return qpnp_chg_masked_write(chip, chip->chgr_base + CHGR_CHG_CTRL,
+ CHGR_ON_BAT_FORCE_BIT,
+ disable ? CHGR_ON_BAT_FORCE_BIT : 0, 1);
+}
+
+static int
+qpnp_chg_charge_en(struct qpnp_chg_chip *chip, int enable)
+{
+ return qpnp_chg_masked_write(chip, chip->chgr_base + CHGR_CHG_CTRL,
+ CHGR_CHG_EN,
+ enable ? CHGR_CHG_EN : 0, 1);
+}
+
+static int
qpnp_batt_power_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
@@ -700,6 +752,9 @@
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
val->intval = get_prop_full_design(chip);
break;
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ val->intval = !charging_disabled;
+ break;
default:
return -EINVAL;
}
@@ -707,27 +762,28 @@
return 0;
}
-#define CHGR_BOOT_DONE BIT(7)
-#define CHGR_CHG_EN BIT(7)
-#define CHGR_ON_BAT_FORCE_BIT BIT(0)
-#define CHGR_BAT_IF_CONST_RDS_EN BIT(7)
-#define CHGR_BAT_IF_VCP_EN BIT(0)
static int
-qpnp_chg_charge_dis(struct qpnp_chg_chip *chip, int disable)
+qpnp_batt_power_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
{
- /* This bit forces the charger to run off of the battery rather
- * than a connected charger */
- return qpnp_chg_masked_write(chip, chip->chgr_base + CHGR_CHG_CTRL,
- CHGR_ON_BAT_FORCE_BIT,
- disable ? CHGR_ON_BAT_FORCE_BIT : 0, 1);
-}
+ struct qpnp_chg_chip *chip = container_of(psy, struct qpnp_chg_chip,
+ batt_psy);
-static int
-qpnp_chg_charge_en(struct qpnp_chg_chip *chip, int enable)
-{
- return qpnp_chg_masked_write(chip, chip->chgr_base + CHGR_CHG_CTRL,
- CHGR_CHG_EN,
- enable ? CHGR_CHG_EN : 0, 1);
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ if (val->intval)
+ qpnp_chg_charge_en(chip, val->intval);
+ else
+ qpnp_chg_charge_dis(chip, val->intval);
+ charging_disabled = !(val->intval);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ power_supply_changed(&chip->batt_psy);
+ return 0;
}
static int
@@ -746,7 +802,7 @@
qpnp_chg_charge_dis(chip, charging_disabled);
return 0;
}
-module_param_call(disabled, qpnp_chg_set_disable_status_param, param_get_uint,
+module_param_call(disabled, qpnp_chg_set_disable_status_param, param_get_bool,
&charging_disabled, 0644);
#define QPNP_CHG_VINMIN_MIN_MV 3400
@@ -863,6 +919,14 @@
chip->chgr_base + CHGR_VDD_MAX, 1);
}
+
+static void
+qpnp_chg_setup_flags(struct qpnp_chg_chip *chip)
+{
+ if (chip->revision > 0)
+ chip->flags |= CHG_FLAGS_VCP_WA;
+}
+
#define WDOG_EN_BIT BIT(7)
static int
qpnp_chg_hwinit(struct qpnp_chg_chip *chip, u8 subtype,
@@ -944,7 +1008,7 @@
case SMBB_BAT_IF_SUBTYPE:
/* HACK: Unlock secure access to override temp comparator */
rc = qpnp_chg_masked_write(chip,
- chip->bat_if_base + 0xD0,
+ chip->bat_if_base + SEC_ACCESS,
0xA5, 0xA5, 1);
pr_debug("override hot cold\n");
rc = qpnp_chg_masked_write(chip,
@@ -980,6 +1044,12 @@
return -ENXIO;
}
}
+
+ rc = qpnp_chg_masked_write(chip,
+ chip->usb_chgpth_base + CHGR_USB_ENUM_T_STOP,
+ ENUM_T_STOP_BIT,
+ ENUM_T_STOP_BIT, 1);
+
break;
case SMBB_DC_CHGPTH_SUBTYPE:
break;
@@ -1184,6 +1254,7 @@
chip->batt_psy.properties = msm_batt_power_props;
chip->batt_psy.num_properties = ARRAY_SIZE(msm_batt_power_props);
chip->batt_psy.get_property = qpnp_batt_power_get_property;
+ chip->batt_psy.set_property = qpnp_batt_power_set_property;
chip->batt_psy.external_power_changed =
qpnp_batt_external_power_changed;
@@ -1199,6 +1270,9 @@
goto unregister_dc;
}
+ /* Turn on appropriate workaround flags */
+ qpnp_chg_setup_flags(chip);
+
power_supply_set_present(chip->usb_psy,
qpnp_chg_is_usb_chg_plugged_in(chip));
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 6b0916e..8f924d6 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -195,6 +195,15 @@
via I2C bus. The provided regulator is suitable for S3C6410
and S5PC1XX chips to control VCC_CORE and VCC_USIM voltages.
+config REGULATOR_ONSEMI_NCP6335D
+ tristate "OnSemi NCP6335D regulator support"
+ depends on I2C
+ help
+ This driver supports the OnSemi NCP6335D switching voltage regulator
+ (buck convertor). The regulator is controlled using an I2C interface
+ and supports a programmable voltage range from 0.6V to 1.4V in steps
+ of 6.25mV.
+
config REGULATOR_PCAP
tristate "Motorola PCAP2 regulator driver"
depends on EZX_PCAP
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 7fa396f..054ce42 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -33,6 +33,7 @@
obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o
+obj-$(CONFIG_REGULATOR_ONSEMI_NCP6335D) += onsemi-ncp6335d.o
obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
diff --git a/drivers/regulator/onsemi-ncp6335d.c b/drivers/regulator/onsemi-ncp6335d.c
new file mode 100644
index 0000000..a0c90f0
--- /dev/null
+++ b/drivers/regulator/onsemi-ncp6335d.c
@@ -0,0 +1,376 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/regulator/driver.h>
+#include <linux/regmap.h>
+#include <linux/regulator/onsemi-ncp6335d.h>
+
+/* registers */
+#define REG_NCP6335D_PID 0x03
+#define REG_NCP6335D_PROGVSEL1 0x10
+#define REG_NCP6335D_PROGVSEL0 0x11
+#define REG_NCP6335D_PGOOD 0x12
+#define REG_NCP6335D_TIMING 0x13
+#define REG_NCP6335D_COMMAND 0x14
+
+/* constraints */
+#define NCP6335D_MIN_VOLTAGE_UV 600000
+#define NCP6335D_STEP_VOLTAGE_UV 6250
+#define NCP6335D_MIN_SLEW_NS 166
+#define NCP6335D_MAX_SLEW_NS 1333
+
+/* bits */
+#define NCP6335D_ENABLE BIT(7)
+#define NCP6335D_DVS_PWM_MODE BIT(5)
+#define NCP6335D_PWM_MODE1 BIT(6)
+#define NCP6335D_PWM_MODE0 BIT(7)
+#define NCP6335D_PGOOD_DISCHG BIT(4)
+
+#define NCP6335D_VOUT_SEL_MASK 0x7F
+#define NCP6335D_SLEW_MASK 0x18
+#define NCP6335D_SLEW_SHIFT 0x3
+
+struct ncp6335d_info {
+ struct regulator_dev *regulator;
+ struct regulator_init_data *init_data;
+ struct regmap *regmap;
+ struct device *dev;
+ unsigned int vsel_reg;
+ unsigned int mode_bit;
+ int curr_voltage;
+ int slew_rate;
+};
+
+static void dump_registers(struct ncp6335d_info *dd,
+ unsigned int reg, const char *func)
+{
+ unsigned int val = 0;
+
+ regmap_read(dd->regmap, reg, &val);
+ dev_dbg(dd->dev, "%s: NCP6335D: Reg = %x, Val = %x\n", func, reg, val);
+}
+
+static void ncp633d_slew_delay(struct ncp6335d_info *dd,
+ int prev_uV, int new_uV)
+{
+ u8 val;
+ int delay;
+
+ val = abs(prev_uV - new_uV) / NCP6335D_STEP_VOLTAGE_UV;
+ delay = (val * dd->slew_rate / 1000) + 1;
+
+ dev_dbg(dd->dev, "Slew Delay = %d\n", delay);
+
+ udelay(delay);
+}
+
+static int ncp6335d_enable(struct regulator_dev *rdev)
+{
+ int rc;
+ struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+ rc = regmap_update_bits(dd->regmap, dd->vsel_reg,
+ NCP6335D_ENABLE, NCP6335D_ENABLE);
+ if (rc)
+ dev_err(dd->dev, "Unable to enable regualtor rc(%d)", rc);
+
+ dump_registers(dd, dd->vsel_reg, __func__);
+
+ return rc;
+}
+
+static int ncp6335d_disable(struct regulator_dev *rdev)
+{
+ int rc;
+ struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+ rc = regmap_update_bits(dd->regmap, dd->vsel_reg,
+ NCP6335D_ENABLE, 0);
+ if (rc)
+ dev_err(dd->dev, "Unable to disable regualtor rc(%d)", rc);
+
+ dump_registers(dd, dd->vsel_reg, __func__);
+
+ return rc;
+}
+
+static int ncp6335d_get_voltage(struct regulator_dev *rdev)
+{
+ unsigned int val;
+ int rc;
+ struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+ rc = regmap_read(dd->regmap, dd->vsel_reg, &val);
+ if (rc) {
+ 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;
+
+ dump_registers(dd, dd->vsel_reg, __func__);
+
+ return dd->curr_voltage;
+}
+
+static int ncp6335d_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV, unsigned *selector)
+{
+ 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;
+ if (new_uV > max_uV) {
+ dev_err(dd->dev, "Unable to set volatge (%d %d)\n",
+ min_uV, max_uV);
+ return -EINVAL;
+ }
+
+ rc = regmap_update_bits(dd->regmap, dd->vsel_reg,
+ NCP6335D_VOUT_SEL_MASK, (set_val & NCP6335D_VOUT_SEL_MASK));
+ if (rc) {
+ dev_err(dd->dev, "Unable to set volatge (%d %d)\n",
+ min_uV, max_uV);
+ } else {
+ ncp633d_slew_delay(dd, dd->curr_voltage, new_uV);
+ dd->curr_voltage = new_uV;
+ }
+
+ dump_registers(dd, dd->vsel_reg, __func__);
+
+ return rc;
+}
+
+static int ncp6335d_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ int rc;
+ struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+ /* only FAST and NORMAL mode types are supported */
+ if (mode != REGULATOR_MODE_FAST && mode != REGULATOR_MODE_NORMAL) {
+ dev_err(dd->dev, "Mode %d not supported\n", mode);
+ return -EINVAL;
+ }
+
+ rc = regmap_update_bits(dd->regmap, REG_NCP6335D_COMMAND, dd->mode_bit,
+ (mode == REGULATOR_MODE_FAST) ? dd->mode_bit : 0);
+ if (rc) {
+ dev_err(dd->dev, "Unable to set operating mode rc(%d)", rc);
+ return rc;
+ }
+
+ rc = regmap_update_bits(dd->regmap, REG_NCP6335D_COMMAND,
+ NCP6335D_DVS_PWM_MODE,
+ (mode == REGULATOR_MODE_FAST) ?
+ NCP6335D_DVS_PWM_MODE : 0);
+ if (rc)
+ dev_err(dd->dev, "Unable to set DVS trans. mode rc(%d)", rc);
+
+ dump_registers(dd, REG_NCP6335D_COMMAND, __func__);
+
+ return rc;
+}
+
+static unsigned int ncp6335d_get_mode(struct regulator_dev *rdev)
+{
+ unsigned int val;
+ int rc;
+ struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+ rc = regmap_read(dd->regmap, REG_NCP6335D_COMMAND, &val);
+ if (rc) {
+ dev_err(dd->dev, "Unable to get regulator mode rc(%d)\n", rc);
+ return rc;
+ }
+
+ dump_registers(dd, REG_NCP6335D_COMMAND, __func__);
+
+ if (val & dd->mode_bit)
+ return REGULATOR_MODE_FAST;
+
+ return REGULATOR_MODE_NORMAL;
+}
+
+static struct regulator_ops ncp6335d_ops = {
+ .set_voltage = ncp6335d_set_voltage,
+ .get_voltage = ncp6335d_get_voltage,
+ .enable = ncp6335d_enable,
+ .disable = ncp6335d_disable,
+ .set_mode = ncp6335d_set_mode,
+ .get_mode = ncp6335d_get_mode,
+};
+
+static struct regulator_desc rdesc = {
+ .name = "ncp6335d",
+ .owner = THIS_MODULE,
+ .n_voltages = 128,
+ .ops = &ncp6335d_ops,
+};
+
+static int __devinit ncp6335d_init(struct ncp6335d_info *dd,
+ const struct ncp6335d_platform_data *pdata)
+{
+ int rc;
+ unsigned int val;
+
+ switch (pdata->default_vsel) {
+ case NCP6335D_VSEL0:
+ dd->vsel_reg = REG_NCP6335D_PROGVSEL0;
+ dd->mode_bit = NCP6335D_PWM_MODE0;
+ break;
+ case NCP6335D_VSEL1:
+ dd->vsel_reg = REG_NCP6335D_PROGVSEL1;
+ dd->mode_bit = NCP6335D_PWM_MODE1;
+ break;
+ default:
+ dev_err(dd->dev, "Invalid VSEL ID %d\n", pdata->default_vsel);
+ return -EINVAL;
+ }
+
+ /* get the current programmed voltage */
+ rc = regmap_read(dd->regmap, dd->vsel_reg, &val);
+ if (rc) {
+ 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;
+
+ /* set discharge */
+ rc = regmap_update_bits(dd->regmap, REG_NCP6335D_PGOOD,
+ NCP6335D_PGOOD_DISCHG,
+ (pdata->discharge_enable ?
+ NCP6335D_PGOOD_DISCHG : 0));
+ if (rc) {
+ dev_err(dd->dev, "Unable to set Active Discharge rc(%d)\n", rc);
+ return -EINVAL;
+ }
+
+ /* set slew rate */
+ if (pdata->slew_rate_ns < NCP6335D_MIN_SLEW_NS ||
+ pdata->slew_rate_ns > NCP6335D_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;
+
+ rc = regmap_update_bits(dd->regmap, REG_NCP6335D_TIMING,
+ NCP6335D_SLEW_MASK, val << NCP6335D_SLEW_SHIFT);
+ if (rc)
+ dev_err(dd->dev, "Unable to set slew rate rc(%d)\n", rc);
+
+ dump_registers(dd, REG_NCP6335D_PROGVSEL0, __func__);
+ dump_registers(dd, REG_NCP6335D_TIMING, __func__);
+ dump_registers(dd, REG_NCP6335D_PGOOD, __func__);
+
+ return rc;
+}
+
+static struct regmap_config ncp6335d_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int __devinit ncp6335d_regulator_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc;
+ unsigned int val = 0;
+ struct ncp6335d_info *dd;
+ const struct ncp6335d_platform_data *pdata;
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->dev, "Platform data not specified\n");
+ return -EINVAL;
+ }
+
+ dd = devm_kzalloc(&client->dev, sizeof(*dd), GFP_KERNEL);
+ if (!dd) {
+ dev_err(&client->dev, "Unable to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ dd->regmap = devm_regmap_init_i2c(client, &ncp6335d_regmap_config);
+ if (IS_ERR(dd->regmap)) {
+ dev_err(&client->dev, "Error allocating regmap\n");
+ return PTR_ERR(dd->regmap);
+ }
+
+ rc = regmap_read(dd->regmap, REG_NCP6335D_PID, &val);
+ if (rc) {
+ dev_err(&client->dev, "Unable to identify NCP6335D, rc(%d)\n",
+ rc);
+ return rc;
+ }
+ dev_info(&client->dev, "Detected Regulator NCP6335D PID = %d\n", val);
+
+ dd->init_data = pdata->init_data;
+ dd->dev = &client->dev;
+ i2c_set_clientdata(client, dd);
+
+ rc = ncp6335d_init(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);
+ if (IS_ERR(dd->regulator)) {
+ dev_err(&client->dev, "Unable to register regulator rc(%ld)",
+ PTR_ERR(dd->regulator));
+ return PTR_ERR(dd->regulator);
+ }
+
+ return 0;
+}
+
+static int __devexit ncp6335d_regulator_remove(struct i2c_client *client)
+{
+ struct ncp6335d_info *dd = i2c_get_clientdata(client);
+
+ regulator_unregister(dd->regulator);
+
+ return 0;
+}
+
+static const struct i2c_device_id ncp6335d_id[] = {
+ {"ncp6335d", -1},
+ { },
+};
+
+static struct i2c_driver ncp6335d_regulator_driver = {
+ .driver = {
+ .name = "ncp6335d-regulator",
+ },
+ .probe = ncp6335d_regulator_probe,
+ .remove = __devexit_p(ncp6335d_regulator_remove),
+ .id_table = ncp6335d_id,
+};
+
+module_i2c_driver(ncp6335d_regulator_driver);
+
+MODULE_DESCRIPTION("OnSemi-NCP6335D regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index d082273..e70924c 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -91,3 +91,19 @@
help
Enable this to plug the SPEAr thermal sensor driver into the Linux
thermal framework
+
+config THERMAL_QPNP
+ tristate "Qualcomm Plug-and-Play PMIC Temperature Alarm"
+ depends on THERMAL
+ depends on OF
+ depends on SPMI
+ depends on OF_SPMI
+ help
+ This enables a thermal Sysfs driver for Qualcomm plug-and-play (QPNP)
+ PMIC devices. It shows up in Sysfs as a thermal zone with multiple
+ trip points. The temperature reported by the thermal zone reflects the
+ real time die temperature if an ADC is present or an estimate of the
+ temperature based upon the over temperature stage value if no ADC is
+ available. If allowed via compile time configuration; enabling the
+ thermal zone device via the mode file results in shifting PMIC over
+ temperature shutdown control from hardware to software.
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index f7e7cc6..3b2b3a8 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -10,3 +10,4 @@
obj-$(CONFIG_THERMAL_MONITOR) += msm_thermal.o
obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
obj-$(CONFIG_THERMAL_TSENS8974) += msm8974-tsens.o
+obj-$(CONFIG_THERMAL_QPNP) += qpnp-temp-alarm.o
diff --git a/drivers/thermal/msm8974-tsens.c b/drivers/thermal/msm8974-tsens.c
index 7169dc0..f3387d9 100644
--- a/drivers/thermal/msm8974-tsens.c
+++ b/drivers/thermal/msm8974-tsens.c
@@ -10,6 +10,9 @@
* GNU General Public License for more details.
*
*/
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/thermal.h>
@@ -53,6 +56,13 @@
#define TSENS_S0_MAIN_CONFIG(n) ((n) + 0x38)
#define TSENS_SN_REMOTE_CONFIG(n) ((n) + 0x3c)
+#define TSENS_EEPROM(n) ((n) + 0xd0)
+#define TSENS_EEPROM_REDUNDANCY_SEL(n) ((n) + 0x1cc)
+#define TSENS_EEPROM_BACKUP_REGION(n) ((n) + 0x440)
+
+#define TSENS_MAIN_CALIB_ADDR_RANGE 6
+#define TSENS_BACKUP_CALIB_ADDR_RANGE 4
+
/* TSENS calibration Mask data */
#define TSENS_BASE1_MASK 0xff
#define TSENS0_POINT1_MASK 0x3f00
@@ -64,8 +74,11 @@
#define TSENS6_POINT1_MASK 0x3f000
#define TSENS7_POINT1_MASK 0xfc0000
#define TSENS8_POINT1_MASK 0x3f000000
+#define TSENS8_POINT1_MASK_BACKUP 0x3f
#define TSENS9_POINT1_MASK 0x3f
+#define TSENS9_POINT1_MASK_BACKUP 0xfc0
#define TSENS10_POINT1_MASK 0xfc00
+#define TSENS10_POINT1_MASK_BACKUP 0x3f000
#define TSENS_CAL_SEL_0_1 0xc0000000
#define TSENS_CAL_SEL_2 0x40000000
#define TSENS_CAL_SEL_SHIFT 30
@@ -82,31 +95,55 @@
#define TSENS6_POINT1_SHIFT 12
#define TSENS7_POINT1_SHIFT 18
#define TSENS8_POINT1_SHIFT 24
+#define TSENS9_POINT1_BACKUP_SHIFT 6
#define TSENS10_POINT1_SHIFT 6
+#define TSENS10_POINT1_BACKUP_SHIFT 12
#define TSENS_POINT2_BASE_SHIFT 12
+#define TSENS_POINT2_BASE_BACKUP_SHIFT 18
#define TSENS0_POINT2_SHIFT 20
+#define TSENS0_POINT2_BACKUP_SHIFT 26
#define TSENS1_POINT2_SHIFT 26
+#define TSENS2_POINT2_BACKUP_SHIFT 6
#define TSENS3_POINT2_SHIFT 6
+#define TSENS3_POINT2_BACKUP_SHIFT 12
#define TSENS4_POINT2_SHIFT 12
+#define TSENS4_POINT2_BACKUP_SHIFT 18
#define TSENS5_POINT2_SHIFT 18
+#define TSENS5_POINT2_BACKUP_SHIFT 24
#define TSENS6_POINT2_SHIFT 24
+#define TSENS7_POINT2_BACKUP_SHIFT 6
#define TSENS8_POINT2_SHIFT 6
+#define TSENS8_POINT2_BACKUP_SHIFT 12
#define TSENS9_POINT2_SHIFT 12
+#define TSENS9_POINT2_BACKUP_SHIFT 18
#define TSENS10_POINT2_SHIFT 18
+#define TSENS10_POINT2_BACKUP_SHIFT 24
#define TSENS_BASE2_MASK 0xff000
+#define TSENS_BASE2_BACKUP_MASK 0xfc0000
#define TSENS0_POINT2_MASK 0x3f00000
+#define TSENS0_POINT2_BACKUP_MASK 0xfc000000
#define TSENS1_POINT2_MASK 0xfc000000
+#define TSENS1_POINT2_BACKUP_MASK 0x3f
#define TSENS2_POINT2_MASK 0x3f
+#define TSENS2_POINT2_BACKUP_MASK 0xfc0
#define TSENS3_POINT2_MASK 0xfc00
+#define TSENS3_POINT2_BACKUP_MASK 0x3f000
#define TSENS4_POINT2_MASK 0x3f000
+#define TSENS4_POINT2_BACKUP_MASK 0xfc0000
#define TSENS5_POINT2_MASK 0xfc0000
+#define TSENS5_POINT2_BACKUP_MASK 0x3f000000
#define TSENS6_POINT2_MASK 0x3f000000
+#define TSENS6_POINT2_BACKUP_MASK 0x3f
#define TSENS7_POINT2_MASK 0x3f
+#define TSENS7_POINT2_BACKUP_MASK 0xfc00
#define TSENS8_POINT2_MASK 0xfc00
+#define TSENS8_POINT2_BACKUP_MASK 0x3f000
#define TSENS9_POINT2_MASK 0x3f000
+#define TSENS9_POINT2_BACKUP_MASK 0xfc0000
#define TSENS10_POINT2_MASK 0xfc0000
+#define TSENS10_POINT2_BACKUP_MASK 0x3f000000
#define TSENS_BIT_APPEND 0x3
#define TSENS_CAL_DEGC_POINT1 30
@@ -125,6 +162,10 @@
#define TSENS_SN_MIN_MAX_STATUS_CTRL_DATA 0x3ffc00
#define TSENS_SN_REMOTE_CFG_DATA 0x11c3
+#define TSENS_QFPROM_BACKUP_SEL 0x3
+#define TSENS_QFPROM_BACKUP_REDUN_SEL 0xe0000000
+#define TSENS_QFPROM_BACKUP_REDUN_SHIFT 29
+
/* Trips: warm and cool */
enum tsens_trip_type {
TSENS_TRIP_WARM = 0,
@@ -146,6 +187,7 @@
struct tsens_tm_device {
struct platform_device *pdev;
bool prev_reading_avail;
+ bool calibration_less_mode;
int tsens_factor;
uint32_t tsens_num_sensor;
int tsens_irq;
@@ -512,78 +554,185 @@
int tsens6_point2 = 0, tsens7_point2 = 0, tsens8_point2 = 0;
int tsens9_point2 = 0, tsens10_point2 = 0;
int tsens_base2_data = 0, tsens_calibration_mode = 0, temp = 0;
- uint32_t calib_data[5];
+ uint32_t calib_data[6], calib_redun_sel, calib_data_backup[4];
- for (i = 0; i < 5; i++)
- calib_data[i] = readl_relaxed(tmdev->tsens_calib_addr
+ if (tmdev->calibration_less_mode)
+ goto calibration_less_mode;
+
+ calib_redun_sel = readl_relaxed(
+ TSENS_EEPROM_REDUNDANCY_SEL(tmdev->tsens_calib_addr));
+ calib_redun_sel = calib_redun_sel & TSENS_QFPROM_BACKUP_REDUN_SEL;
+ calib_redun_sel >>= TSENS_QFPROM_BACKUP_REDUN_SHIFT;
+
+ for (i = 0; i < TSENS_MAIN_CALIB_ADDR_RANGE; i++)
+ calib_data[i] = readl_relaxed(
+ (TSENS_EEPROM(tmdev->tsens_calib_addr))
+ (i * TSENS_SN_ADDR_OFFSET));
- tsens_calibration_mode = (calib_data[1] & TSENS_CAL_SEL_0_1)
- >> TSENS_CAL_SEL_SHIFT;
- temp = (calib_data[3] & TSENS_CAL_SEL_2)
- >> TSENS_CAL_SEL_SHIFT_2;
- tsens_calibration_mode |= temp;
+ if (calib_redun_sel == TSENS_QFPROM_BACKUP_SEL) {
+ tsens_calibration_mode = (calib_data[4] & TSENS_CAL_SEL_0_1)
+ >> TSENS_CAL_SEL_SHIFT;
+ temp = (calib_data[5] & TSENS_CAL_SEL_2)
+ >> TSENS_CAL_SEL_SHIFT_2;
+ tsens_calibration_mode |= temp;
- if (tsens_calibration_mode == 0) {
- pr_debug("TSENS is calibrationless mode\n");
+ for (i = 0; i < TSENS_BACKUP_CALIB_ADDR_RANGE; i++)
+ calib_data_backup[i] = readl_relaxed(
+ (TSENS_EEPROM_BACKUP_REGION(
+ tmdev->tsens_calib_addr))
+ + (i * TSENS_SN_ADDR_OFFSET));
+
+ if ((tsens_calibration_mode == TSENS_ONE_POINT_CALIB)
+ || (tsens_calibration_mode ==
+ TSENS_TWO_POINT_CALIB) ||
+ (tsens_calibration_mode ==
+ TSENS_ONE_POINT_CALIB_OPTION_2)) {
+ pr_debug("backup one point calibrationless mode\n");
+ tsens_base1_data = (calib_data_backup[0] &
+ TSENS_BASE1_MASK);
+ tsens0_point1 = (calib_data_backup[0] &
+ TSENS0_POINT1_MASK) >>
+ TSENS0_POINT1_SHIFT;
+ tsens1_point1 = (calib_data_backup[0] &
+ TSENS1_POINT1_MASK) >> TSENS1_POINT1_SHIFT;
+ tsens2_point1 = (calib_data_backup[0] &
+ TSENS2_POINT1_MASK) >> TSENS2_POINT1_SHIFT;
+ tsens3_point1 = (calib_data_backup[0] &
+ TSENS3_POINT1_MASK) >> TSENS3_POINT1_SHIFT;
+ tsens4_point1 = (calib_data_backup[1] &
+ TSENS4_POINT1_MASK);
+ tsens5_point1 = (calib_data_backup[1] &
+ TSENS5_POINT1_MASK) >> TSENS5_POINT1_SHIFT;
+ tsens6_point1 = (calib_data_backup[1] &
+ TSENS6_POINT1_MASK) >> TSENS6_POINT1_SHIFT;
+ tsens7_point1 = (calib_data_backup[1] &
+ TSENS7_POINT1_MASK) >> TSENS7_POINT1_SHIFT;
+ tsens8_point1 = (calib_data_backup[2] &
+ TSENS8_POINT1_MASK_BACKUP) >>
+ TSENS8_POINT1_SHIFT;
+ tsens9_point1 = (calib_data_backup[2] &
+ TSENS9_POINT1_MASK_BACKUP) >>
+ TSENS9_POINT1_BACKUP_SHIFT;
+ tsens10_point1 = (calib_data_backup[2] &
+ TSENS10_POINT1_MASK_BACKUP) >>
+ TSENS10_POINT1_BACKUP_SHIFT;
+ } else if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) {
+ pr_debug("backup two point calibrationless mode\n");
+ tsens_base2_data = (calib_data_backup[2] &
+ TSENS_BASE2_BACKUP_MASK) >>
+ TSENS_POINT2_BASE_BACKUP_SHIFT;
+ tsens0_point2 = (calib_data_backup[2] &
+ TSENS0_POINT2_BACKUP_MASK) >>
+ TSENS0_POINT2_BACKUP_SHIFT;
+ tsens1_point2 = (calib_data_backup[3] &
+ TSENS1_POINT2_BACKUP_MASK);
+ tsens2_point2 = (calib_data_backup[3] &
+ TSENS2_POINT2_BACKUP_MASK) >>
+ TSENS2_POINT2_BACKUP_SHIFT;
+ tsens3_point2 = (calib_data_backup[3] &
+ TSENS3_POINT2_BACKUP_MASK) >>
+ TSENS3_POINT2_BACKUP_SHIFT;
+ tsens4_point2 = (calib_data_backup[3] &
+ TSENS4_POINT2_BACKUP_MASK) >>
+ TSENS4_POINT2_BACKUP_SHIFT;
+ tsens5_point2 = (calib_data[4] & TSENS5_POINT2_BACKUP_MASK) >>
+ TSENS5_POINT2_BACKUP_SHIFT;
+ tsens6_point2 = (calib_data[5] & TSENS6_POINT2_BACKUP_MASK);
+ tsens7_point2 = (calib_data[5] & TSENS7_POINT2_BACKUP_MASK) >>
+ TSENS7_POINT2_BACKUP_SHIFT;
+ tsens8_point2 = (calib_data[5] & TSENS8_POINT2_BACKUP_MASK) >>
+ TSENS8_POINT2_BACKUP_SHIFT;
+ tsens9_point2 = (calib_data[5] & TSENS9_POINT2_BACKUP_MASK) >>
+ TSENS9_POINT2_BACKUP_SHIFT;
+ tsens10_point2 = (calib_data[5] & TSENS10_POINT2_BACKUP_MASK)
+ >> TSENS10_POINT2_BACKUP_SHIFT;
+ } else {
+ pr_debug("TSENS:backup is calibrationless mode\n");
for (i = 0; i < tmdev->tsens_num_sensor; i++) {
tmdev->sensor[i].calib_data_point2 = 780;
tmdev->sensor[i].calib_data_point1 = 492;
}
+ tsens_calibration_mode = 0;
goto compute_intercept_slope;
- } else if ((tsens_calibration_mode == TSENS_ONE_POINT_CALIB) ||
- (tsens_calibration_mode == TSENS_TWO_POINT_CALIB)) {
- tsens_base1_data = (calib_data[0] & TSENS_BASE1_MASK);
- tsens0_point1 = (calib_data[0] & TSENS0_POINT1_MASK) >>
- TSENS0_POINT1_SHIFT;
- tsens1_point1 = (calib_data[0] & TSENS1_POINT1_MASK) >>
- TSENS1_POINT1_SHIFT;
- tsens2_point1 = (calib_data[0] & TSENS2_POINT1_MASK) >>
- TSENS2_POINT1_SHIFT;
- tsens3_point1 = (calib_data[0] & TSENS3_POINT1_MASK) >>
- TSENS3_POINT1_SHIFT;
- tsens4_point1 = (calib_data[1] & TSENS4_POINT1_MASK);
- tsens5_point1 = (calib_data[1] & TSENS5_POINT1_MASK) >>
- TSENS5_POINT1_SHIFT;
- tsens6_point1 = (calib_data[1] & TSENS6_POINT1_MASK) >>
- TSENS6_POINT1_SHIFT;
- tsens7_point1 = (calib_data[1] & TSENS7_POINT1_MASK) >>
- TSENS7_POINT1_SHIFT;
- tsens8_point1 = (calib_data[1] & TSENS8_POINT1_MASK) >>
- TSENS8_POINT1_SHIFT;
- tsens9_point1 = (calib_data[2] & TSENS9_POINT1_MASK);
- tsens10_point1 = (calib_data[2] & TSENS10_POINT1_MASK) >>
- TSENS10_POINT1_SHIFT;
- } else if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) {
- tsens_base2_data = (calib_data[2] & TSENS_BASE2_MASK) >>
- TSENS_POINT2_BASE_SHIFT;
- tsens0_point2 = (calib_data[2] & TSENS0_POINT2_MASK) >>
- TSENS0_POINT2_SHIFT;
- tsens1_point2 = (calib_data[2] & TSENS1_POINT2_MASK) >>
- TSENS1_POINT2_SHIFT;
- tsens2_point2 = (calib_data[3] & TSENS2_POINT2_MASK);
- tsens3_point2 = (calib_data[3] & TSENS3_POINT2_MASK) >>
- TSENS3_POINT2_SHIFT;
- tsens4_point2 = (calib_data[3] & TSENS4_POINT2_MASK) >>
- TSENS4_POINT2_SHIFT;
- tsens5_point2 = (calib_data[3] & TSENS5_POINT2_MASK) >>
- TSENS5_POINT2_SHIFT;
- tsens6_point2 = (calib_data[3] & TSENS6_POINT2_MASK) >>
- TSENS6_POINT2_SHIFT;
- tsens7_point2 = (calib_data[4] & TSENS7_POINT2_MASK);
- tsens8_point2 = (calib_data[4] & TSENS8_POINT2_MASK) >>
- TSENS8_POINT2_SHIFT;
- tsens9_point2 = (calib_data[4] & TSENS9_POINT2_MASK) >>
- TSENS9_POINT2_SHIFT;
- tsens10_point2 = (calib_data[4] & TSENS10_POINT2_MASK) >>
- TSENS10_POINT2_SHIFT;
+ }
} else {
- pr_debug("Calibration mode is unknown: %d\n",
- tsens_calibration_mode);
- return -ENODEV;
+ tsens_calibration_mode = (calib_data[1] & TSENS_CAL_SEL_0_1)
+ >> TSENS_CAL_SEL_SHIFT;
+ temp = (calib_data[3] & TSENS_CAL_SEL_2)
+ >> TSENS_CAL_SEL_SHIFT_2;
+ tsens_calibration_mode |= temp;
+ if ((tsens_calibration_mode == TSENS_ONE_POINT_CALIB) ||
+ (tsens_calibration_mode ==
+ TSENS_ONE_POINT_CALIB_OPTION_2) ||
+ (tsens_calibration_mode == TSENS_TWO_POINT_CALIB)) {
+ pr_debug("TSENS is one point calibrationless mode\n");
+ tsens_base1_data = (calib_data[0] & TSENS_BASE1_MASK);
+ tsens0_point1 = (calib_data[0] & TSENS0_POINT1_MASK) >>
+ TSENS0_POINT1_SHIFT;
+ tsens1_point1 = (calib_data[0] & TSENS1_POINT1_MASK) >>
+ TSENS1_POINT1_SHIFT;
+ tsens2_point1 = (calib_data[0] & TSENS2_POINT1_MASK) >>
+ TSENS2_POINT1_SHIFT;
+ tsens3_point1 = (calib_data[0] & TSENS3_POINT1_MASK) >>
+ TSENS3_POINT1_SHIFT;
+ tsens4_point1 = (calib_data[1] & TSENS4_POINT1_MASK);
+ tsens5_point1 = (calib_data[1] & TSENS5_POINT1_MASK) >>
+ TSENS5_POINT1_SHIFT;
+ tsens6_point1 = (calib_data[1] & TSENS6_POINT1_MASK) >>
+ TSENS6_POINT1_SHIFT;
+ tsens7_point1 = (calib_data[1] & TSENS7_POINT1_MASK) >>
+ TSENS7_POINT1_SHIFT;
+ tsens8_point1 = (calib_data[1] & TSENS8_POINT1_MASK) >>
+ TSENS8_POINT1_SHIFT;
+ tsens9_point1 = (calib_data[2] & TSENS9_POINT1_MASK);
+ tsens10_point1 = (calib_data[2] & TSENS10_POINT1_MASK)
+ >> TSENS10_POINT1_SHIFT;
+ } else if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) {
+ pr_debug("TSENS is two point calibrationless mode\n");
+ tsens_base2_data = (calib_data[2] & TSENS_BASE2_MASK) >>
+ TSENS_POINT2_BASE_SHIFT;
+ tsens0_point2 = (calib_data[2] & TSENS0_POINT2_MASK) >>
+ TSENS0_POINT2_SHIFT;
+ tsens1_point2 = (calib_data[2] & TSENS1_POINT2_MASK) >>
+ TSENS1_POINT2_SHIFT;
+ tsens2_point2 = (calib_data[3] & TSENS2_POINT2_MASK);
+ tsens3_point2 = (calib_data[3] & TSENS3_POINT2_MASK) >>
+ TSENS3_POINT2_SHIFT;
+ tsens4_point2 = (calib_data[3] & TSENS4_POINT2_MASK) >>
+ TSENS4_POINT2_SHIFT;
+ tsens5_point2 = (calib_data[3] & TSENS5_POINT2_MASK) >>
+ TSENS5_POINT2_SHIFT;
+ tsens6_point2 = (calib_data[3] & TSENS6_POINT2_MASK) >>
+ TSENS6_POINT2_SHIFT;
+ tsens7_point2 = (calib_data[4] & TSENS7_POINT2_MASK);
+ tsens8_point2 = (calib_data[4] & TSENS8_POINT2_MASK) >>
+ TSENS8_POINT2_SHIFT;
+ tsens9_point2 = (calib_data[4] & TSENS9_POINT2_MASK) >>
+ TSENS9_POINT2_SHIFT;
+ tsens10_point2 = (calib_data[4] & TSENS10_POINT2_MASK)
+ >> TSENS10_POINT2_SHIFT;
+ } else {
+calibration_less_mode:
+ pr_debug("TSENS is calibrationless mode\n");
+ for (i = 0; i < tmdev->tsens_num_sensor; i++)
+ tmdev->sensor[i].calib_data_point2 = 780;
+ tmdev->sensor[0].calib_data_point1 = 502;
+ tmdev->sensor[1].calib_data_point1 = 509;
+ tmdev->sensor[2].calib_data_point1 = 503;
+ tmdev->sensor[3].calib_data_point1 = 509;
+ tmdev->sensor[4].calib_data_point1 = 505;
+ tmdev->sensor[5].calib_data_point1 = 509;
+ tmdev->sensor[6].calib_data_point1 = 507;
+ tmdev->sensor[7].calib_data_point1 = 510;
+ tmdev->sensor[8].calib_data_point1 = 508;
+ tmdev->sensor[9].calib_data_point1 = 509;
+ tmdev->sensor[10].calib_data_point1 = 508;
+ goto compute_intercept_slope;
+ }
}
if (tsens_calibration_mode == TSENS_ONE_POINT_CALIB) {
+ pr_debug("old one point calibration calculation\n");
tmdev->sensor[0].calib_data_point1 =
(((tsens_base1_data) << 2) | TSENS_BIT_APPEND) + tsens0_point1;
tmdev->sensor[1].calib_data_point1 =
@@ -610,6 +759,8 @@
if ((tsens_calibration_mode == TSENS_ONE_POINT_CALIB_OPTION_2) ||
(tsens_calibration_mode == TSENS_TWO_POINT_CALIB)) {
+ pr_debug("one and two point calibration calculation\n");
+
tmdev->sensor[0].calib_data_point1 =
((((tsens_base1_data) + tsens0_point1) << 2) |
TSENS_BIT_APPEND);
@@ -646,6 +797,7 @@
}
if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) {
+ pr_debug("two point calibration calculation\n");
tmdev->sensor[0].calib_data_point2 =
(((tsens_base2_data + tsens0_point2) << 2) | TSENS_BIT_APPEND);
tmdev->sensor[1].calib_data_point2 =
@@ -705,7 +857,7 @@
}
tsens_slope_data = devm_kzalloc(&pdev->dev,
- tsens_num_sensors, GFP_KERNEL);
+ tsens_num_sensors * sizeof(u32), GFP_KERNEL);
if (!tsens_slope_data) {
dev_err(&pdev->dev, "can not allocate slope data\n");
return -ENOMEM;
@@ -732,19 +884,23 @@
tmdev->sensor[i].slope_mul_tsens_factor = tsens_slope_data[i];
tmdev->tsens_factor = TSENS_SLOPE_FACTOR;
tmdev->tsens_num_sensor = tsens_num_sensors;
+ tmdev->calibration_less_mode = of_property_read_bool(of_node,
+ "qcom,calibration-less-mode");
tmdev->tsens_irq = platform_get_irq(pdev, 0);
if (tmdev->tsens_irq < 0) {
pr_err("Invalid get irq\n");
- return tmdev->tsens_irq;
+ rc = tmdev->tsens_irq;
+ goto fail_tmdev;
}
+ /* TSENS register region */
tmdev->res_tsens_mem = platform_get_resource_byname(pdev,
IORESOURCE_MEM, "tsens_physical");
if (!tmdev->res_tsens_mem) {
pr_err("Could not get tsens physical address resource\n");
rc = -EINVAL;
- goto fail_free_irq;
+ goto fail_tmdev;
}
tmdev->tsens_len = tmdev->res_tsens_mem->end -
@@ -755,7 +911,7 @@
if (!res_mem) {
pr_err("Request tsens physical memory region failed\n");
rc = -EINVAL;
- goto fail_free_irq;
+ goto fail_tmdev;
}
tmdev->tsens_addr = ioremap(res_mem->start, tmdev->tsens_len);
@@ -765,6 +921,7 @@
goto fail_unmap_tsens_region;
}
+ /* TSENS calibration region */
tmdev->res_calib_mem = platform_get_resource_byname(pdev,
IORESOURCE_MEM, "tsens_eeprom_physical");
if (!tmdev->res_calib_mem) {
@@ -805,9 +962,8 @@
if (tmdev->res_tsens_mem)
release_mem_region(tmdev->res_tsens_mem->start,
tmdev->tsens_len);
-fail_free_irq:
- free_irq(tmdev->tsens_irq, tmdev);
-
+fail_tmdev:
+ tmdev = NULL;
return rc;
}
@@ -820,15 +976,21 @@
return -EBUSY;
}
- if (pdev->dev.of_node)
+ if (pdev->dev.of_node) {
rc = get_device_tree_data(pdev);
- else
+ if (rc) {
+ pr_err("Error reading TSENS DT\n");
+ return rc;
+ }
+ } else
return -ENODEV;
tmdev->pdev = pdev;
rc = tsens_calib_sensors();
- if (rc < 0)
+ if (rc < 0) {
+ pr_err("Calibration failed\n");
goto fail;
+ }
tsens_hw_init();
@@ -848,8 +1010,7 @@
if (tmdev->res_tsens_mem)
release_mem_region(tmdev->res_tsens_mem->start,
tmdev->tsens_len);
- free_irq(tmdev->tsens_irq, tmdev);
- kfree(tmdev);
+ tmdev = NULL;
return rc;
}
@@ -900,14 +1061,12 @@
iounmap(tmdev->tsens_calib_addr);
if (tmdev->res_calib_mem)
release_mem_region(tmdev->res_calib_mem->start,
- tmdev->calib_len);
+ tmdev->calib_len);
if (tmdev->tsens_addr)
iounmap(tmdev->tsens_addr);
if (tmdev->res_tsens_mem)
release_mem_region(tmdev->res_tsens_mem->start,
- tmdev->tsens_len);
- kfree(tmdev);
-
+ tmdev->tsens_len);
return rc;
}
@@ -922,15 +1081,14 @@
iounmap(tmdev->tsens_calib_addr);
if (tmdev->res_calib_mem)
release_mem_region(tmdev->res_calib_mem->start,
- tmdev->calib_len);
+ tmdev->calib_len);
if (tmdev->tsens_addr)
iounmap(tmdev->tsens_addr);
if (tmdev->res_tsens_mem)
release_mem_region(tmdev->res_tsens_mem->start,
- tmdev->tsens_len);
+ tmdev->tsens_len);
free_irq(tmdev->tsens_irq, tmdev);
platform_set_drvdata(pdev, NULL);
- kfree(tmdev);
return 0;
}
diff --git a/drivers/thermal/qpnp-temp-alarm.c b/drivers/thermal/qpnp-temp-alarm.c
new file mode 100644
index 0000000..499d67e
--- /dev/null
+++ b/drivers/thermal/qpnp-temp-alarm.c
@@ -0,0 +1,721 @@
+/*
+ * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/spmi.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/thermal.h>
+#include <linux/qpnp/qpnp-adc.h>
+
+#define QPNP_TM_DRIVER_NAME "qcom,qpnp-temp-alarm"
+
+enum qpnp_tm_registers {
+ QPNP_TM_REG_TYPE = 0x04,
+ QPNP_TM_REG_SUBTYPE = 0x05,
+ QPNP_TM_REG_STATUS = 0x08,
+ QPNP_TM_REG_SHUTDOWN_CTRL1 = 0x40,
+ QPNP_TM_REG_SHUTDOWN_CTRL2 = 0x42,
+ QPNP_TM_REG_ALARM_CTRL = 0x46,
+};
+
+#define QPNP_TM_TYPE 0x09
+#define QPNP_TM_SUBTYPE 0x08
+
+#define STATUS_STAGE_MASK 0x03
+
+#define SHUTDOWN_CTRL1_OVERRIDE_STAGE3 0x80
+#define SHUTDOWN_CTRL1_OVERRIDE_STAGE2 0x40
+#define SHUTDOWN_CTRL1_THRESHOLD_MASK 0x03
+
+#define SHUTDOWN_CTRL2_CLEAR_STAGE3 0x80
+#define SHUTDOWN_CTRL2_CLEAR_STAGE2 0x40
+
+#define ALARM_CTRL_FORCE_ENABLE 0x80
+#define ALARM_CTRL_FOLLOW_HW_ENABLE 0x01
+
+#define TEMP_STAGE_STEP 20000 /* Stage step: 20.000 C */
+#define TEMP_STAGE_HYSTERESIS 2000
+
+#define TEMP_THRESH_MIN 105000 /* Threshold Min: 105 C */
+#define TEMP_THRESH_STEP 5000 /* Threshold step: 5 C */
+
+#define THRESH_MIN 0
+#define THRESH_MAX 3
+
+/* Trip points from most critical to least critical */
+#define TRIP_STAGE3 0
+#define TRIP_STAGE2 1
+#define TRIP_STAGE1 2
+#define TRIP_NUM 3
+
+enum qpnp_tm_adc_type {
+ QPNP_TM_ADC_NONE, /* Estimates temp based on overload level. */
+ QPNP_TM_ADC_QPNP_ADC,
+};
+
+/*
+ * Temperature in millicelcius reported during stage 0 if no ADC is present and
+ * no value has been specified via device tree.
+ */
+#define DEFAULT_NO_ADC_TEMP 37000
+
+struct qpnp_tm_chip {
+ struct delayed_work irq_work;
+ struct spmi_device *spmi_dev;
+ struct thermal_zone_device *tz_dev;
+ const char *tm_name;
+ enum qpnp_tm_adc_type adc_type;
+ unsigned long temperature;
+ enum thermal_device_mode mode;
+ unsigned int thresh;
+ unsigned int stage;
+ unsigned int prev_stage;
+ int irq;
+ enum qpnp_vadc_channels adc_channel;
+ u16 base_addr;
+ bool allow_software_override;
+};
+
+/* Delay between TEMP_STAT IRQ going high and status value changing in ms. */
+#define STATUS_REGISTER_DELAY_MS 40
+
+enum pmic_thermal_override_mode {
+ SOFTWARE_OVERRIDE_DISABLED = 0,
+ SOFTWARE_OVERRIDE_ENABLED,
+};
+
+static inline int qpnp_tm_read(struct qpnp_tm_chip *chip, u16 addr, u8 *buf,
+ int len)
+{
+ int rc;
+
+ rc = spmi_ext_register_readl(chip->spmi_dev->ctrl,
+ chip->spmi_dev->sid, chip->base_addr + addr, buf, len);
+
+ if (rc)
+ dev_err(&chip->spmi_dev->dev, "%s: spmi_ext_register_readl() failed. sid=%d, addr=%04X, len=%d, rc=%d\n",
+ __func__, chip->spmi_dev->sid, chip->base_addr + addr,
+ len, rc);
+
+ return rc;
+}
+
+static inline int qpnp_tm_write(struct qpnp_tm_chip *chip, u16 addr, u8 *buf,
+ int len)
+{
+ int rc;
+
+ rc = spmi_ext_register_writel(chip->spmi_dev->ctrl,
+ chip->spmi_dev->sid, chip->base_addr + addr, buf, len);
+
+ if (rc)
+ dev_err(&chip->spmi_dev->dev, "%s: spmi_ext_register_writel() failed. sid=%d, addr=%04X, len=%d, rc=%d\n",
+ __func__, chip->spmi_dev->sid, chip->base_addr + addr,
+ len, rc);
+
+ return rc;
+}
+
+
+static inline int qpnp_tm_shutdown_override(struct qpnp_tm_chip *chip,
+ enum pmic_thermal_override_mode mode)
+{
+ int rc = 0;
+ u8 reg;
+
+ if (chip->allow_software_override) {
+ reg = chip->thresh & SHUTDOWN_CTRL1_THRESHOLD_MASK;
+
+ if (mode == SOFTWARE_OVERRIDE_ENABLED)
+ reg |= SHUTDOWN_CTRL1_OVERRIDE_STAGE2
+ | SHUTDOWN_CTRL1_OVERRIDE_STAGE3;
+
+ rc = qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, ®, 1);
+ }
+
+ return rc;
+}
+
+static int qpnp_tm_update_temp(struct qpnp_tm_chip *chip)
+{
+ struct qpnp_vadc_result adc_result;
+ int rc;
+
+ rc = qpnp_vadc_read(chip->adc_channel, &adc_result);
+ if (!rc)
+ chip->temperature = adc_result.physical;
+ else
+ dev_err(&chip->spmi_dev->dev, "%s: qpnp_vadc_read(%d) failed, rc=%d\n",
+ __func__, chip->adc_channel, rc);
+
+ return rc;
+}
+
+/*
+ * This function initializes the internal temperature value based on only the
+ * current thermal stage and threshold.
+ */
+static int qpnp_tm_init_temp_no_adc(struct qpnp_tm_chip *chip)
+{
+ int rc;
+ u8 reg;
+
+ rc = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, ®, 1);
+ if (rc < 0)
+ return rc;
+
+ chip->stage = reg & STATUS_STAGE_MASK;
+
+ if (chip->stage)
+ chip->temperature = chip->thresh * TEMP_THRESH_STEP +
+ (chip->stage - 1) * TEMP_STAGE_STEP +
+ TEMP_THRESH_MIN;
+
+ return 0;
+}
+
+/*
+ * This function updates the internal temperature value based on the
+ * current thermal stage and threshold as well as the previous stage
+ */
+static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip)
+{
+ unsigned int stage;
+ int rc;
+ u8 reg;
+
+ rc = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, ®, 1);
+ if (rc < 0)
+ return rc;
+
+ stage = reg & STATUS_STAGE_MASK;
+
+ if (stage > chip->stage) {
+ /* increasing stage, use lower bound */
+ chip->temperature = (stage - 1) * TEMP_STAGE_STEP
+ + chip->thresh * TEMP_THRESH_STEP
+ + TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
+ } else if (stage < chip->stage) {
+ /* decreasing stage, use upper bound */
+ chip->temperature = stage * TEMP_STAGE_STEP
+ + chip->thresh * TEMP_THRESH_STEP
+ - TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
+ }
+
+ chip->stage = stage;
+
+ return 0;
+}
+
+static int qpnp_tz_get_temp_no_adc(struct thermal_zone_device *thermal,
+ unsigned long *temperature)
+{
+ struct qpnp_tm_chip *chip = thermal->devdata;
+ int rc;
+
+ if (!temperature)
+ return -EINVAL;
+
+ rc = qpnp_tm_update_temp_no_adc(chip);
+ if (rc < 0)
+ return rc;
+
+ *temperature = chip->temperature;
+
+ return 0;
+}
+
+static int qpnp_tz_get_temp_qpnp_adc(struct thermal_zone_device *thermal,
+ unsigned long *temperature)
+{
+ struct qpnp_tm_chip *chip = thermal->devdata;
+ int rc;
+
+ if (!temperature)
+ return -EINVAL;
+
+ rc = qpnp_tm_update_temp(chip);
+ if (rc < 0) {
+ dev_err(&chip->spmi_dev->dev, "%s: %s: adc read failed, rc = %d\n",
+ __func__, chip->tm_name, rc);
+ return rc;
+ }
+
+ *temperature = chip->temperature;
+
+ return 0;
+}
+
+static int qpnp_tz_get_mode(struct thermal_zone_device *thermal,
+ enum thermal_device_mode *mode)
+{
+ struct qpnp_tm_chip *chip = thermal->devdata;
+
+ if (!mode)
+ return -EINVAL;
+
+ *mode = chip->mode;
+
+ return 0;
+}
+
+static int qpnp_tz_set_mode(struct thermal_zone_device *thermal,
+ enum thermal_device_mode mode)
+{
+ struct qpnp_tm_chip *chip = thermal->devdata;
+ int rc = 0;
+
+ if (mode != chip->mode) {
+ if (mode == THERMAL_DEVICE_ENABLED)
+ rc = qpnp_tm_shutdown_override(chip,
+ SOFTWARE_OVERRIDE_ENABLED);
+ else
+ rc = qpnp_tm_shutdown_override(chip,
+ SOFTWARE_OVERRIDE_DISABLED);
+
+ chip->mode = mode;
+ }
+
+ return rc;
+}
+
+static int qpnp_tz_get_trip_type(struct thermal_zone_device *thermal,
+ int trip, enum thermal_trip_type *type)
+{
+ if (trip < 0 || !type)
+ return -EINVAL;
+
+ switch (trip) {
+ case TRIP_STAGE3:
+ *type = THERMAL_TRIP_CRITICAL;
+ break;
+ case TRIP_STAGE2:
+ *type = THERMAL_TRIP_HOT;
+ break;
+ case TRIP_STAGE1:
+ *type = THERMAL_TRIP_HOT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int qpnp_tz_get_trip_temp(struct thermal_zone_device *thermal,
+ int trip, unsigned long *temperature)
+{
+ struct qpnp_tm_chip *chip = thermal->devdata;
+ int thresh_temperature;
+
+ if (trip < 0 || !temperature)
+ return -EINVAL;
+
+ thresh_temperature = chip->thresh * TEMP_THRESH_STEP + TEMP_THRESH_MIN;
+
+ switch (trip) {
+ case TRIP_STAGE3:
+ thresh_temperature += 2 * TEMP_STAGE_STEP;
+ break;
+ case TRIP_STAGE2:
+ thresh_temperature += TEMP_STAGE_STEP;
+ break;
+ case TRIP_STAGE1:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *temperature = thresh_temperature;
+
+ return 0;
+}
+
+static int qpnp_tz_get_crit_temp(struct thermal_zone_device *thermal,
+ unsigned long *temperature)
+{
+ struct qpnp_tm_chip *chip = thermal->devdata;
+
+ if (!temperature)
+ return -EINVAL;
+
+ *temperature = chip->thresh * TEMP_THRESH_STEP + TEMP_THRESH_MIN +
+ 2 * TEMP_STAGE_STEP;
+
+ return 0;
+}
+
+static struct thermal_zone_device_ops qpnp_thermal_zone_ops_no_adc = {
+ .get_temp = qpnp_tz_get_temp_no_adc,
+ .get_mode = qpnp_tz_get_mode,
+ .set_mode = qpnp_tz_set_mode,
+ .get_trip_type = qpnp_tz_get_trip_type,
+ .get_trip_temp = qpnp_tz_get_trip_temp,
+ .get_crit_temp = qpnp_tz_get_crit_temp,
+};
+
+static struct thermal_zone_device_ops qpnp_thermal_zone_ops_qpnp_adc = {
+ .get_temp = qpnp_tz_get_temp_qpnp_adc,
+ .get_mode = qpnp_tz_get_mode,
+ .set_mode = qpnp_tz_set_mode,
+ .get_trip_type = qpnp_tz_get_trip_type,
+ .get_trip_temp = qpnp_tz_get_trip_temp,
+ .get_crit_temp = qpnp_tz_get_crit_temp,
+};
+
+static void qpnp_tm_work(struct work_struct *work)
+{
+ struct delayed_work *dwork
+ = container_of(work, struct delayed_work, work);
+ struct qpnp_tm_chip *chip
+ = container_of(dwork, struct qpnp_tm_chip, irq_work);
+ int rc;
+ u8 reg;
+
+ if (chip->adc_type == QPNP_TM_ADC_NONE) {
+ rc = qpnp_tm_update_temp_no_adc(chip);
+ if (rc < 0)
+ goto bail;
+ } else {
+ rc = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, ®, 1);
+ if (rc < 0)
+ goto bail;
+
+ chip->stage = reg & STATUS_STAGE_MASK;
+
+ rc = qpnp_tm_update_temp(chip);
+ if (rc < 0)
+ goto bail;
+ }
+
+ if (chip->stage != chip->prev_stage) {
+ chip->prev_stage = chip->stage;
+
+ pr_crit("%s: PMIC Temp Alarm - stage=%u, threshold=%u, temperature=%lu mC\n",
+ chip->tm_name, chip->stage, chip->thresh,
+ chip->temperature);
+
+ thermal_zone_device_update(chip->tz_dev);
+
+ /* Notify user space */
+ sysfs_notify(&chip->tz_dev->device.kobj, NULL, "type");
+ }
+
+bail:
+ return;
+}
+
+static irqreturn_t qpnp_tm_isr(int irq, void *data)
+{
+ struct qpnp_tm_chip *chip = data;
+
+ schedule_delayed_work(&chip->irq_work,
+ msecs_to_jiffies(STATUS_REGISTER_DELAY_MS) + 1);
+
+ return IRQ_HANDLED;
+}
+
+static int qpnp_tm_init_reg(struct qpnp_tm_chip *chip)
+{
+ int rc = 0;
+ u8 reg;
+
+ if (chip->thresh < THRESH_MIN || chip->thresh > THRESH_MAX) {
+ /* Read hardware threshold value if configuration is invalid. */
+ rc = qpnp_tm_read(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, ®, 1);
+ if (rc < 0)
+ return rc;
+ chip->thresh = reg & SHUTDOWN_CTRL1_THRESHOLD_MASK;
+ }
+
+ /*
+ * Set threshold and disable software override of stage 2 and 3
+ * shutdowns.
+ */
+ reg = chip->thresh & SHUTDOWN_CTRL1_THRESHOLD_MASK;
+ rc = qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, ®, 1);
+ if (rc < 0)
+ return rc;
+
+ /* Enable the thermal alarm PMIC module in always-on mode. */
+ reg = ALARM_CTRL_FORCE_ENABLE;
+ rc = qpnp_tm_write(chip, QPNP_TM_REG_ALARM_CTRL, ®, 1);
+
+ return rc;
+}
+
+static int __devinit qpnp_tm_probe(struct spmi_device *spmi)
+{
+ struct device_node *node;
+ struct resource *res;
+ struct qpnp_tm_chip *chip;
+ struct thermal_zone_device_ops *tz_ops;
+ char *tm_name;
+ u32 default_temperature;
+ int rc = 0;
+ u8 raw_type[2], type, subtype;
+
+ if (!spmi || !(&spmi->dev) || !spmi->dev.of_node) {
+ dev_err(&spmi->dev, "%s: device tree node not found\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ node = spmi->dev.of_node;
+
+ chip = kzalloc(sizeof(struct qpnp_tm_chip), GFP_KERNEL);
+ if (!chip) {
+ dev_err(&spmi->dev, "%s: Can't allocate qpnp_tm_chip\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ dev_set_drvdata(&spmi->dev, chip);
+
+ res = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&spmi->dev, "%s: node is missing base address\n",
+ __func__);
+ rc = -EINVAL;
+ goto free_chip;
+ }
+ chip->base_addr = res->start;
+ chip->spmi_dev = spmi;
+
+ chip->irq = spmi_get_irq(spmi, NULL, 0);
+ if (chip->irq < 0) {
+ rc = chip->irq;
+ dev_err(&spmi->dev, "%s: node is missing irq, rc=%d\n",
+ __func__, rc);
+ goto free_chip;
+ }
+
+ chip->tm_name = of_get_property(node, "label", NULL);
+ if (chip->tm_name == NULL) {
+ dev_err(&spmi->dev, "%s: node is missing label\n",
+ __func__);
+ rc = -EINVAL;
+ goto free_chip;
+ }
+
+ tm_name = kstrdup(chip->tm_name, GFP_KERNEL);
+ if (tm_name == NULL) {
+ dev_err(&spmi->dev, "%s: could not allocate memory for label\n",
+ __func__);
+ rc = -ENOMEM;
+ goto free_chip;
+ }
+ chip->tm_name = tm_name;
+
+ INIT_DELAYED_WORK(&chip->irq_work, qpnp_tm_work);
+
+ /* These bindings are optional, so it is okay if they are not found. */
+ chip->thresh = THRESH_MAX + 1;
+ rc = of_property_read_u32(node, "qcom,threshold-set", &chip->thresh);
+ if (!rc && (chip->thresh < THRESH_MIN || chip->thresh > THRESH_MAX))
+ dev_err(&spmi->dev, "%s: invalid qcom,threshold-set=%u specified\n",
+ __func__, chip->thresh);
+
+ chip->adc_type = QPNP_TM_ADC_NONE;
+ rc = of_property_read_u32(node, "qcom,channel-num", &chip->adc_channel);
+ if (!rc) {
+ if (chip->adc_channel < 0 || chip->adc_channel >= ADC_MAX_NUM) {
+ dev_err(&spmi->dev, "%s: invalid qcom,channel-num=%d specified\n",
+ __func__, chip->adc_channel);
+ } else {
+ chip->adc_type = QPNP_TM_ADC_QPNP_ADC;
+ rc = qpnp_vadc_is_ready();
+ if (rc) {
+ /* Probe retry, do not print an error message */
+ goto err_cancel_work;
+ }
+ }
+ }
+
+ if (chip->adc_type == QPNP_TM_ADC_QPNP_ADC)
+ tz_ops = &qpnp_thermal_zone_ops_qpnp_adc;
+ else
+ tz_ops = &qpnp_thermal_zone_ops_no_adc;
+
+ chip->allow_software_override
+ = of_property_read_bool(node, "qcom,allow-override");
+
+ default_temperature = DEFAULT_NO_ADC_TEMP;
+ rc = of_property_read_u32(node, "qcom,default-temp",
+ &default_temperature);
+ chip->temperature = default_temperature;
+
+ rc = qpnp_tm_read(chip, QPNP_TM_REG_TYPE, raw_type, 2);
+ if (rc) {
+ dev_err(&spmi->dev, "%s: could not read type register, rc=%d\n",
+ __func__, rc);
+ goto err_cancel_work;
+ }
+ type = raw_type[0];
+ subtype = raw_type[1];
+
+ if (type != QPNP_TM_TYPE || subtype != QPNP_TM_SUBTYPE) {
+ dev_err(&spmi->dev, "%s: invalid type=%02X or subtype=%02X register value\n",
+ __func__, type, subtype);
+ rc = -ENODEV;
+ goto err_cancel_work;
+ }
+
+ rc = qpnp_tm_init_reg(chip);
+ if (rc) {
+ dev_err(&spmi->dev, "%s: qpnp_tm_init_reg() failed, rc=%d\n",
+ __func__, rc);
+ goto err_cancel_work;
+ }
+
+ if (chip->adc_type == QPNP_TM_ADC_NONE) {
+ rc = qpnp_tm_init_temp_no_adc(chip);
+ if (rc) {
+ dev_err(&spmi->dev, "%s: qpnp_tm_init_temp_no_adc() failed, rc=%d\n",
+ __func__, rc);
+ goto err_cancel_work;
+ }
+ }
+
+ /* Start in HW control; switch to SW control when user changes mode. */
+ chip->mode = THERMAL_DEVICE_DISABLED;
+ rc = qpnp_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
+ if (rc) {
+ dev_err(&spmi->dev, "%s: qpnp_tm_shutdown_override() failed, rc=%d\n",
+ __func__, rc);
+ goto err_cancel_work;
+ }
+
+ chip->tz_dev = thermal_zone_device_register(tm_name, TRIP_NUM, chip,
+ tz_ops, 0, 0, 0, 0);
+ if (chip->tz_dev == NULL) {
+ dev_err(&spmi->dev, "%s: thermal_zone_device_register() failed.\n",
+ __func__);
+ rc = -ENODEV;
+ goto err_cancel_work;
+ }
+
+ rc = request_irq(chip->irq, qpnp_tm_isr, IRQF_TRIGGER_RISING, tm_name,
+ chip);
+ if (rc < 0) {
+ dev_err(&spmi->dev, "%s: request_irq(%d) failed: %d\n",
+ __func__, chip->irq, rc);
+ goto err_free_tz;
+ }
+
+ return 0;
+
+err_free_tz:
+ thermal_zone_device_unregister(chip->tz_dev);
+err_cancel_work:
+ cancel_delayed_work_sync(&chip->irq_work);
+ kfree(chip->tm_name);
+free_chip:
+ dev_set_drvdata(&spmi->dev, NULL);
+ kfree(chip);
+ return rc;
+}
+
+static int __devexit qpnp_tm_remove(struct spmi_device *spmi)
+{
+ struct qpnp_tm_chip *chip = dev_get_drvdata(&spmi->dev);
+
+ dev_set_drvdata(&spmi->dev, NULL);
+ thermal_zone_device_unregister(chip->tz_dev);
+ kfree(chip->tm_name);
+ qpnp_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
+ free_irq(chip->irq, chip);
+ cancel_delayed_work_sync(&chip->irq_work);
+ kfree(chip);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int qpnp_tm_suspend(struct device *dev)
+{
+ struct qpnp_tm_chip *chip = dev_get_drvdata(dev);
+
+ /* Clear override bits in suspend to allow hardware control */
+ qpnp_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
+
+ return 0;
+}
+
+static int qpnp_tm_resume(struct device *dev)
+{
+ struct qpnp_tm_chip *chip = dev_get_drvdata(dev);
+
+ /* Override hardware actions so software can control */
+ if (chip->mode == THERMAL_DEVICE_ENABLED)
+ qpnp_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_ENABLED);
+
+ return 0;
+}
+
+static const struct dev_pm_ops qpnp_tm_pm_ops = {
+ .suspend = qpnp_tm_suspend,
+ .resume = qpnp_tm_resume,
+};
+
+#define QPNP_TM_PM_OPS (&qpnp_tm_pm_ops)
+#else
+#define QPNP_TM_PM_OPS NULL
+#endif
+
+static struct of_device_id qpnp_tm_match_table[] = {
+ { .compatible = QPNP_TM_DRIVER_NAME, },
+ {}
+};
+
+static const struct spmi_device_id qpnp_tm_id[] = {
+ { QPNP_TM_DRIVER_NAME, 0 },
+ {}
+};
+
+static struct spmi_driver qpnp_tm_driver = {
+ .driver = {
+ .name = QPNP_TM_DRIVER_NAME,
+ .of_match_table = qpnp_tm_match_table,
+ .owner = THIS_MODULE,
+ .pm = QPNP_TM_PM_OPS,
+ },
+ .probe = qpnp_tm_probe,
+ .remove = __devexit_p(qpnp_tm_remove),
+ .id_table = qpnp_tm_id,
+};
+
+int __init qpnp_tm_init(void)
+{
+ return spmi_driver_register(&qpnp_tm_driver);
+}
+
+static void __exit qpnp_tm_exit(void)
+{
+ spmi_driver_unregister(&qpnp_tm_driver);
+}
+
+module_init(qpnp_tm_init);
+module_exit(qpnp_tm_exit);
+
+MODULE_DESCRIPTION("QPNP PMIC Temperature Alarm driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/serial/msm_serial_hs_hwreg.h b/drivers/tty/serial/msm_serial_hs_hwreg.h
index 81f3d54..8debc36 100644
--- a/drivers/tty/serial/msm_serial_hs_hwreg.h
+++ b/drivers/tty/serial/msm_serial_hs_hwreg.h
@@ -183,8 +183,8 @@
/* Parity configuration */
#define NO_PARITY 0x0
-#define EVEN_PARITY 0x1
-#define ODD_PARITY 0x2
+#define EVEN_PARITY 0x2
+#define ODD_PARITY 0x1
#define SPACE_PARITY 0x3
#define UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK 0xffffff80
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index c74ba7b..056ce18 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -451,8 +451,11 @@
INIT_LIST_HEAD(&dev->filelist);
#ifdef CONFIG_PM
- pm_runtime_set_autosuspend_delay(&dev->dev,
- usb_autosuspend_delay * 1000);
+ if (usb_hcd->driver->set_autosuspend_delay)
+ usb_hcd->driver->set_autosuspend_delay(dev);
+ else
+ pm_runtime_set_autosuspend_delay(&dev->dev,
+ usb_autosuspend_delay * 1000);
dev->connect_time = jiffies;
dev->active_duration = -jiffies;
#endif
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index a3f6e58..f431e9d 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1021,6 +1021,9 @@
dev_vdbg(dwc->dev, "queing request %p to %s length %d\n",
request, ep->name, request->length);
+ WARN(!dep->direction && (request->length % ep->desc->wMaxPacketSize),
+ "trying to queue unaligned request (%d)\n", request->length);
+
spin_lock_irqsave(&dwc->lock, flags);
ret = __dwc3_gadget_ep_queue(dep, req);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -2363,7 +2366,7 @@
dev_set_name(&dwc->gadget.dev, "gadget");
dwc->gadget.ops = &dwc3_gadget_ops;
- dwc->gadget.max_speed = USB_SPEED_SUPER;
+ dwc->gadget.max_speed = USB_SPEED_HIGH;
dwc->gadget.speed = USB_SPEED_UNKNOWN;
dwc->gadget.dev.parent = dwc->dev;
dwc->gadget.sg_supported = true;
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index e3bfbd0..52c19cf 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -52,6 +52,7 @@
#include "f_rmnet_sdio.c"
#include "f_rmnet_smd_sdio.c"
#include "f_rmnet.c"
+#include "f_audio_source.c"
#include "f_mass_storage.c"
#include "u_serial.c"
#include "u_sdio.c"
@@ -80,8 +81,10 @@
#ifdef CONFIG_TARGET_CORE
#include "f_tcm.c"
#endif
+#ifdef CONFIG_SND_PCM
#include "u_uac1.c"
#include "f_uac1.c"
+#endif
MODULE_AUTHOR("Mike Lockwood");
MODULE_DESCRIPTION("Android Composite USB Driver");
@@ -633,7 +636,7 @@
static void ecm_qc_function_unbind_config(struct android_usb_function *f,
struct usb_configuration *c)
{
- gether_qc_cleanup();
+ gether_qc_cleanup_name("ecm0");
}
static ssize_t ecm_ethaddr_show(struct device *dev,
@@ -697,13 +700,22 @@
return mbim_bind_config(c, 0);
}
+static int mbim_function_ctrlrequest(struct android_usb_function *f,
+ struct usb_composite_dev *cdev,
+ const struct usb_ctrlrequest *c)
+{
+ return mbim_ctrlrequest(cdev, c);
+}
+
static struct android_usb_function mbim_function = {
.name = "usb_mbim",
.cleanup = mbim_function_cleanup,
.bind_config = mbim_function_bind_config,
.init = mbim_function_init,
+ .ctrlrequest = mbim_function_ctrlrequest,
};
+#ifdef CONFIG_SND_PCM
/* PERIPHERAL AUDIO */
static int audio_function_bind_config(struct android_usb_function *f,
struct usb_configuration *c)
@@ -715,6 +727,7 @@
.name = "audio",
.bind_config = audio_function_bind_config,
};
+#endif
/* DIAG */
@@ -1168,7 +1181,7 @@
static void rndis_qc_function_unbind_config(struct android_usb_function *f,
struct usb_configuration *c)
{
- gether_qc_cleanup();
+ gether_qc_cleanup_name("rndis0");
}
static ssize_t rndis_manufacturer_show(struct device *dev,
@@ -1471,6 +1484,68 @@
.ctrlrequest = accessory_function_ctrlrequest,
};
+static int audio_source_function_init(struct android_usb_function *f,
+ struct usb_composite_dev *cdev)
+{
+ struct audio_source_config *config;
+
+ config = kzalloc(sizeof(struct audio_source_config), GFP_KERNEL);
+ if (!config)
+ return -ENOMEM;
+ config->card = -1;
+ config->device = -1;
+ f->config = config;
+ return 0;
+}
+
+static void audio_source_function_cleanup(struct android_usb_function *f)
+{
+ kfree(f->config);
+}
+
+static int audio_source_function_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ struct audio_source_config *config = f->config;
+
+ return audio_source_bind_config(c, config);
+}
+
+static void audio_source_function_unbind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ struct audio_source_config *config = f->config;
+
+ config->card = -1;
+ config->device = -1;
+}
+
+static ssize_t audio_source_pcm_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct audio_source_config *config = f->config;
+
+ /* print PCM card and device numbers */
+ return sprintf(buf, "%d %d\n", config->card, config->device);
+}
+
+static DEVICE_ATTR(pcm, S_IRUGO | S_IWUSR, audio_source_pcm_show, NULL);
+
+static struct device_attribute *audio_source_function_attributes[] = {
+ &dev_attr_pcm,
+ NULL
+};
+
+static struct android_usb_function audio_source_function = {
+ .name = "audio_source",
+ .init = audio_source_function_init,
+ .cleanup = audio_source_function_cleanup,
+ .bind_config = audio_source_function_bind_config,
+ .unbind_config = audio_source_function_unbind_config,
+ .attributes = audio_source_function_attributes,
+};
+
static int android_uasp_connect_cb(bool connect)
{
/*
@@ -1520,7 +1595,9 @@
static struct android_usb_function *supported_functions[] = {
&mbim_function,
&ecm_qc_function,
+#ifdef CONFIG_SND_PCM
&audio_function,
+#endif
&rmnet_smd_function,
&rmnet_sdio_function,
&rmnet_smd_sdio_function,
@@ -1537,6 +1614,7 @@
&rndis_qc_function,
&mass_storage_function,
&accessory_function,
+ &audio_source_function,
&uasp_function,
NULL
};
@@ -2169,6 +2247,11 @@
unsigned long flags;
composite_disconnect(gadget);
+ /* accessory HID support can be active while the
+ accessory function is not actually enabled,
+ so we need to inform it when we are disconnected.
+ */
+ acc_disconnect();
spin_lock_irqsave(&cdev->lock, flags);
dev->connected = 0;
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index 0ace679..fa1bf43 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -76,6 +76,8 @@
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
#define ATDTW_SET_DELAY 100 /* 100msec delay */
+#define EP_PRIME_CHECK_DELAY (jiffies + msecs_to_jiffies(1000))
+#define MAX_PRIME_CHECK_RETRY 3 /*Wait for 3sec for EP prime failure */
/* ctrl register bank access */
static DEFINE_SPINLOCK(udc_lock);
@@ -498,8 +500,6 @@
hw_cwrite(CAP_ENDPTPRIME, BIT(n), BIT(n));
- while (hw_cread(CAP_ENDPTPRIME, BIT(n)))
- cpu_relax();
if (is_ctrl && dir == RX && hw_cread(CAP_ENDPTSETUPSTAT, BIT(num)))
return -EAGAIN;
@@ -1006,6 +1006,44 @@
}
/**
+ * dbg_prime_fail: prints a PRIME FAIL event
+ * @addr: endpoint address
+ * @mEp: endpoint structure
+ */
+static void dbg_prime_fail(u8 addr, const char *name,
+ const struct ci13xxx_ep *mEp)
+{
+ char msg[DBG_DATA_MSG];
+ struct ci13xxx_req *req;
+ struct list_head *ptr = NULL;
+
+ if (mEp != NULL) {
+ scnprintf(msg, sizeof(msg),
+ "PRIME fail EP%d%s QH:%08X",
+ mEp->num, mEp->dir ? "IN" : "OUT", mEp->qh.ptr->cap);
+ dbg_print(addr, name, 0, msg);
+ scnprintf(msg, sizeof(msg),
+ "cap:%08X %08X %08X\n",
+ mEp->qh.ptr->curr, mEp->qh.ptr->td.next,
+ mEp->qh.ptr->td.token);
+ dbg_print(addr, "QHEAD", 0, msg);
+
+ list_for_each(ptr, &mEp->qh.queue) {
+ req = list_entry(ptr, struct ci13xxx_req, queue);
+ scnprintf(msg, sizeof(msg),
+ "%08X:%08X:%08X\n",
+ req->dma, req->ptr->next,
+ req->ptr->token);
+ dbg_print(addr, "REQ", 0, msg);
+ scnprintf(msg, sizeof(msg), "%08X:%d\n",
+ req->ptr->page[0],
+ req->req.status);
+ dbg_print(addr, "REQPAGE", 0, msg);
+ }
+ }
+}
+
+/**
* show_events: displays the event buffer
*
* Check "device.h" for details
@@ -1468,12 +1506,14 @@
n = hw_ep_bit(mEp->num, mEp->dir);
pr_info("%s: prime:%08x stat:%08x ep#%d dir:%s"
"dTD_update_fail_count: %lu "
- "mEp->dTD_update_fail_count: %lu\n", __func__,
+ "mEp->dTD_update_fail_count: %lu"
+ "mEp->prime_fail_count: %lu\n", __func__,
hw_cread(CAP_ENDPTPRIME, ~0),
hw_cread(CAP_ENDPTSTAT, ~0),
mEp->num, mEp->dir ? "IN" : "OUT",
udc->dTD_update_fail_count,
- mEp->dTD_update_fail_count);
+ mEp->dTD_update_fail_count,
+ mEp->prime_fail_count);
pr_info("QH: cap:%08x cur:%08x next:%08x token:%08x\n",
mEp->qh.ptr->cap, mEp->qh.ptr->curr,
@@ -1661,6 +1701,57 @@
return ((ep->dir == TX) ? USB_ENDPOINT_DIR_MASK : 0) | ep->num;
}
+static void ep_prime_timer_func(unsigned long data)
+{
+ struct ci13xxx_ep *mEp = (struct ci13xxx_ep *)data;
+ struct ci13xxx_req *req;
+ struct list_head *ptr = NULL;
+ int n = hw_ep_bit(mEp->num, mEp->dir);
+ unsigned long flags;
+
+
+ spin_lock_irqsave(mEp->lock, flags);
+ if (!hw_cread(CAP_ENDPTPRIME, BIT(n)))
+ goto out;
+
+ if (list_empty(&mEp->qh.queue))
+ goto out;
+
+ req = list_entry(mEp->qh.queue.next, struct ci13xxx_req, queue);
+
+ mb();
+ if (!(TD_STATUS_ACTIVE & req->ptr->token))
+ goto out;
+
+ mEp->prime_timer_count++;
+ if (mEp->prime_timer_count == MAX_PRIME_CHECK_RETRY) {
+ mEp->prime_timer_count = 0;
+ pr_info("ep%d dir:%s QH:cap:%08x cur:%08x next:%08x tkn:%08x\n",
+ mEp->num, mEp->dir ? "IN" : "OUT",
+ mEp->qh.ptr->cap, mEp->qh.ptr->curr,
+ mEp->qh.ptr->td.next, mEp->qh.ptr->td.token);
+ list_for_each(ptr, &mEp->qh.queue) {
+ req = list_entry(ptr, struct ci13xxx_req, queue);
+ pr_info("\treq:%08xnext:%08xtkn:%08xpage0:%08xsts:%d\n",
+ req->dma, req->ptr->next,
+ req->ptr->token, req->ptr->page[0],
+ req->req.status);
+ }
+ dbg_prime_fail(0xFF, "PRIMEF", mEp);
+ mEp->prime_fail_count++;
+ } else {
+ mod_timer(&mEp->prime_timer, EP_PRIME_CHECK_DELAY);
+ }
+
+ spin_unlock_irqrestore(mEp->lock, flags);
+ return;
+
+out:
+ mEp->prime_timer_count = 0;
+ spin_unlock_irqrestore(mEp->lock, flags);
+
+}
+
/**
* _hardware_queue: configures a request at hardware level
* @gadget: gadget
@@ -1852,6 +1943,8 @@
ret = hw_ep_prime(mEp->num, mEp->dir,
mEp->type == USB_ENDPOINT_XFER_CONTROL);
+ if (!ret)
+ mod_timer(&mEp->prime_timer, EP_PRIME_CHECK_DELAY);
done:
return ret;
}
@@ -2300,6 +2393,8 @@
if (list_empty(&mEp->qh.queue))
return 0;
+ del_timer(&mEp->prime_timer);
+ mEp->prime_timer_count = 0;
list_for_each_entry_safe(mReq, mReqTemp, &mEp->qh.queue,
queue) {
dequeue:
@@ -2311,7 +2406,8 @@
* bits. This is a temporary workaround till
* HW designers come back on this.
*/
- if (retval == -EBUSY && req_dequeue && mEp->dir == 0) {
+ if (retval == -EBUSY && req_dequeue &&
+ (mEp->dir == 0 || mEp->num == 0)) {
req_dequeue = 0;
udc->dTD_update_fail_count++;
mEp->dTD_update_fail_count++;
@@ -2574,6 +2670,7 @@
struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep);
int retval = 0;
unsigned long flags;
+ unsigned mult = 0;
trace("ep = %p, desc = %p", ep, desc);
@@ -2599,13 +2696,15 @@
mEp->qh.ptr->cap = 0;
- if (mEp->type == USB_ENDPOINT_XFER_CONTROL)
+ if (mEp->type == USB_ENDPOINT_XFER_CONTROL) {
mEp->qh.ptr->cap |= QH_IOS;
- else if (mEp->type == USB_ENDPOINT_XFER_ISOC) {
+ } else if (mEp->type == USB_ENDPOINT_XFER_ISOC) {
mEp->qh.ptr->cap &= ~QH_MULT;
- mEp->qh.ptr->cap |= BIT(30);
- } else
+ mult = ((mEp->ep.maxpacket >> QH_MULT_SHIFT) + 1) & 0x03;
+ mEp->qh.ptr->cap |= (mult << ffs_nr(QH_MULT));
+ } else {
mEp->qh.ptr->cap |= QH_ZLT;
+ }
mEp->qh.ptr->cap |=
(mEp->ep.maxpacket << ffs_nr(QH_MAX_PKT)) & QH_MAX_PKT;
@@ -2647,6 +2746,8 @@
/* only internal SW should disable ctrl endpts */
+ del_timer(&mEp->prime_timer);
+ mEp->prime_timer_count = 0;
direction = mEp->dir;
do {
dbg_event(_usb_addr(mEp), "DISABLE", 0);
@@ -2959,6 +3060,8 @@
spin_lock_irqsave(mEp->lock, flags);
+ del_timer(&mEp->prime_timer);
+ mEp->prime_timer_count = 0;
dbg_event(_usb_addr(mEp), "FFLUSH", 0);
hw_ep_flush(mEp->num, mEp->dir);
@@ -3431,6 +3534,8 @@
for (i = 0; i < hw_ep_max; i++) {
struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i];
INIT_LIST_HEAD(&mEp->ep.ep_list);
+ setup_timer(&mEp->prime_timer, ep_prime_timer_func,
+ (unsigned long) mEp);
}
if (!(udc->udc_driver->flags & CI13XXX_REGS_SHARED)) {
diff --git a/drivers/usb/gadget/ci13xxx_udc.h b/drivers/usb/gadget/ci13xxx_udc.h
index 3162e15..6b3cad8 100644
--- a/drivers/usb/gadget/ci13xxx_udc.h
+++ b/drivers/usb/gadget/ci13xxx_udc.h
@@ -67,6 +67,7 @@
#define QH_MAX_PKT (0x07FFUL << 16)
#define QH_ZLT BIT(29)
#define QH_MULT (0x0003UL << 30)
+#define QH_MULT_SHIFT 11
/* 1 */
u32 curr;
/* 2 - 8 */
@@ -107,6 +108,9 @@
struct device *device;
struct dma_pool *td_pool;
unsigned long dTD_update_fail_count;
+ unsigned long prime_fail_count;
+ int prime_timer_count;
+ struct timer_list prime_timer;
};
struct ci13xxx;
diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c
index 108caf9..42a6c43 100644
--- a/drivers/usb/gadget/f_accessory.c
+++ b/drivers/usb/gadget/f_accessory.c
@@ -33,6 +33,8 @@
#include <linux/device.h>
#include <linux/miscdevice.h>
+#include <linux/hid.h>
+#include <linux/hiddev.h>
#include <linux/usb.h>
#include <linux/usb/ch9.h>
#include <linux/usb/f_accessory.h>
@@ -40,7 +42,7 @@
#define BULK_BUFFER_SIZE 16384
#define ACC_STRING_SIZE 256
-#define PROTOCOL_VERSION 1
+#define PROTOCOL_VERSION 2
/* String IDs */
#define INTERFACE_STRING_INDEX 0
@@ -49,6 +51,20 @@
#define TX_REQ_MAX 4
#define RX_REQ_MAX 2
+struct acc_hid_dev {
+ struct list_head list;
+ struct hid_device *hid;
+ struct acc_dev *dev;
+ /* accessory defined ID */
+ int id;
+ /* HID report descriptor */
+ u8 *report_desc;
+ /* length of HID report descriptor */
+ int report_desc_len;
+ /* number of bytes of report_desc we have received so far */
+ int report_desc_offset;
+};
+
struct acc_dev {
struct usb_function function;
struct usb_composite_dev *cdev;
@@ -78,6 +94,8 @@
/* set to 1 if we have a pending start request */
int start_requested;
+ int audio_mode;
+
/* synchronize access to our device file */
atomic_t open_excl;
@@ -87,7 +105,21 @@
wait_queue_head_t write_wq;
struct usb_request *rx_req[RX_REQ_MAX];
int rx_done;
- struct delayed_work work;
+
+ /* delayed work for handling ACCESSORY_START */
+ struct delayed_work start_work;
+
+ /* worker for registering and unregistering hid devices */
+ struct work_struct hid_work;
+
+ /* list of active HID devices */
+ struct list_head hid_list;
+
+ /* list of new HID devices to register */
+ struct list_head new_hid_list;
+
+ /* list of dead HID devices to unregister */
+ struct list_head dead_hid_list;
};
static struct usb_interface_descriptor acc_interface_desc = {
@@ -296,6 +328,160 @@
}
}
+static void acc_complete_set_hid_report_desc(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ struct acc_hid_dev *hid = req->context;
+ struct acc_dev *dev = hid->dev;
+ int length = req->actual;
+
+ if (req->status != 0) {
+ pr_err("acc_complete_set_hid_report_desc, err %d\n",
+ req->status);
+ return;
+ }
+
+ memcpy(hid->report_desc + hid->report_desc_offset, req->buf, length);
+ hid->report_desc_offset += length;
+ if (hid->report_desc_offset == hid->report_desc_len) {
+ /* After we have received the entire report descriptor
+ * we schedule work to initialize the HID device
+ */
+ schedule_work(&dev->hid_work);
+ }
+}
+
+static void acc_complete_send_hid_event(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ struct acc_hid_dev *hid = req->context;
+ int length = req->actual;
+
+ if (req->status != 0) {
+ pr_err("acc_complete_send_hid_event, err %d\n", req->status);
+ return;
+ }
+
+ hid_report_raw_event(hid->hid, HID_INPUT_REPORT, req->buf, length, 1);
+}
+
+static int acc_hid_parse(struct hid_device *hid)
+{
+ struct acc_hid_dev *hdev = hid->driver_data;
+
+ hid_parse_report(hid, hdev->report_desc, hdev->report_desc_len);
+ return 0;
+}
+
+static int acc_hid_start(struct hid_device *hid)
+{
+ return 0;
+}
+
+static void acc_hid_stop(struct hid_device *hid)
+{
+}
+
+static int acc_hid_open(struct hid_device *hid)
+{
+ return 0;
+}
+
+static void acc_hid_close(struct hid_device *hid)
+{
+}
+
+static struct hid_ll_driver acc_hid_ll_driver = {
+ .parse = acc_hid_parse,
+ .start = acc_hid_start,
+ .stop = acc_hid_stop,
+ .open = acc_hid_open,
+ .close = acc_hid_close,
+};
+
+static struct acc_hid_dev *acc_hid_new(struct acc_dev *dev,
+ int id, int desc_len)
+{
+ struct acc_hid_dev *hdev;
+
+ hdev = kzalloc(sizeof(*hdev), GFP_ATOMIC);
+ if (!hdev)
+ return NULL;
+ hdev->report_desc = kzalloc(desc_len, GFP_ATOMIC);
+ if (!hdev->report_desc) {
+ kfree(hdev);
+ return NULL;
+ }
+ hdev->dev = dev;
+ hdev->id = id;
+ hdev->report_desc_len = desc_len;
+
+ return hdev;
+}
+
+static struct acc_hid_dev *acc_hid_get(struct list_head *list, int id)
+{
+ struct acc_hid_dev *hid;
+
+ list_for_each_entry(hid, list, list) {
+ if (hid->id == id)
+ return hid;
+ }
+ return NULL;
+}
+
+static int acc_register_hid(struct acc_dev *dev, int id, int desc_length)
+{
+ struct acc_hid_dev *hid;
+ unsigned long flags;
+
+ /* report descriptor length must be > 0 */
+ if (desc_length <= 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ /* replace HID if one already exists with this ID */
+ hid = acc_hid_get(&dev->hid_list, id);
+ if (!hid)
+ hid = acc_hid_get(&dev->new_hid_list, id);
+ if (hid)
+ list_move(&hid->list, &dev->dead_hid_list);
+
+ hid = acc_hid_new(dev, id, desc_length);
+ if (!hid) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -ENOMEM;
+ }
+
+ list_add(&hid->list, &dev->new_hid_list);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /* schedule work to register the HID device */
+ schedule_work(&dev->hid_work);
+ return 0;
+}
+
+static int acc_unregister_hid(struct acc_dev *dev, int id)
+{
+ struct acc_hid_dev *hid;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ hid = acc_hid_get(&dev->hid_list, id);
+ if (!hid)
+ hid = acc_hid_get(&dev->new_hid_list, id);
+ if (!hid) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -EINVAL;
+ }
+
+ list_move(&hid->list, &dev->dead_hid_list);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ schedule_work(&dev->hid_work);
+ return 0;
+}
+
static int create_bulk_endpoints(struct acc_dev *dev,
struct usb_endpoint_descriptor *in_desc,
struct usb_endpoint_descriptor *out_desc)
@@ -353,7 +539,7 @@
return 0;
fail:
- printk(KERN_ERR "acc_bind() could not allocate requests\n");
+ pr_err("acc_bind() could not allocate requests\n");
while ((req = req_get(dev, &dev->tx_idle)))
acc_request_free(req, dev->ep_in);
for (i = 0; i < RX_REQ_MAX; i++)
@@ -510,6 +696,8 @@
break;
case ACCESSORY_IS_START_REQUESTED:
return dev->start_requested;
+ case ACCESSORY_GET_AUDIO_MODE:
+ return dev->audio_mode;
}
if (!src)
return -EINVAL;
@@ -540,7 +728,7 @@
return 0;
}
-/* file operations for /dev/acc_usb */
+/* file operations for /dev/usb_accessory */
static const struct file_operations acc_fops = {
.owner = THIS_MODULE,
.read = acc_read,
@@ -550,23 +738,47 @@
.release = acc_release,
};
+static int acc_hid_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ int ret;
+
+ ret = hid_parse(hdev);
+ if (ret)
+ return ret;
+ return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+}
+
static struct miscdevice acc_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "usb_accessory",
.fops = &acc_fops,
};
+static const struct hid_device_id acc_hid_table[] = {
+ { HID_USB_DEVICE(HID_ANY_ID, HID_ANY_ID) },
+ { }
+};
+
+static struct hid_driver acc_hid_driver = {
+ .name = "USB accessory",
+ .id_table = acc_hid_table,
+ .probe = acc_hid_probe,
+};
static int acc_ctrlrequest(struct usb_composite_dev *cdev,
const struct usb_ctrlrequest *ctrl)
{
struct acc_dev *dev = _acc_dev;
int value = -EOPNOTSUPP;
+ struct acc_hid_dev *hid;
+ int offset;
u8 b_requestType = ctrl->bRequestType;
u8 b_request = ctrl->bRequest;
u16 w_index = le16_to_cpu(ctrl->wIndex);
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
+ unsigned long flags;
/*
printk(KERN_INFO "acc_ctrlrequest "
@@ -579,20 +791,56 @@
if (b_request == ACCESSORY_START) {
dev->start_requested = 1;
schedule_delayed_work(
- &dev->work, msecs_to_jiffies(10));
+ &dev->start_work, msecs_to_jiffies(10));
value = 0;
} else if (b_request == ACCESSORY_SEND_STRING) {
dev->string_index = w_index;
cdev->gadget->ep0->driver_data = dev;
cdev->req->complete = acc_complete_set_string;
value = w_length;
+ } else if (b_request == ACCESSORY_SET_AUDIO_MODE &&
+ w_index == 0 && w_length == 0) {
+ dev->audio_mode = w_value;
+ value = 0;
+ } else if (b_request == ACCESSORY_REGISTER_HID) {
+ value = acc_register_hid(dev, w_value, w_index);
+ } else if (b_request == ACCESSORY_UNREGISTER_HID) {
+ value = acc_unregister_hid(dev, w_value);
+ } else if (b_request == ACCESSORY_SET_HID_REPORT_DESC) {
+ spin_lock_irqsave(&dev->lock, flags);
+ hid = acc_hid_get(&dev->new_hid_list, w_value);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ if (!hid) {
+ value = -EINVAL;
+ goto err;
+ }
+ offset = w_index;
+ if (offset != hid->report_desc_offset
+ || offset + w_length > hid->report_desc_len) {
+ value = -EINVAL;
+ goto err;
+ }
+ cdev->req->context = hid;
+ cdev->req->complete = acc_complete_set_hid_report_desc;
+ value = w_length;
+ } else if (b_request == ACCESSORY_SEND_HID_EVENT) {
+ spin_lock_irqsave(&dev->lock, flags);
+ hid = acc_hid_get(&dev->hid_list, w_value);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ if (!hid) {
+ value = -EINVAL;
+ goto err;
+ }
+ cdev->req->context = hid;
+ cdev->req->complete = acc_complete_send_hid_event;
+ value = w_length;
}
} else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) {
if (b_request == ACCESSORY_GET_PROTOCOL) {
*((u16 *)cdev->req->buf) = PROTOCOL_VERSION;
value = sizeof(u16);
- /* clear any string left over from a previous session */
+ /* clear strings left over from a previous session */
memset(dev->manufacturer, 0, sizeof(dev->manufacturer));
memset(dev->model, 0, sizeof(dev->model));
memset(dev->description, 0, sizeof(dev->description));
@@ -600,6 +848,7 @@
memset(dev->uri, 0, sizeof(dev->uri));
memset(dev->serial, 0, sizeof(dev->serial));
dev->start_requested = 0;
+ dev->audio_mode = 0;
}
}
@@ -612,6 +861,7 @@
__func__);
}
+err:
if (value == -EOPNOTSUPP)
VDBG(cdev,
"unknown class-specific control req "
@@ -631,6 +881,10 @@
DBG(cdev, "acc_function_bind dev: %p\n", dev);
+ ret = hid_register_driver(&acc_hid_driver);
+ if (ret)
+ return ret;
+
dev->start_requested = 0;
/* allocate interface ID(s) */
@@ -660,6 +914,36 @@
}
static void
+kill_all_hid_devices(struct acc_dev *dev)
+{
+ struct acc_hid_dev *hid;
+ struct list_head *entry, *temp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ list_for_each_safe(entry, temp, &dev->hid_list) {
+ hid = list_entry(entry, struct acc_hid_dev, list);
+ list_del(&hid->list);
+ list_add(&hid->list, &dev->dead_hid_list);
+ }
+ list_for_each_safe(entry, temp, &dev->new_hid_list) {
+ hid = list_entry(entry, struct acc_hid_dev, list);
+ list_del(&hid->list);
+ list_add(&hid->list, &dev->dead_hid_list);
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ schedule_work(&dev->hid_work);
+}
+
+static void
+acc_hid_unbind(struct acc_dev *dev)
+{
+ hid_unregister_driver(&acc_hid_driver);
+ kill_all_hid_devices(dev);
+}
+
+static void
acc_function_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct acc_dev *dev = func_to_dev(f);
@@ -670,14 +954,104 @@
acc_request_free(req, dev->ep_in);
for (i = 0; i < RX_REQ_MAX; i++)
acc_request_free(dev->rx_req[i], dev->ep_out);
+
+ acc_hid_unbind(dev);
}
-static void acc_work(struct work_struct *data)
+static void acc_start_work(struct work_struct *data)
{
char *envp[2] = { "ACCESSORY=START", NULL };
kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp);
}
+static int acc_hid_init(struct acc_hid_dev *hdev)
+{
+ struct hid_device *hid;
+ int ret;
+
+ hid = hid_allocate_device();
+ if (IS_ERR(hid))
+ return PTR_ERR(hid);
+
+ hid->ll_driver = &acc_hid_ll_driver;
+ hid->dev.parent = acc_device.this_device;
+
+ hid->bus = BUS_USB;
+ hid->vendor = HID_ANY_ID;
+ hid->product = HID_ANY_ID;
+ hid->driver_data = hdev;
+ ret = hid_add_device(hid);
+ if (ret) {
+ pr_err("can't add hid device: %d\n", ret);
+ hid_destroy_device(hid);
+ return ret;
+ }
+
+ hdev->hid = hid;
+ return 0;
+}
+
+static void acc_hid_delete(struct acc_hid_dev *hid)
+{
+ kfree(hid->report_desc);
+ kfree(hid);
+}
+
+static void acc_hid_work(struct work_struct *data)
+{
+ struct acc_dev *dev = _acc_dev;
+ struct list_head *entry, *temp;
+ struct acc_hid_dev *hid;
+ struct list_head new_list, dead_list;
+ unsigned long flags;
+
+ INIT_LIST_HEAD(&new_list);
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ /* copy hids that are ready for initialization to new_list */
+ list_for_each_safe(entry, temp, &dev->new_hid_list) {
+ hid = list_entry(entry, struct acc_hid_dev, list);
+ if (hid->report_desc_offset == hid->report_desc_len)
+ list_move(&hid->list, &new_list);
+ }
+
+ if (list_empty(&dev->dead_hid_list)) {
+ INIT_LIST_HEAD(&dead_list);
+ } else {
+ /* move all of dev->dead_hid_list to dead_list */
+ dead_list.prev = dev->dead_hid_list.prev;
+ dead_list.next = dev->dead_hid_list.next;
+ dead_list.next->prev = &dead_list;
+ dead_list.prev->next = &dead_list;
+ INIT_LIST_HEAD(&dev->dead_hid_list);
+ }
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /* register new HID devices */
+ list_for_each_safe(entry, temp, &new_list) {
+ hid = list_entry(entry, struct acc_hid_dev, list);
+ if (acc_hid_init(hid)) {
+ pr_err("can't add HID device %p\n", hid);
+ acc_hid_delete(hid);
+ } else {
+ spin_lock_irqsave(&dev->lock, flags);
+ list_move(&hid->list, &dev->hid_list);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ }
+ }
+
+ /* remove dead HID devices */
+ list_for_each_safe(entry, temp, &dead_list) {
+ hid = list_entry(entry, struct acc_hid_dev, list);
+ list_del(&hid->list);
+ if (hid->hid)
+ hid_destroy_device(hid->hid);
+ acc_hid_delete(hid);
+ }
+}
+
static int acc_function_set_alt(struct usb_function *f,
unsigned intf, unsigned alt)
{
@@ -783,7 +1157,11 @@
init_waitqueue_head(&dev->write_wq);
atomic_set(&dev->open_excl, 0);
INIT_LIST_HEAD(&dev->tx_idle);
- INIT_DELAYED_WORK(&dev->work, acc_work);
+ INIT_LIST_HEAD(&dev->hid_list);
+ INIT_LIST_HEAD(&dev->new_hid_list);
+ INIT_LIST_HEAD(&dev->dead_hid_list);
+ INIT_DELAYED_WORK(&dev->start_work, acc_start_work);
+ INIT_WORK(&dev->hid_work, acc_hid_work);
/* _acc_dev must be set before calling usb_gadget_register_driver */
_acc_dev = dev;
@@ -796,10 +1174,16 @@
err:
kfree(dev);
- printk(KERN_ERR "USB accessory gadget driver failed to initialize\n");
+ pr_err("USB accessory gadget driver failed to initialize\n");
return ret;
}
+static void acc_disconnect(void)
+{
+ /* unregister all HID devices if USB is disconnected */
+ kill_all_hid_devices(_acc_dev);
+}
+
static void acc_cleanup(void)
{
misc_deregister(&acc_device);
diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c
index 045fc6c..7966a79 100644
--- a/drivers/usb/gadget/f_adb.c
+++ b/drivers/usb/gadget/f_adb.c
@@ -55,6 +55,7 @@
wait_queue_head_t write_wq;
struct usb_request *rx_req;
int rx_done;
+ bool notify_close;
};
static struct usb_interface_descriptor adb_interface_desc = {
@@ -423,8 +424,10 @@
/* clear the error latch */
atomic_set(&_adb_dev->error, 0);
- adb_ready_callback();
+ if (_adb_dev->notify_close)
+ adb_ready_callback();
+ _adb_dev->notify_close = true;
return 0;
}
@@ -432,7 +435,16 @@
{
pr_info("adb_release\n");
- adb_closed_callback();
+ /*
+ * ADB daemon closes the device file after I/O error. The
+ * I/O error happen when Rx requests are flushed during
+ * cable disconnect or bus reset in configured state. Disabling
+ * USB configuration and pull-up during these scenarios are
+ * undesired. We want to force bus reset only for certain
+ * commands like "adb root" and "adb usb".
+ */
+ if (_adb_dev->notify_close)
+ adb_closed_callback();
adb_unlock(&_adb_dev->open_excl);
return 0;
@@ -561,6 +573,12 @@
struct usb_composite_dev *cdev = dev->cdev;
DBG(cdev, "adb_function_disable cdev %p\n", cdev);
+ /*
+ * Bus reset happened or cable disconnected. No
+ * need to disable the configuration now. We will
+ * set noify_close to true when device file is re-opened.
+ */
+ dev->notify_close = false;
atomic_set(&dev->online, 0);
atomic_set(&dev->error, 1);
usb_ep_disable(dev->ep_in);
@@ -607,6 +625,7 @@
atomic_set(&dev->open_excl, 0);
atomic_set(&dev->read_excl, 0);
atomic_set(&dev->write_excl, 0);
+ dev->notify_close = true;
INIT_LIST_HEAD(&dev->tx_idle);
diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c
new file mode 100644
index 0000000..aae941e
--- /dev/null
+++ b/drivers/usb/gadget/f_audio_source.c
@@ -0,0 +1,832 @@
+/*
+ * Gadget Function Driver for USB audio source device
+ *
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/device.h>
+#include <linux/usb/audio.h>
+#include <linux/wait.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+
+#define SAMPLE_RATE 44100
+#define FRAMES_PER_MSEC (SAMPLE_RATE / 1000)
+
+#define IN_EP_MAX_PACKET_SIZE 256
+
+/* Number of requests to allocate */
+#define IN_EP_REQ_COUNT 4
+
+#define AUDIO_AC_INTERFACE 0
+#define AUDIO_AS_INTERFACE 1
+#define AUDIO_NUM_INTERFACES 2
+
+/* B.3.1 Standard AC Interface Descriptor */
+static struct usb_interface_descriptor audio_source_ac_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+};
+
+
+#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(AUDIO_NUM_INTERFACES)
+/* 1 input terminal, 1 output terminal and 1 feature unit */
+#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH \
+ + UAC_DT_INPUT_TERMINAL_SIZE + UAC_DT_OUTPUT_TERMINAL_SIZE \
+ + UAC_DT_FEATURE_UNIT_SIZE(0))
+/* B.3.2 Class-Specific AC Interface Descriptor */
+static struct uac1_ac_header_descriptor_2 audio_source_ac_header_desc = {
+ .bLength = UAC_DT_AC_HEADER_LENGTH,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_HEADER,
+ .bcdADC = __constant_cpu_to_le16(0x0100),
+ .wTotalLength = __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH),
+ .bInCollection = AUDIO_NUM_INTERFACES,
+ .baInterfaceNr = {
+ [0] = AUDIO_AC_INTERFACE,
+ [1] = AUDIO_AS_INTERFACE,
+ }
+};
+
+#define INPUT_TERMINAL_ID 1
+static struct uac_input_terminal_descriptor input_terminal_desc = {
+ .bLength = UAC_DT_INPUT_TERMINAL_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_INPUT_TERMINAL,
+ .bTerminalID = INPUT_TERMINAL_ID,
+ .wTerminalType = UAC_INPUT_TERMINAL_MICROPHONE,
+ .bAssocTerminal = 0,
+ .wChannelConfig = 0x3,
+};
+
+DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0);
+
+#define FEATURE_UNIT_ID 2
+static struct uac_feature_unit_descriptor_0 feature_unit_desc = {
+ .bLength = UAC_DT_FEATURE_UNIT_SIZE(0),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_FEATURE_UNIT,
+ .bUnitID = FEATURE_UNIT_ID,
+ .bSourceID = INPUT_TERMINAL_ID,
+ .bControlSize = 2,
+};
+
+#define OUTPUT_TERMINAL_ID 3
+static struct uac1_output_terminal_descriptor output_terminal_desc = {
+ .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
+ .bTerminalID = OUTPUT_TERMINAL_ID,
+ .wTerminalType = UAC_TERMINAL_STREAMING,
+ .bAssocTerminal = FEATURE_UNIT_ID,
+ .bSourceID = FEATURE_UNIT_ID,
+};
+
+/* B.4.1 Standard AS Interface Descriptor */
+static struct usb_interface_descriptor as_interface_alt_0_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
+};
+
+static struct usb_interface_descriptor as_interface_alt_1_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bAlternateSetting = 1,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
+};
+
+/* B.4.2 Class-Specific AS Interface Descriptor */
+static struct uac1_as_header_descriptor as_header_desc = {
+ .bLength = UAC_DT_AS_HEADER_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_AS_GENERAL,
+ .bTerminalLink = INPUT_TERMINAL_ID,
+ .bDelay = 1,
+ .wFormatTag = UAC_FORMAT_TYPE_I_PCM,
+};
+
+static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = {
+ .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_FORMAT_TYPE,
+ .bFormatType = UAC_FORMAT_TYPE_I,
+ .bSubframeSize = 2,
+ .bBitResolution = 16,
+ .bSamFreqType = 1,
+};
+
+/* Standard ISO IN Endpoint Descriptor for highspeed */
+static struct usb_endpoint_descriptor hs_as_in_ep_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_SYNC_SYNC
+ | USB_ENDPOINT_XFER_ISOC,
+ .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE),
+ .bInterval = 4, /* poll 1 per millisecond */
+};
+
+/* Standard ISO IN Endpoint Descriptor for highspeed */
+static struct usb_endpoint_descriptor fs_as_in_ep_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_SYNC_SYNC
+ | USB_ENDPOINT_XFER_ISOC,
+ .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE),
+ .bInterval = 1, /* poll 1 per millisecond */
+};
+
+/* Class-specific AS ISO OUT Endpoint Descriptor */
+static struct uac_iso_endpoint_descriptor as_iso_in_desc = {
+ .bLength = UAC_ISO_ENDPOINT_DESC_SIZE,
+ .bDescriptorType = USB_DT_CS_ENDPOINT,
+ .bDescriptorSubtype = UAC_EP_GENERAL,
+ .bmAttributes = 1,
+ .bLockDelayUnits = 1,
+ .wLockDelay = __constant_cpu_to_le16(1),
+};
+
+static struct usb_descriptor_header *hs_audio_desc[] = {
+ (struct usb_descriptor_header *)&audio_source_ac_interface_desc,
+ (struct usb_descriptor_header *)&audio_source_ac_header_desc,
+
+ (struct usb_descriptor_header *)&input_terminal_desc,
+ (struct usb_descriptor_header *)&output_terminal_desc,
+ (struct usb_descriptor_header *)&feature_unit_desc,
+
+ (struct usb_descriptor_header *)&as_interface_alt_0_desc,
+ (struct usb_descriptor_header *)&as_interface_alt_1_desc,
+ (struct usb_descriptor_header *)&as_header_desc,
+
+ (struct usb_descriptor_header *)&as_type_i_desc,
+
+ (struct usb_descriptor_header *)&hs_as_in_ep_desc,
+ (struct usb_descriptor_header *)&as_iso_in_desc,
+ NULL,
+};
+
+static struct usb_descriptor_header *fs_audio_desc[] = {
+ (struct usb_descriptor_header *)&audio_source_ac_interface_desc,
+ (struct usb_descriptor_header *)&audio_source_ac_header_desc,
+
+ (struct usb_descriptor_header *)&input_terminal_desc,
+ (struct usb_descriptor_header *)&output_terminal_desc,
+ (struct usb_descriptor_header *)&feature_unit_desc,
+
+ (struct usb_descriptor_header *)&as_interface_alt_0_desc,
+ (struct usb_descriptor_header *)&as_interface_alt_1_desc,
+ (struct usb_descriptor_header *)&as_header_desc,
+
+ (struct usb_descriptor_header *)&as_type_i_desc,
+
+ (struct usb_descriptor_header *)&fs_as_in_ep_desc,
+ (struct usb_descriptor_header *)&as_iso_in_desc,
+ NULL,
+};
+
+static struct snd_pcm_hardware audio_hw_info = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
+
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = SAMPLE_RATE,
+ .rate_max = SAMPLE_RATE,
+
+ .buffer_bytes_max = 1024 * 1024,
+ .period_bytes_min = 64,
+ .period_bytes_max = 512 * 1024,
+ .periods_min = 2,
+ .periods_max = 1024,
+};
+
+/*-------------------------------------------------------------------------*/
+
+struct audio_source_config {
+ int card;
+ int device;
+};
+
+struct audio_dev {
+ struct usb_function func;
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ struct snd_pcm_substream *substream;
+
+ struct list_head idle_reqs;
+ struct usb_ep *in_ep;
+
+ spinlock_t lock;
+
+ /* beginning, end and current position in our buffer */
+ void *buffer_start;
+ void *buffer_end;
+ void *buffer_pos;
+
+ /* byte size of a "period" */
+ unsigned int period;
+ /* bytes sent since last call to snd_pcm_period_elapsed */
+ unsigned int period_offset;
+ /* time we started playing */
+ ktime_t start_time;
+ /* number of frames sent since start_time */
+ s64 frames_sent;
+};
+
+static inline struct audio_dev *func_to_audio_source(struct usb_function *f)
+{
+ return container_of(f, struct audio_dev, func);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_request *audio_request_new(struct usb_ep *ep, int buffer_size)
+{
+ struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (!req)
+ return NULL;
+
+ req->buf = kmalloc(buffer_size, GFP_KERNEL);
+ if (!req->buf) {
+ usb_ep_free_request(ep, req);
+ return NULL;
+ }
+ req->length = buffer_size;
+ return req;
+}
+
+static void audio_request_free(struct usb_request *req, struct usb_ep *ep)
+{
+ if (req) {
+ kfree(req->buf);
+ usb_ep_free_request(ep, req);
+ }
+}
+
+static void audio_req_put(struct audio_dev *audio, struct usb_request *req)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->lock, flags);
+ list_add_tail(&req->list, &audio->idle_reqs);
+ spin_unlock_irqrestore(&audio->lock, flags);
+}
+
+static struct usb_request *audio_req_get(struct audio_dev *audio)
+{
+ unsigned long flags;
+ struct usb_request *req;
+
+ spin_lock_irqsave(&audio->lock, flags);
+ if (list_empty(&audio->idle_reqs)) {
+ req = 0;
+ } else {
+ req = list_first_entry(&audio->idle_reqs, struct usb_request,
+ list);
+ list_del(&req->list);
+ }
+ spin_unlock_irqrestore(&audio->lock, flags);
+ return req;
+}
+
+/* send the appropriate number of packets to match our bitrate */
+static void audio_send(struct audio_dev *audio)
+{
+ struct snd_pcm_runtime *runtime;
+ struct usb_request *req;
+ int length, length1, length2, ret;
+ s64 msecs;
+ s64 frames;
+ ktime_t now;
+
+ /* audio->substream will be null if we have been closed */
+ if (!audio->substream)
+ return;
+ /* audio->buffer_pos will be null if we have been stopped */
+ if (!audio->buffer_pos)
+ return;
+
+ runtime = audio->substream->runtime;
+
+ /* compute number of frames to send */
+ now = ktime_get();
+ msecs = ktime_to_ns(now) - ktime_to_ns(audio->start_time);
+ do_div(msecs, 1000000);
+ frames = msecs * SAMPLE_RATE;
+ do_div(frames, 1000);
+
+ /* Readjust our frames_sent if we fall too far behind.
+ * If we get too far behind it is better to drop some frames than
+ * to keep sending data too fast in an attempt to catch up.
+ */
+ if (frames - audio->frames_sent > 10 * FRAMES_PER_MSEC)
+ audio->frames_sent = frames - FRAMES_PER_MSEC;
+
+ frames -= audio->frames_sent;
+
+ /* We need to send something to keep the pipeline going */
+ if (frames <= 0)
+ frames = FRAMES_PER_MSEC;
+
+ while (frames > 0) {
+ req = audio_req_get(audio);
+ if (!req)
+ break;
+
+ length = frames_to_bytes(runtime, frames);
+ if (length > IN_EP_MAX_PACKET_SIZE)
+ length = IN_EP_MAX_PACKET_SIZE;
+
+ if (audio->buffer_pos + length > audio->buffer_end)
+ length1 = audio->buffer_end - audio->buffer_pos;
+ else
+ length1 = length;
+ memcpy(req->buf, audio->buffer_pos, length1);
+ if (length1 < length) {
+ /* Wrap around and copy remaining length
+ * at beginning of buffer.
+ */
+ length2 = length - length1;
+ memcpy(req->buf + length1, audio->buffer_start,
+ length2);
+ audio->buffer_pos = audio->buffer_start + length2;
+ } else {
+ audio->buffer_pos += length1;
+ if (audio->buffer_pos >= audio->buffer_end)
+ audio->buffer_pos = audio->buffer_start;
+ }
+
+ req->length = length;
+ ret = usb_ep_queue(audio->in_ep, req, GFP_ATOMIC);
+ if (ret < 0) {
+ pr_err("usb_ep_queue failed ret: %d\n", ret);
+ audio_req_put(audio, req);
+ break;
+ }
+
+ frames -= bytes_to_frames(runtime, length);
+ audio->frames_sent += bytes_to_frames(runtime, length);
+ }
+}
+
+static void audio_control_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ /* nothing to do here */
+}
+
+static void audio_data_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct audio_dev *audio = req->context;
+
+ pr_debug("audio_data_complete req->status %d req->actual %d\n",
+ req->status, req->actual);
+
+ audio_req_put(audio, req);
+
+ if (!audio->buffer_start || req->status)
+ return;
+
+ audio->period_offset += req->actual;
+ if (audio->period_offset >= audio->period) {
+ snd_pcm_period_elapsed(audio->substream);
+ audio->period_offset = 0;
+ }
+ audio_send(audio);
+}
+
+static int audio_source_set_endpoint_req(struct usb_function *f,
+ const struct usb_ctrlrequest *ctrl)
+{
+ int value = -EOPNOTSUPP;
+ u16 ep = le16_to_cpu(ctrl->wIndex);
+ u16 len = le16_to_cpu(ctrl->wLength);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+
+ pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
+ ctrl->bRequest, w_value, len, ep);
+
+ switch (ctrl->bRequest) {
+ case UAC_SET_CUR:
+ case UAC_SET_MIN:
+ case UAC_SET_MAX:
+ case UAC_SET_RES:
+ value = len;
+ break;
+ default:
+ break;
+ }
+
+ return value;
+}
+
+static int audio_source_get_endpoint_req(struct usb_function *f,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct usb_composite_dev *cdev = f->config->cdev;
+ int value = -EOPNOTSUPP;
+ u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
+ u16 len = le16_to_cpu(ctrl->wLength);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u8 *buf = cdev->req->buf;
+
+ pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
+ ctrl->bRequest, w_value, len, ep);
+
+ if (w_value == UAC_EP_CS_ATTR_SAMPLE_RATE << 8) {
+ switch (ctrl->bRequest) {
+ case UAC_GET_CUR:
+ case UAC_GET_MIN:
+ case UAC_GET_MAX:
+ case UAC_GET_RES:
+ /* return our sample rate */
+ buf[0] = (u8)SAMPLE_RATE;
+ buf[1] = (u8)(SAMPLE_RATE >> 8);
+ buf[2] = (u8)(SAMPLE_RATE >> 16);
+ value = 3;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return value;
+}
+
+static int
+audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+ struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_request *req = cdev->req;
+ int value = -EOPNOTSUPP;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+
+ /* composite driver infrastructure handles everything; interface
+ * activation uses set_alt().
+ */
+ switch (ctrl->bRequestType) {
+ case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
+ value = audio_source_set_endpoint_req(f, ctrl);
+ break;
+
+ case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
+ value = audio_source_get_endpoint_req(f, ctrl);
+ break;
+ }
+
+ /* respond with data transfer or status phase? */
+ if (value >= 0) {
+ pr_debug("audio req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ req->zero = 0;
+ req->length = value;
+ req->complete = audio_control_complete;
+ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+ if (value < 0)
+ pr_err("audio response on err %d\n", value);
+ }
+
+ /* device either stalls (value < 0) or reports success */
+ return value;
+}
+
+static int audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct audio_dev *audio = func_to_audio_source(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ int ret;
+
+ pr_debug("audio_set_alt intf %d, alt %d\n", intf, alt);
+
+ ret = config_ep_by_speed(cdev->gadget, f, audio->in_ep);
+ if (ret) {
+ audio->in_ep->desc = NULL;
+ ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+ audio->in_ep->name, ret);
+ return ret;
+ }
+ ret = usb_ep_enable(audio->in_ep);
+ if (ret) {
+ ERROR(cdev, "failed to enable ep %s, result %d\n",
+ audio->in_ep->name, ret);
+ return ret;
+ }
+ return 0;
+}
+
+static void audio_disable(struct usb_function *f)
+{
+ struct audio_dev *audio = func_to_audio_source(f);
+
+ pr_debug("audio_disable\n");
+ usb_ep_disable(audio->in_ep);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void audio_build_desc(struct audio_dev *audio)
+{
+ u8 *sam_freq;
+ int rate;
+
+ /* Set channel numbers */
+ input_terminal_desc.bNrChannels = 2;
+ as_type_i_desc.bNrChannels = 2;
+
+ /* Set sample rates */
+ rate = SAMPLE_RATE;
+ sam_freq = as_type_i_desc.tSamFreq[0];
+ memcpy(sam_freq, &rate, 3);
+}
+
+/* audio function driver setup/binding */
+static int
+audio_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct audio_dev *audio = func_to_audio_source(f);
+ int status;
+ struct usb_ep *ep;
+ struct usb_request *req;
+ int i;
+
+ audio_build_desc(audio);
+
+ /* allocate instance-specific interface IDs, and patch descriptors */
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ audio_source_ac_interface_desc.bInterfaceNumber = status;
+
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ as_interface_alt_0_desc.bInterfaceNumber = status;
+ as_interface_alt_1_desc.bInterfaceNumber = status;
+
+ status = -ENODEV;
+
+ /* allocate our endpoint */
+ ep = usb_ep_autoconfig(cdev->gadget, &fs_as_in_ep_desc);
+ if (!ep)
+ goto fail;
+ audio->in_ep = ep;
+ ep->driver_data = audio; /* claim */
+
+ if (gadget_is_dualspeed(c->cdev->gadget))
+ hs_as_in_ep_desc.bEndpointAddress =
+ fs_as_in_ep_desc.bEndpointAddress;
+
+ for (i = 0, status = 0; i < IN_EP_REQ_COUNT && status == 0; i++) {
+ req = audio_request_new(ep, IN_EP_MAX_PACKET_SIZE);
+ if (req) {
+ req->context = audio;
+ req->complete = audio_data_complete;
+ audio_req_put(audio, req);
+ } else
+ status = -ENOMEM;
+ }
+
+fail:
+ return status;
+}
+
+static void
+audio_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct audio_dev *audio = func_to_audio_source(f);
+ struct usb_request *req;
+
+ while ((req = audio_req_get(audio)))
+ audio_request_free(req, audio->in_ep);
+
+ snd_card_free_when_closed(audio->card);
+ audio->card = NULL;
+ audio->pcm = NULL;
+ audio->substream = NULL;
+ audio->in_ep = NULL;
+}
+
+static void audio_pcm_playback_start(struct audio_dev *audio)
+{
+ audio->start_time = ktime_get();
+ audio->frames_sent = 0;
+ audio_send(audio);
+}
+
+static void audio_pcm_playback_stop(struct audio_dev *audio)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->lock, flags);
+ audio->buffer_start = 0;
+ audio->buffer_end = 0;
+ audio->buffer_pos = 0;
+ spin_unlock_irqrestore(&audio->lock, flags);
+}
+
+static int audio_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_dev *audio = substream->private_data;
+
+ runtime->private_data = audio;
+ runtime->hw = audio_hw_info;
+ snd_pcm_limit_hw_rates(runtime);
+ runtime->hw.channels_max = 2;
+
+ audio->substream = substream;
+ return 0;
+}
+
+static int audio_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct audio_dev *audio = substream->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->lock, flags);
+ audio->substream = NULL;
+ spin_unlock_irqrestore(&audio->lock, flags);
+
+ return 0;
+}
+
+static int audio_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ unsigned int channels = params_channels(params);
+ unsigned int rate = params_rate(params);
+
+ if (rate != SAMPLE_RATE)
+ return -EINVAL;
+ if (channels != 2)
+ return -EINVAL;
+
+ return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(params));
+}
+
+static int audio_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int audio_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_dev *audio = runtime->private_data;
+
+ audio->period = snd_pcm_lib_period_bytes(substream);
+ audio->period_offset = 0;
+ audio->buffer_start = runtime->dma_area;
+ audio->buffer_end = audio->buffer_start
+ + snd_pcm_lib_buffer_bytes(substream);
+ audio->buffer_pos = audio->buffer_start;
+
+ return 0;
+}
+
+static snd_pcm_uframes_t audio_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_dev *audio = runtime->private_data;
+ ssize_t bytes = audio->buffer_pos - audio->buffer_start;
+
+ /* return offset of next frame to fill in our buffer */
+ return bytes_to_frames(runtime, bytes);
+}
+
+static int audio_pcm_playback_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct audio_dev *audio = substream->runtime->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ audio_pcm_playback_start(audio);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ audio_pcm_playback_stop(audio);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static struct audio_dev _audio_dev = {
+ .func = {
+ .name = "audio_source",
+ .bind = audio_bind,
+ .unbind = audio_unbind,
+ .set_alt = audio_set_alt,
+ .setup = audio_setup,
+ .disable = audio_disable,
+ .descriptors = fs_audio_desc,
+ .hs_descriptors = hs_audio_desc,
+ },
+ .lock = __SPIN_LOCK_UNLOCKED(_audio_dev.lock),
+ .idle_reqs = LIST_HEAD_INIT(_audio_dev.idle_reqs),
+};
+
+static struct snd_pcm_ops audio_playback_ops = {
+ .open = audio_pcm_open,
+ .close = audio_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = audio_pcm_hw_params,
+ .hw_free = audio_pcm_hw_free,
+ .prepare = audio_pcm_prepare,
+ .trigger = audio_pcm_playback_trigger,
+ .pointer = audio_pcm_pointer,
+};
+
+int audio_source_bind_config(struct usb_configuration *c,
+ struct audio_source_config *config)
+{
+ struct audio_dev *audio;
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ int err;
+
+ config->card = -1;
+ config->device = -1;
+
+ audio = &_audio_dev;
+
+ err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, 0, &card);
+ if (err)
+ return err;
+
+ snd_card_set_dev(card, &c->cdev->gadget->dev);
+
+ err = snd_pcm_new(card, "USB audio source", 0, 1, 0, &pcm);
+ if (err)
+ goto pcm_fail;
+ pcm->private_data = audio;
+ pcm->info_flags = 0;
+ audio->pcm = pcm;
+
+ strlcpy(pcm->name, "USB gadget audio", sizeof(pcm->name));
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &audio_playback_ops);
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ NULL, 0, 64 * 1024);
+
+ strlcpy(card->driver, "audio_source", sizeof(card->driver));
+ strlcpy(card->shortname, card->driver, sizeof(card->shortname));
+ strlcpy(card->longname, "USB accessory audio source",
+ sizeof(card->longname));
+
+ err = snd_card_register(card);
+ if (err)
+ goto register_fail;
+
+ err = usb_add_function(c, &audio->func);
+ if (err)
+ goto add_fail;
+
+ config->card = pcm->card->number;
+ config->device = pcm->device;
+ audio->card = card;
+ return 0;
+
+add_fail:
+register_fail:
+pcm_fail:
+ snd_card_free(audio->card);
+ return err;
+}
diff --git a/drivers/usb/gadget/f_mbim.c b/drivers/usb/gadget/f_mbim.c
index 7a84ca3..681435a 100644
--- a/drivers/usb/gadget/f_mbim.c
+++ b/drivers/usb/gadget/f_mbim.c
@@ -19,7 +19,6 @@
#include <linux/usb/cdc.h>
#include <linux/usb/composite.h>
-#include <linux/usb/android_composite.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
@@ -41,6 +40,9 @@
#define NR_MBIM_PORTS 1
+/* ID for Microsoft OS String */
+#define MBIM_OS_STRING_ID 0xEE
+
struct ctrl_pkt {
void *buf;
int len;
@@ -356,6 +358,63 @@
NULL,
};
+/* Microsoft OS Descriptors */
+
+/*
+ * We specify our own bMS_VendorCode byte which Windows will use
+ * as the bRequest value in subsequent device get requests.
+ */
+#define MBIM_VENDOR_CODE 0xA5
+
+/* Microsoft OS String */
+static u8 mbim_os_string[] = {
+ 18, /* sizeof(mtp_os_string) */
+ USB_DT_STRING,
+ /* Signature field: "MSFT100" */
+ 'M', 0, 'S', 0, 'F', 0, 'T', 0, '1', 0, '0', 0, '0', 0,
+ /* vendor code */
+ MBIM_VENDOR_CODE,
+ /* padding */
+ 0
+};
+
+/* Microsoft Extended Configuration Descriptor Header Section */
+struct mbim_ext_config_desc_header {
+ __le32 dwLength;
+ __u16 bcdVersion;
+ __le16 wIndex;
+ __u8 bCount;
+ __u8 reserved[7];
+};
+
+/* Microsoft Extended Configuration Descriptor Function Section */
+struct mbim_ext_config_desc_function {
+ __u8 bFirstInterfaceNumber;
+ __u8 bInterfaceCount;
+ __u8 compatibleID[8];
+ __u8 subCompatibleID[8];
+ __u8 reserved[6];
+};
+
+/* Microsoft Extended Configuration Descriptor */
+static struct {
+ struct mbim_ext_config_desc_header header;
+ struct mbim_ext_config_desc_function function;
+} mbim_ext_config_desc = {
+ .header = {
+ .dwLength = __constant_cpu_to_le32(sizeof mbim_ext_config_desc),
+ .bcdVersion = __constant_cpu_to_le16(0x0100),
+ .wIndex = __constant_cpu_to_le16(4),
+ .bCount = 1,
+ },
+ .function = {
+ .bFirstInterfaceNumber = 0,
+ .bInterfaceCount = 1,
+ .compatibleID = { 'A', 'L', 'T', 'R', 'C', 'F', 'G' },
+ /* .subCompatibleID = DYNAMIC */
+ },
+};
+
/*
* Here are options for the Datagram Pointer table (NDP) parser.
* There are 2 different formats: NDP16 and NDP32 in the spec (ch. 3),
@@ -1108,6 +1167,63 @@
return value;
}
+/*
+ * This function handles the Microsoft-specific OS descriptor control
+ * requests that are issued by Windows host drivers to determine the
+ * configuration containing the MBIM function.
+ *
+ * Unlike mbim_setup() this function handles two specific device requests,
+ * and only when a configuration has not yet been selected.
+ */
+static int mbim_ctrlrequest(struct usb_composite_dev *cdev,
+ const struct usb_ctrlrequest *ctrl)
+{
+ int value = -EOPNOTSUPP;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+
+ /* only respond to OS desciptors when no configuration selected */
+ if (cdev->config || !mbim_ext_config_desc.function.subCompatibleID[0])
+ return value;
+
+ pr_debug("%02x.%02x v%04x i%04x l%u",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+
+ /* Handle MSFT OS string */
+ if (ctrl->bRequestType ==
+ (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE)
+ && ctrl->bRequest == USB_REQ_GET_DESCRIPTOR
+ && (w_value >> 8) == USB_DT_STRING
+ && (w_value & 0xFF) == MBIM_OS_STRING_ID) {
+
+ value = (w_length < sizeof(mbim_os_string) ?
+ w_length : sizeof(mbim_os_string));
+ memcpy(cdev->req->buf, mbim_os_string, value);
+
+ } else if (ctrl->bRequestType ==
+ (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE)
+ && ctrl->bRequest == MBIM_VENDOR_CODE && w_index == 4) {
+
+ /* Handle Extended OS descriptor */
+ value = (w_length < sizeof(mbim_ext_config_desc) ?
+ w_length : sizeof(mbim_ext_config_desc));
+ memcpy(cdev->req->buf, &mbim_ext_config_desc, value);
+ }
+
+ /* respond with data transfer or status phase? */
+ if (value >= 0) {
+ int rc;
+ cdev->req->zero = value < w_length;
+ cdev->req->length = value;
+ rc = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC);
+ if (rc < 0)
+ pr_err("response queue error: %d", rc);
+ }
+ return value;
+}
+
static int mbim_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_mbim *mbim = func_to_mbim(f);
@@ -1371,6 +1487,17 @@
goto fail;
}
+ /*
+ * If MBIM is bound in a config other than the first, tell Windows
+ * about it by returning the num as a string in the OS descriptor's
+ * subCompatibleID field. Windows only supports up to config #4.
+ */
+ if (c->bConfigurationValue >= 2 && c->bConfigurationValue <= 4) {
+ pr_debug("MBIM in configuration %d", c->bConfigurationValue);
+ mbim_ext_config_desc.function.subCompatibleID[0] =
+ c->bConfigurationValue + '0';
+ }
+
pr_info("mbim(%d): %s speed IN/%s OUT/%s NOTIFY/%s\n",
mbim->port_num,
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
@@ -1412,6 +1539,8 @@
kfree(mbim->not_port.notify_req->buf);
usb_ep_free_request(mbim->not_port.notify, mbim->not_port.notify_req);
+
+ mbim_ext_config_desc.function.subCompatibleID[0] = 0;
}
/**
diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c
index 96790c5..ccbc330 100644
--- a/drivers/usb/gadget/f_mtp.c
+++ b/drivers/usb/gadget/f_mtp.c
@@ -463,6 +463,10 @@
if (count > MTP_BULK_BUFFER_SIZE)
return -EINVAL;
+ if (!IS_ALIGNED(count, dev->ep_out->maxpacket))
+ DBG(cdev, "%s - count(%d) not multiple of mtu(%d)\n", __func__,
+ count, dev->ep_out->maxpacket);
+
/* we will block until we're online */
DBG(cdev, "mtp_read: waiting for online state\n");
ret = wait_event_interruptible(dev->read_wq,
@@ -484,7 +488,7 @@
requeue_req:
/* queue a request */
req = dev->rx_req[0];
- req->length = count;
+ req->length = MTP_BULK_BUFFER_SIZE;
dev->rx_done = 0;
ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL);
if (ret < 0) {
@@ -751,6 +755,9 @@
count = dev->xfer_file_length;
DBG(cdev, "receive_file_work(%lld)\n", count);
+ if (!IS_ALIGNED(count, dev->ep_out->maxpacket))
+ DBG(cdev, "%s- count(%lld) not multiple of mtu(%d)\n", __func__,
+ count, dev->ep_out->maxpacket);
while (count > 0 || write_req) {
if (count > 0) {
@@ -758,8 +765,9 @@
read_req = dev->rx_req[cur_buf];
cur_buf = (cur_buf + 1) % RX_REQ_MAX;
- read_req->length = (count > MTP_BULK_BUFFER_SIZE
- ? MTP_BULK_BUFFER_SIZE : count);
+ /* some h/w expects size to be aligned to ep's MTU */
+ read_req->length = MTP_BULK_BUFFER_SIZE;
+
dev->rx_done = 0;
ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL);
if (ret < 0) {
@@ -795,6 +803,10 @@
usb_ep_dequeue(dev->ep_out, read_req);
break;
}
+ /* Check if we aligned the size due to MTU constraint */
+ if (count < read_req->length)
+ read_req->actual = (read_req->actual > count ?
+ count : read_req->actual);
/* if xfer_file_length is 0xFFFFFFFF, then we read until
* we get a zero length packet
*/
diff --git a/drivers/usb/gadget/f_qc_ecm.c b/drivers/usb/gadget/f_qc_ecm.c
index 1c64955..0b41197 100644
--- a/drivers/usb/gadget/f_qc_ecm.c
+++ b/drivers/usb/gadget/f_qc_ecm.c
@@ -518,7 +518,7 @@
if (ecm->port.in_ep->driver_data) {
DBG(cdev, "reset ecm\n");
- gether_qc_disconnect(&ecm->port);
+ gether_qc_disconnect_name(&ecm->port, "ecm0");
ecm_qc_bam_disconnect(ecm);
}
@@ -548,7 +548,7 @@
);
ecm->port.cdc_filter = DEFAULT_FILTER;
DBG(cdev, "activate ecm\n");
- net = gether_qc_connect(&ecm->port);
+ net = gether_qc_connect_name(&ecm->port, "ecm0");
if (IS_ERR(net))
return PTR_ERR(net);
@@ -591,7 +591,7 @@
DBG(cdev, "ecm deactivated\n");
if (ecm->port.in_ep->driver_data) {
- gether_qc_disconnect(&ecm->port);
+ gether_qc_disconnect_name(&ecm->port, "ecm0");
ecm_qc_bam_disconnect(ecm);
}
diff --git a/drivers/usb/gadget/f_qc_rndis.c b/drivers/usb/gadget/f_qc_rndis.c
index 7a181eb..f86bf12 100644
--- a/drivers/usb/gadget/f_qc_rndis.c
+++ b/drivers/usb/gadget/f_qc_rndis.c
@@ -661,7 +661,7 @@
if (rndis->port.in_ep->driver_data) {
DBG(cdev, "reset rndis\n");
- gether_qc_disconnect(&rndis->port);
+ gether_qc_disconnect_name(&rndis->port, "rndis0");
rndis_qc_bam_disconnect(rndis);
}
@@ -695,7 +695,7 @@
rndis->port.cdc_filter = 0;
DBG(cdev, "RNDIS RX/TX early activation ...\n");
- net = gether_qc_connect(&rndis->port);
+ net = gether_qc_connect_name(&rndis->port, "rndis0");
if (IS_ERR(net))
return PTR_ERR(net);
@@ -722,7 +722,7 @@
pr_info("rndis deactivated\n");
rndis_uninit(rndis->config);
- gether_qc_disconnect(&rndis->port);
+ gether_qc_disconnect_name(&rndis->port, "rndis0");
rndis_qc_bam_disconnect(rndis);
usb_ep_disable(rndis->notify);
diff --git a/drivers/usb/gadget/f_qdss.c b/drivers/usb/gadget/f_qdss.c
index 0c81904..4f098f1 100644
--- a/drivers/usb/gadget/f_qdss.c
+++ b/drivers/usb/gadget/f_qdss.c
@@ -615,6 +615,7 @@
qdss->function.unbind = qdss_unbind;
qdss->function.set_alt = qdss_set_alt;
qdss->function.disable = qdss_disable;
+ spin_lock_init(&qdss->lock);
INIT_LIST_HEAD(&qdss->ctrl_read_pool);
INIT_LIST_HEAD(&qdss->ctrl_write_pool);
INIT_WORK(&qdss->qdss_work, usb_qdss_work_func);
diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c
index 3d6ceaa..649fe14 100644
--- a/drivers/usb/gadget/f_serial.c
+++ b/drivers/usb/gadget/f_serial.c
@@ -225,26 +225,26 @@
NULL,
};
-static struct usb_endpoint_descriptor gser_ss_in_desc __initdata = {
+static struct usb_endpoint_descriptor gser_ss_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
-static struct usb_endpoint_descriptor gser_ss_out_desc __initdata = {
+static struct usb_endpoint_descriptor gser_ss_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
-static struct usb_ss_ep_comp_descriptor gser_ss_bulk_comp_desc __initdata = {
+static struct usb_ss_ep_comp_descriptor gser_ss_bulk_comp_desc = {
.bLength = sizeof gser_ss_bulk_comp_desc,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
};
-static struct usb_descriptor_header *gser_ss_function[] __initdata = {
+static struct usb_descriptor_header *gser_ss_function[] = {
(struct usb_descriptor_header *) &gser_interface_desc,
(struct usb_descriptor_header *) &gser_ss_in_desc,
(struct usb_descriptor_header *) &gser_ss_bulk_comp_desc,
diff --git a/drivers/usb/gadget/f_uac1.c b/drivers/usb/gadget/f_uac1.c
index b385592..8c74381 100644
--- a/drivers/usb/gadget/f_uac1.c
+++ b/drivers/usb/gadget/f_uac1.c
@@ -62,8 +62,6 @@
static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value);
static int generic_get_cmd(struct usb_audio_control *con, u8 cmd);
-DECLARE_UAC_AC_HEADER_DESCRIPTOR(2);
-DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1);
#define SPEAKER_INPUT_TERMINAL_ID 3
#define SPEAKER_OUTPUT_TERMINAL_ID 4
diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c
index 3f4e428..b408bfd 100644
--- a/drivers/usb/gadget/msm72k_udc.c
+++ b/drivers/usb/gadget/msm72k_udc.c
@@ -516,6 +516,15 @@
{
struct usb_info *ui = ept->ui;
unsigned cfg = CONFIG_MAX_PKT(ept->ep.maxpacket) | CONFIG_ZLT;
+ const struct usb_endpoint_descriptor *desc = ept->ep.desc;
+ unsigned mult = 0;
+
+ if (desc && ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ == USB_ENDPOINT_XFER_ISOC)) {
+ cfg &= ~(CONFIG_MULT);
+ mult = ((ept->ep.maxpacket >> CONFIG_MULT_SHIFT) + 1) & 0x03;
+ cfg |= (mult << (ffs(CONFIG_MULT) - 1));
+ }
/* ep0 out needs interrupt-on-setup */
if (ept->bit == 0)
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c
index 3c57df4..7e62c19 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/storage_common.c
@@ -925,7 +925,7 @@
int rc = 0;
-#ifndef CONFIG_USB_ANDROID_MASS_STORAGE
+#if !defined(CONFIG_USB_G_ANDROID)
/* disabled in android because we need to allow closing the backing file
* if the media was removed
*/
diff --git a/drivers/usb/gadget/u_bam.c b/drivers/usb/gadget/u_bam.c
index 45dfb87..f092329 100644
--- a/drivers/usb/gadget/u_bam.c
+++ b/drivers/usb/gadget/u_bam.c
@@ -50,7 +50,7 @@
#define BAM_MUX_RX_Q_SIZE 16
#define BAM_MUX_TX_Q_SIZE 200
-#define BAM_MUX_RX_REQ_SIZE (2048 - BAM_MUX_HDR)
+#define BAM_MUX_RX_REQ_SIZE 2048 /* Must be 1KB aligned */
#define DL_INTR_THRESHOLD 20
diff --git a/drivers/usb/gadget/u_qc_ether.c b/drivers/usb/gadget/u_qc_ether.c
index 20933b6..4931c1e 100644
--- a/drivers/usb/gadget/u_qc_ether.c
+++ b/drivers/usb/gadget/u_qc_ether.c
@@ -222,8 +222,6 @@
return 1;
}
-static struct eth_qc_dev *qc_dev;
-
static const struct net_device_ops eth_qc_netdev_ops = {
.ndo_open = eth_qc_open,
.ndo_stop = eth_qc_stop,
@@ -276,9 +274,6 @@
struct net_device *net;
int status;
- if (qc_dev)
- return -EBUSY;
-
net = alloc_etherdev(sizeof *dev);
if (!net)
return -ENOMEM;
@@ -318,32 +313,33 @@
INFO(dev, "MAC %pM\n", net->dev_addr);
INFO(dev, "HOST MAC %pM\n", dev->host_mac);
- qc_dev = dev;
}
return status;
}
/**
- * gether_qc_cleanup - remove Ethernet-over-USB device
+ * gether_qc_cleanup_name - remove Ethernet-over-USB device
* Context: may sleep
*
* This is called to free all resources allocated by @gether_qc_setup().
*/
-void gether_qc_cleanup(void)
+void gether_qc_cleanup_name(const char *netname)
{
- if (!qc_dev)
- return;
+ struct net_device *net_dev;
- unregister_netdev(qc_dev->net);
- free_netdev(qc_dev->net);
+ /* Extract the eth_qc_dev from the net device */
+ net_dev = dev_get_by_name(&init_net, netname);
- qc_dev = NULL;
+ if (net_dev) {
+ unregister_netdev(net_dev);
+ free_netdev(net_dev);
+ }
}
-
/**
- * gether_qc_connect - notify network layer that USB link is active
+ * gether_qc_connect_name - notify network layer that USB link
+ * is active
* @link: the USB link, set up with endpoints, descriptors matching
* current device speed, and any framing wrapper(s) set up.
* Context: irqs blocked
@@ -351,9 +347,15 @@
* This is called to let the network layer know the connection
* is active ("carrier detect").
*/
-struct net_device *gether_qc_connect(struct qc_gether *link)
+struct net_device *gether_qc_connect_name(struct qc_gether *link,
+ const char *netname)
{
- struct eth_qc_dev *dev = qc_dev;
+ struct net_device *net_dev;
+ struct eth_qc_dev *dev;
+
+ /* Extract the eth_qc_dev from the net device */
+ net_dev = dev_get_by_name(&init_net, netname);
+ dev = netdev_priv(net_dev);
if (!dev)
return ERR_PTR(-EINVAL);
@@ -381,7 +383,8 @@
}
/**
- * gether_qc_disconnect - notify network layer that USB link is inactive
+ * gether_qc_disconnect_name - notify network layer that USB
+ * link is inactive
* @link: the USB link, on which gether_connect() was called
* Context: irqs blocked
*
@@ -390,9 +393,14 @@
*
* On return, the state is as if gether_connect() had never been called.
*/
-void gether_qc_disconnect(struct qc_gether *link)
+void gether_qc_disconnect_name(struct qc_gether *link, const char *netname)
{
- struct eth_qc_dev *dev = link->ioport;
+ struct net_device *net_dev;
+ struct eth_qc_dev *dev;
+
+ /* Extract the eth_qc_dev from the net device */
+ net_dev = dev_get_by_name(&init_net, netname);
+ dev = netdev_priv(net_dev);
if (!dev)
return;
diff --git a/drivers/usb/gadget/u_qc_ether.h b/drivers/usb/gadget/u_qc_ether.h
index b3c281b..d91e805 100644
--- a/drivers/usb/gadget/u_qc_ether.h
+++ b/drivers/usb/gadget/u_qc_ether.h
@@ -78,14 +78,15 @@
/* netdev setup/teardown as directed by the gadget driver */
int gether_qc_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]);
-void gether_qc_cleanup(void);
+void gether_qc_cleanup_name(const char *netname);
/* variant of gether_setup that allows customizing network device name */
int gether_qc_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
const char *netname);
/* connect/disconnect is handled by individual functions */
-struct net_device *gether_qc_connect(struct qc_gether *);
-void gether_qc_disconnect(struct qc_gether *);
+struct net_device *gether_qc_connect_name(struct qc_gether *link,
+ const char *netname);
+void gether_qc_disconnect_name(struct qc_gether *link, const char *netname);
/* each configuration may bind one instance of an ethernet link */
int ecm_qc_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
diff --git a/drivers/usb/host/ehci-msm-hsic.c b/drivers/usb/host/ehci-msm-hsic.c
index 6fe9e58..cd02489 100644
--- a/drivers/usb/host/ehci-msm-hsic.c
+++ b/drivers/usb/host/ehci-msm-hsic.c
@@ -1144,6 +1144,14 @@
#endif /* CONFIG_PM */
+static void ehci_msm_set_autosuspend_delay(struct usb_device *dev)
+{
+ if (!dev->parent) /*for root hub no delay*/
+ pm_runtime_set_autosuspend_delay(&dev->dev, 0);
+ else
+ pm_runtime_set_autosuspend_delay(&dev->dev, 200);
+}
+
static struct hc_driver msm_hsic_driver = {
.description = hcd_name,
.product_desc = "Qualcomm EHCI Host Controller using HSIC",
@@ -1194,6 +1202,8 @@
.enable_ulpi_control = ehci_msm_enable_ulpi_control,
.disable_ulpi_control = ehci_msm_disable_ulpi_control,
+
+ .set_autosuspend_delay = ehci_msm_set_autosuspend_delay,
};
static int msm_hsic_init_clocks(struct msm_hsic_hcd *mehci, u32 init)
diff --git a/drivers/usb/misc/diag_bridge.c b/drivers/usb/misc/diag_bridge.c
index b1b7763..2d95945 100644
--- a/drivers/usb/misc/diag_bridge.c
+++ b/drivers/usb/misc/diag_bridge.c
@@ -31,6 +31,8 @@
#define DRIVER_DESC "USB host diag bridge driver"
#define DRIVER_VERSION "1.0"
+#define AUTOSUSP_DELAY_WITH_USB 1000
+
struct diag_bridge {
struct usb_device *udev;
struct usb_interface *ifc;
@@ -42,6 +44,7 @@
struct mutex ifc_mutex;
struct diag_bridge_ops *ops;
struct platform_device *pdev;
+ unsigned default_autosusp_delay;
/* debugging counters */
unsigned long bytes_to_host;
@@ -68,6 +71,12 @@
dev->ops = ops;
dev->err = 0;
+#ifdef CONFIG_PM_RUNTIME
+ dev->default_autosusp_delay = dev->udev->dev.power.autosuspend_delay;
+#endif
+ pm_runtime_set_autosuspend_delay(&dev->udev->dev,
+ AUTOSUSP_DELAY_WITH_USB);
+
kref_get(&dev->kref);
return 0;
@@ -101,6 +110,10 @@
usb_kill_anchored_urbs(&dev->submitted);
dev->ops = 0;
+
+ pm_runtime_set_autosuspend_delay(&dev->udev->dev,
+ dev->default_autosusp_delay);
+
kref_put(&dev->kref, diag_bridge_delete);
}
EXPORT_SYMBOL(diag_bridge_close);
diff --git a/drivers/usb/misc/ks_bridge.c b/drivers/usb/misc/ks_bridge.c
index 656e379..410b5c4 100644
--- a/drivers/usb/misc/ks_bridge.c
+++ b/drivers/usb/misc/ks_bridge.c
@@ -48,6 +48,7 @@
#define BOOT_BRIDGE_INDEX 0
#define EFS_BRIDGE_INDEX 1
#define MAX_DATA_PKT_SIZE 16384
+#define PENDING_URB_TIMEOUT 10
struct ks_bridge {
char *name;
@@ -58,7 +59,10 @@
struct list_head to_mdm_list;
struct list_head to_ks_list;
wait_queue_head_t ks_wait_q;
+ wait_queue_head_t pending_urb_wait;
struct miscdevice *fs_dev;
+ atomic_t tx_pending_cnt;
+ atomic_t rx_pending_cnt;
/* usb specific */
struct usb_device *udev;
@@ -207,13 +211,16 @@
dbg_log_event(ksb, "C TX_URB", urb->status, 0);
pr_debug("status:%d", urb->status);
- if (ksb->ifc)
+ if (test_bit(USB_DEV_CONNECTED, &ksb->flags))
usb_autopm_put_interface_async(ksb->ifc);
if (urb->status < 0)
pr_err_ratelimited("urb failed with err:%d", urb->status);
ksb_free_data_pkt(pkt);
+
+ atomic_dec(&ksb->tx_pending_cnt);
+ wake_up(&ksb->pending_urb_wait);
}
static void ksb_tomdm_work(struct work_struct *w)
@@ -252,6 +259,7 @@
dbg_log_event(ksb, "S TX_URB", pkt->len, 0);
+ atomic_inc(&ksb->tx_pending_cnt);
ret = usb_submit_urb(urb, GFP_KERNEL);
if (ret) {
pr_err("out urb submission failed");
@@ -259,6 +267,8 @@
usb_free_urb(urb);
ksb_free_data_pkt(pkt);
usb_autopm_put_interface(ksb->ifc);
+ atomic_dec(&ksb->tx_pending_cnt);
+ wake_up(&ksb->pending_urb_wait);
return;
}
@@ -277,6 +287,9 @@
unsigned long flags;
struct ks_bridge *ksb = fp->private_data;
+ if (!test_bit(USB_DEV_CONNECTED, &ksb->flags))
+ return -ENODEV;
+
pkt = ksb_alloc_data_pkt(count, GFP_KERNEL, ksb);
if (IS_ERR(pkt)) {
pr_err("unable to allocate data packet");
@@ -420,8 +433,15 @@
ksb_rx_cb, pkt);
usb_anchor_urb(urb, &ksb->submitted);
- dbg_log_event(ksb, "S RX_URB", pkt->len, 0);
+ if (!test_bit(USB_DEV_CONNECTED, &ksb->flags)) {
+ usb_unanchor_urb(urb);
+ usb_free_urb(urb);
+ ksb_free_data_pkt(pkt);
+ ksb->alloced_read_pkts--;
+ return;
+ }
+ atomic_inc(&ksb->rx_pending_cnt);
ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret) {
pr_err("in urb submission failed");
@@ -429,9 +449,13 @@
usb_free_urb(urb);
ksb_free_data_pkt(pkt);
ksb->alloced_read_pkts--;
+ atomic_dec(&ksb->rx_pending_cnt);
+ wake_up(&ksb->pending_urb_wait);
return;
}
+ dbg_log_event(ksb, "S RX_URB", pkt->len, 0);
+
usb_free_urb(urb);
}
static void ksb_rx_cb(struct urb *urb)
@@ -454,7 +478,7 @@
urb->status);
ksb_free_data_pkt(pkt);
ksb->alloced_read_pkts--;
- return;
+ goto done;
}
if (urb->actual_length == 0) {
@@ -474,7 +498,9 @@
resubmit_urb:
submit_one_urb(ksb);
-
+done:
+ atomic_dec(&ksb->rx_pending_cnt);
+ wake_up(&ksb->pending_urb_wait);
}
static void ksb_start_rx_work(struct work_struct *w)
@@ -487,6 +513,10 @@
int ret;
for (i = 0; i < NO_RX_REQS; i++) {
+
+ if (!test_bit(USB_DEV_CONNECTED, &ksb->flags))
+ return;
+
pkt = ksb_alloc_data_pkt(MAX_DATA_PKT_SIZE, GFP_KERNEL, ksb);
if (IS_ERR(pkt)) {
pr_err("unable to allocate data pkt");
@@ -516,6 +546,7 @@
dbg_log_event(ksb, "S RX_URB", pkt->len, 0);
+ atomic_inc(&ksb->rx_pending_cnt);
ret = usb_submit_urb(urb, GFP_KERNEL);
if (ret) {
pr_err("in urb submission failed");
@@ -524,6 +555,8 @@
ksb_free_data_pkt(pkt);
ksb->alloced_read_pkts--;
usb_autopm_put_interface(ksb->ifc);
+ atomic_dec(&ksb->rx_pending_cnt);
+ wake_up(&ksb->pending_urb_wait);
return;
}
@@ -540,6 +573,8 @@
struct usb_endpoint_descriptor *ep_desc;
int i;
struct ks_bridge *ksb;
+ unsigned long flags;
+ struct data_pkt *pkt;
ifc_num = ifc->cur_altsetting->desc.bInterfaceNumber;
@@ -590,9 +625,28 @@
usb_set_intfdata(ifc, ksb);
set_bit(USB_DEV_CONNECTED, &ksb->flags);
+ atomic_set(&ksb->tx_pending_cnt, 0);
+ atomic_set(&ksb->rx_pending_cnt, 0);
dbg_log_event(ksb, "PID-ATT", id->idProduct, 0);
+ /*free up stale buffers if any from previous disconnect*/
+ spin_lock_irqsave(&ksb->lock, flags);
+ while (!list_empty(&ksb->to_ks_list)) {
+ pkt = list_first_entry(&ksb->to_ks_list,
+ struct data_pkt, list);
+ list_del_init(&pkt->list);
+ ksb_free_data_pkt(pkt);
+ ksb->alloced_read_pkts--;
+ }
+ while (!list_empty(&ksb->to_mdm_list)) {
+ pkt = list_first_entry(&ksb->to_mdm_list,
+ struct data_pkt, list);
+ list_del_init(&pkt->list);
+ ksb_free_data_pkt(pkt);
+ }
+ spin_unlock_irqrestore(&ksb->lock, flags);
+
ksb->fs_dev = (struct miscdevice *)id->driver_info;
misc_register(ksb->fs_dev);
@@ -640,15 +694,25 @@
clear_bit(USB_DEV_CONNECTED, &ksb->flags);
wake_up(&ksb->ks_wait_q);
cancel_work_sync(&ksb->to_mdm_work);
+ cancel_work_sync(&ksb->start_rx_work);
+
+ misc_deregister(ksb->fs_dev);
usb_kill_anchored_urbs(&ksb->submitted);
+ wait_event_interruptible_timeout(
+ ksb->pending_urb_wait,
+ !atomic_read(&ksb->tx_pending_cnt) &&
+ !atomic_read(&ksb->rx_pending_cnt),
+ msecs_to_jiffies(PENDING_URB_TIMEOUT));
+
spin_lock_irqsave(&ksb->lock, flags);
while (!list_empty(&ksb->to_ks_list)) {
pkt = list_first_entry(&ksb->to_ks_list,
struct data_pkt, list);
list_del_init(&pkt->list);
ksb_free_data_pkt(pkt);
+ ksb->alloced_read_pkts--;
}
while (!list_empty(&ksb->to_mdm_list)) {
pkt = list_first_entry(&ksb->to_mdm_list,
@@ -658,7 +722,6 @@
}
spin_unlock_irqrestore(&ksb->lock, flags);
- misc_deregister(ksb->fs_dev);
ifc->needs_remote_wakeup = 0;
usb_put_dev(ksb->udev);
ksb->ifc = NULL;
@@ -741,6 +804,7 @@
INIT_LIST_HEAD(&ksb->to_mdm_list);
INIT_LIST_HEAD(&ksb->to_ks_list);
init_waitqueue_head(&ksb->ks_wait_q);
+ init_waitqueue_head(&ksb->pending_urb_wait);
ksb->wq = create_singlethread_workqueue(ksb->name);
if (!ksb->wq) {
pr_err("unable to allocate workqueue");
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 813fc94..18f8729 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -39,9 +39,9 @@
#include <linux/regulator/consumer.h>
#include <linux/mfd/pm8xxx/pm8921-charger.h>
#include <linux/mfd/pm8xxx/misc.h>
-#include <linux/power_supply.h>
#include <linux/mhl_8334.h>
+#include <mach/scm.h>
#include <mach/clk.h>
#include <mach/mpm.h>
#include <mach/msm_xo.h>
@@ -96,6 +96,7 @@
static struct power_supply *psy;
static bool aca_id_turned_on;
+static bool legacy_power_supply;
static inline bool aca_enabled(void)
{
#ifdef CONFIG_USB_MSM_ACA
@@ -926,10 +927,15 @@
if (motg->caps & ALLOW_PHY_RETENTION && !host_bus_suspend &&
!device_bus_suspend && !dcp) {
phy_ctrl_val = readl_relaxed(USB_PHY_CTRL);
- if (motg->pdata->otg_control == OTG_PHY_CONTROL)
+ if (motg->pdata->otg_control == OTG_PHY_CONTROL) {
/* Enable PHY HV interrupts to wake MPM/Link */
- phy_ctrl_val |=
- (PHY_IDHV_INTEN | PHY_OTGSESSVLDHV_INTEN);
+ if ((motg->pdata->mode == USB_OTG) ||
+ (motg->pdata->mode == USB_HOST))
+ phy_ctrl_val |= (PHY_IDHV_INTEN |
+ PHY_OTGSESSVLDHV_INTEN);
+ else
+ phy_ctrl_val |= PHY_OTGSESSVLDHV_INTEN;
+ }
writel_relaxed(phy_ctrl_val & ~PHY_RETEN, USB_PHY_CTRL);
motg->lpm_flags |= PHY_RETENTIONED;
@@ -1009,6 +1015,7 @@
if (!atomic_read(&motg->in_lpm))
return 0;
+ disable_irq(motg->irq);
wake_lock(&motg->wlock);
/* Vote for TCXO when waking up the phy */
@@ -1099,6 +1106,7 @@
enable_irq(motg->async_int);
motg->async_int = 0;
}
+ enable_irq(motg->irq);
/* If ASYNC IRQ is present then keep it enabled only during LPM */
if (motg->async_irq)
@@ -1110,24 +1118,32 @@
}
#endif
-static int msm_otg_notify_host_mode(struct msm_otg *motg, bool host_mode)
+static void msm_otg_notify_host_mode(struct msm_otg *motg, bool host_mode)
{
- if (!psy)
- goto psy_not_supported;
+ struct power_supply *usb = psy ? psy : &motg->usb_psy;
- if (host_mode)
- power_supply_set_scope(psy, POWER_SUPPLY_SCOPE_SYSTEM);
- else
- power_supply_set_scope(psy, POWER_SUPPLY_SCOPE_DEVICE);
+ if (!usb) {
+ pr_err("No USB power supply registered!\n");
+ return;
+ }
-psy_not_supported:
- dev_dbg(motg->phy.dev, "Power Supply doesn't support USB charger\n");
- return -ENXIO;
+ if (psy) {
+ /* legacy support */
+ if (host_mode)
+ power_supply_set_scope(psy, POWER_SUPPLY_SCOPE_SYSTEM);
+ else
+ power_supply_set_scope(psy, POWER_SUPPLY_SCOPE_DEVICE);
+ return;
+ } else {
+ motg->host_mode = host_mode;
+ power_supply_changed(usb);
+ }
}
static int msm_otg_notify_chg_type(struct msm_otg *motg)
{
static int charger_type;
+ struct power_supply *usb = psy ? psy : &motg->usb_psy;
/*
* TODO
@@ -1151,40 +1167,44 @@
else
charger_type = POWER_SUPPLY_TYPE_BATTERY;
- if (!psy) {
+ if (!usb) {
pr_err("No USB power supply registered!\n");
return -EINVAL;
}
pr_debug("setting usb power supply type %d\n", charger_type);
- power_supply_set_supply_type(psy, charger_type);
+ power_supply_set_supply_type(usb, charger_type);
return 0;
}
static int msm_otg_notify_power_supply(struct msm_otg *motg, unsigned mA)
{
+ struct power_supply *usb = psy ? psy : &motg->usb_psy;
- if (!psy)
- goto psy_not_supported;
-
- if (motg->cur_power == 0 && mA > 0) {
- /* Enable charging */
- if (power_supply_set_online(psy, true))
- goto psy_not_supported;
- } else if (motg->cur_power > 0 && mA == 0) {
- /* Disable charging */
- if (power_supply_set_online(psy, false))
- goto psy_not_supported;
- return 0;
+ if (!usb) {
+ dev_dbg(motg->phy.dev, "no usb power supply registered\n");
+ goto psy_error;
}
- /* Set max current limit */
- if (power_supply_set_current_limit(psy, 1000*mA))
- goto psy_not_supported;
+ if (motg->cur_power == 0 && mA > 2) {
+ /* Enable charging */
+ if (power_supply_set_online(usb, true))
+ goto psy_error;
+ if (power_supply_set_current_limit(usb, 1000*mA))
+ goto psy_error;
+ } else if (motg->cur_power > 0 && (mA == 0 || mA == 2)) {
+ /* Disable charging */
+ if (power_supply_set_online(usb, false))
+ goto psy_error;
+ /* Set max current limit */
+ if (power_supply_set_current_limit(usb, 0))
+ goto psy_error;
+ }
+ power_supply_changed(usb);
return 0;
-psy_not_supported:
- dev_dbg(motg->phy.dev, "Power Supply doesn't support USB charger\n");
+psy_error:
+ dev_dbg(motg->phy.dev, "power supply error when setting property\n");
return -ENXIO;
}
@@ -2041,6 +2061,8 @@
udelay(20);
break;
case SNPS_28NM_INTEGRATED_PHY:
+ /* disable DP and DM pull down resistors */
+ ulpi_write(phy, 0x6, 0xC);
/* Clear charger detecting control bits */
ulpi_write(phy, 0x1F, 0x86);
/* Clear alt interrupt latch and enable bits */
@@ -2120,8 +2142,7 @@
switch (motg->chg_state) {
case USB_CHG_STATE_UNDEFINED:
msm_chg_block_on(motg);
- if (motg->pdata->enable_dcd)
- msm_chg_enable_dcd(motg);
+ msm_chg_enable_dcd(motg);
msm_chg_enable_aca_det(motg);
motg->chg_state = USB_CHG_STATE_WAIT_FOR_DCD;
motg->dcd_retries = 0;
@@ -2148,12 +2169,10 @@
break;
}
}
- if (motg->pdata->enable_dcd)
- is_dcd = msm_chg_check_dcd(motg);
+ is_dcd = msm_chg_check_dcd(motg);
tmout = ++motg->dcd_retries == MSM_CHG_DCD_MAX_RETRIES;
if (is_dcd || tmout) {
- if (motg->pdata->enable_dcd)
- msm_chg_disable_dcd(motg);
+ msm_chg_disable_dcd(motg);
msm_chg_enable_primary_det(motg);
delay = MSM_CHG_PRIMARY_DET_TIME;
motg->chg_state = USB_CHG_STATE_DCD_DONE;
@@ -2316,9 +2335,13 @@
case OTG_STATE_UNDEFINED:
msm_otg_reset(otg->phy);
msm_otg_init_sm(motg);
- psy = power_supply_get_by_name("usb");
- if (!psy)
- pr_err("couldn't get usb power supply\n");
+ if (!psy && legacy_power_supply) {
+ psy = power_supply_get_by_name("usb");
+
+ if (!psy)
+ pr_err("couldn't get usb power supply\n");
+ }
+
otg->phy->state = OTG_STATE_B_IDLE;
if (!test_bit(B_SESS_VLD, &motg->inputs) &&
test_bit(ID, &motg->inputs)) {
@@ -3359,6 +3382,71 @@
return count;
}
+static int otg_power_get_property_usb(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct msm_otg *motg = container_of(psy, struct msm_otg,
+ usb_psy);
+ switch (psp) {
+ case POWER_SUPPLY_PROP_SCOPE:
+ if (motg->host_mode)
+ val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
+ else
+ val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ val->intval = motg->current_max;
+ break;
+ /* Reflect USB enumeration */
+ case POWER_SUPPLY_PROP_PRESENT:
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = motg->online;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int otg_power_set_property_usb(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct msm_otg *motg = container_of(psy, struct msm_otg,
+ usb_psy);
+
+ switch (psp) {
+ /* Process PMIC notification in PRESENT prop */
+ case POWER_SUPPLY_PROP_PRESENT:
+ msm_otg_set_vbus_state(val->intval);
+ break;
+ /* The ONLINE property reflects if usb has enumerated */
+ case POWER_SUPPLY_PROP_ONLINE:
+ motg->online = val->intval;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ motg->current_max = val->intval;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ power_supply_changed(&motg->usb_psy);
+ return 0;
+}
+
+static char *otg_pm_power_supplied_to[] = {
+ "battery",
+};
+
+static enum power_supply_property otg_pm_power_props_usb[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_SCOPE,
+};
+
const struct file_operations msm_otg_bus_fops = {
.open = msm_otg_bus_open,
.read = seq_read,
@@ -3434,6 +3522,38 @@
debugfs_remove_recursive(msm_otg_dbg_root);
}
+#define MSM_OTG_CMD_ID 0x09
+#define MSM_OTG_DEVICE_ID 0x04
+#define MSM_OTG_VMID_IDX 0xFF
+#define MSM_OTG_MEM_TYPE 0x02
+struct msm_otg_scm_cmd_buf {
+ unsigned int device_id;
+ unsigned int vmid_idx;
+ unsigned int mem_type;
+} __attribute__ ((__packed__));
+
+static void msm_otg_pnoc_errata_fix(struct msm_otg *motg)
+{
+ int ret;
+ struct msm_otg_platform_data *pdata = motg->pdata;
+ struct msm_otg_scm_cmd_buf cmd_buf;
+
+ if (!pdata->pnoc_errata_fix)
+ return;
+
+ dev_dbg(motg->phy.dev, "applying fix for pnoc h/w issue\n");
+
+ cmd_buf.device_id = MSM_OTG_DEVICE_ID;
+ cmd_buf.vmid_idx = MSM_OTG_VMID_IDX;
+ cmd_buf.mem_type = MSM_OTG_MEM_TYPE;
+
+ ret = scm_call(SCM_SVC_CP, MSM_OTG_CMD_ID, &cmd_buf,
+ sizeof(cmd_buf), NULL, 0);
+
+ if (ret)
+ dev_err(motg->phy.dev, "scm command failed to update VMIDMT\n");
+}
+
static u64 msm_otg_dma_mask = DMA_BIT_MASK(64);
static struct platform_device *msm_otg_add_pdev(
struct platform_device *ofdev, const char *name)
@@ -3513,6 +3633,23 @@
return retval;
}
+static int msm_otg_register_power_supply(struct platform_device *pdev,
+ struct msm_otg *motg)
+{
+ int ret;
+
+ ret = power_supply_register(&pdev->dev, &motg->usb_psy);
+ if (ret < 0) {
+ dev_err(motg->phy.dev,
+ "%s:power_supply_register usb failed\n",
+ __func__);
+ return ret;
+ }
+
+ legacy_power_supply = false;
+ return 0;
+}
+
struct msm_otg_platform_data *msm_otg_dt_to_pdata(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
@@ -3547,6 +3684,8 @@
&pdata->pmic_id_irq);
pdata->disable_reset_on_disconnect = of_property_read_bool(node,
"qcom,hsusb-otg-disable-reset");
+ pdata->pnoc_errata_fix = of_property_read_bool(node,
+ "qcom,hsusb-otg-pnoc-errata-fix");
return pdata;
}
@@ -3745,6 +3884,9 @@
}
clk_prepare_enable(motg->core_clk);
+ /* Check if USB mem_type change is needed to workaround PNOC hw issue */
+ msm_otg_pnoc_errata_fix(motg);
+
writel(0, USB_USBINTR);
writel(0, USB_OTGSC);
/* Ensure that above STOREs are completed before enabling interrupts */
@@ -3771,8 +3913,8 @@
}
if (motg->async_irq) {
- ret = request_irq(motg->async_irq, msm_otg_irq, IRQF_SHARED,
- "msm_otg", motg);
+ ret = request_irq(motg->async_irq, msm_otg_irq,
+ IRQF_TRIGGER_RISING, "msm_otg", motg);
if (ret) {
dev_err(&pdev->dev, "request irq failed (ASYNC INT)\n");
goto free_irq;
@@ -3831,9 +3973,6 @@
dev_dbg(&pdev->dev, "mode debugfs file is"
"not available\n");
- if (motg->pdata->otg_control == OTG_PMIC_CONTROL)
- pm8921_charger_register_vbus_sn(&msm_otg_set_vbus_state);
-
if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY) {
if (motg->pdata->otg_control == OTG_PMIC_CONTROL &&
(!(motg->pdata->mode == USB_OTG) ||
@@ -3863,6 +4002,28 @@
debug_bus_voting_enabled = true;
}
+ motg->usb_psy.name = "usb";
+ motg->usb_psy.type = POWER_SUPPLY_TYPE_USB;
+ motg->usb_psy.supplied_to = otg_pm_power_supplied_to;
+ motg->usb_psy.num_supplicants = ARRAY_SIZE(otg_pm_power_supplied_to);
+ motg->usb_psy.properties = otg_pm_power_props_usb;
+ motg->usb_psy.num_properties = ARRAY_SIZE(otg_pm_power_props_usb);
+ motg->usb_psy.get_property = otg_power_get_property_usb;
+ motg->usb_psy.set_property = otg_power_set_property_usb;
+
+ if (motg->pdata->otg_control == OTG_PMIC_CONTROL) {
+ /* if pm8921 use legacy implementation */
+ if (!pm8921_charger_register_vbus_sn(&msm_otg_set_vbus_state)) {
+ dev_dbg(motg->phy.dev, "%s: legacy support\n",
+ __func__);
+ legacy_power_supply = true;
+ } else {
+ ret = msm_otg_register_power_supply(pdev, motg);
+ if (ret)
+ goto remove_phy;
+ }
+ }
+
return 0;
remove_phy:
diff --git a/drivers/video/msm/Kconfig b/drivers/video/msm/Kconfig
index 54d7090..ed4c25d 100644
--- a/drivers/video/msm/Kconfig
+++ b/drivers/video/msm/Kconfig
@@ -820,14 +820,6 @@
---help---
Support for DVI mode for MSM HDMI 1080p Panel
-config FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
- depends on FB_MSM_HDMI_MSM_PANEL
- bool "Use HDCP mode"
- default y
- ---help---
- Support for HDCP mode for MSM HDMI 1080p Panel
- Choose to enable HDCP
-
config FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
depends on FB_MSM_HDMI_MSM_PANEL
bool "Enable CEC"
diff --git a/drivers/video/msm/hdmi_msm.c b/drivers/video/msm/hdmi_msm.c
index 3b1610a..9f30041 100644
--- a/drivers/video/msm/hdmi_msm.c
+++ b/drivers/video/msm/hdmi_msm.c
@@ -69,12 +69,7 @@
static void hdmi_msm_dump_regs(const char *prefix);
-#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
static void hdmi_msm_hdcp_enable(void);
-#else
-static inline void hdmi_msm_hdcp_enable(void) {}
-#endif
-
static void hdmi_msm_turn_on(void);
static int hdmi_msm_audio_off(void);
static int hdmi_msm_read_edid(void);
@@ -678,7 +673,6 @@
case 0x00D8: return "ACR_48_1";
case 0x00E4: return "AUDIO_INFO0";
case 0x00E8: return "AUDIO_INFO1";
-#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
case 0x0110: return "HDCP_CTRL";
case 0x0114: return "HDCP_DEBUG_CTRL";
case 0x0118: return "HDCP_INT_CTRL";
@@ -693,7 +687,6 @@
case 0x014C: return "HDCP_RCVPORT_DATA5";
case 0x0150: return "HDCP_RCVPORT_DATA6";
case 0x0168: return "HDCP_RCVPORT_DATA12";
-#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
case 0x01D0: return "AUDIO_CFG";
case 0x0208: return "USEC_REFTIMER";
case 0x020C: return "DDC_CTRL";
@@ -708,14 +701,10 @@
case 0x0250: return "HPD_INT_STATUS";
case 0x0254: return "HPD_INT_CTRL";
case 0x0258: return "HPD_CTRL";
-#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
case 0x025C: return "HDCP_ENTROPY_CTRL1";
-#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
case 0x027C: return "DDC_REF";
-#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
case 0x0284: return "HDCP_SW_UPPER_AKSV";
case 0x0288: return "HDCP_SW_LOWER_AKSV";
-#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
case 0x02B4: return "ACTIVE_H";
case 0x02B8: return "ACTIVE_V";
case 0x02BC: return "ACTIVE_V_F2";
@@ -784,7 +773,7 @@
DEV_INFO("HDMI HPD: CONNECTED: send ONLINE\n");
kobject_uevent(external_common_state->uevent_kobj, KOBJ_ONLINE);
- if (!external_common_state->present_hdcp) {
+ if (!hdmi_msm_state->hdcp_enable) {
/* Send Audio for HDMI Compliance Cases*/
envp[0] = "HDCP_STATE=PASS";
envp[1] = NULL;
@@ -873,10 +862,13 @@
}
#endif
-#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
static void hdcp_deauthenticate(void);
static void hdmi_msm_hdcp_reauth_work(struct work_struct *work)
{
+ if (!hdmi_msm_state->hdcp_enable) {
+ DEV_DBG("%s: HDCP not enabled\n", __func__);
+ return;
+ }
/* Don't process recursive actions */
mutex_lock(&hdmi_msm_state_mutex);
@@ -894,17 +886,20 @@
* Therefore, as surprising as it may sound do reauth
* only if the device is HDCP-capable
*/
- if (external_common_state->present_hdcp) {
- hdcp_deauthenticate();
- mutex_lock(&hdcp_auth_state_mutex);
- hdmi_msm_state->reauth = TRUE;
- mutex_unlock(&hdcp_auth_state_mutex);
- mod_timer(&hdmi_msm_state->hdcp_timer, jiffies + HZ/2);
- }
+ hdcp_deauthenticate();
+ mutex_lock(&hdcp_auth_state_mutex);
+ hdmi_msm_state->reauth = TRUE;
+ mutex_unlock(&hdcp_auth_state_mutex);
+ mod_timer(&hdmi_msm_state->hdcp_timer, jiffies + HZ/2);
}
static void hdmi_msm_hdcp_work(struct work_struct *work)
{
+ if (!hdmi_msm_state->hdcp_enable) {
+ DEV_DBG("%s: HDCP not enabled\n", __func__);
+ return;
+ }
+
/* Only re-enable if cable still connected */
mutex_lock(&external_common_state_hpd_mutex);
if (external_common_state->hpd_state &&
@@ -925,7 +920,111 @@
hdmi_msm_state->reauth = FALSE;
}
}
-#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
+
+int hdmi_msm_process_hdcp_interrupts(void)
+{
+ int rc = -1;
+ uint32 hdcp_int_val;
+ char *envp[2];
+
+ if (!hdmi_msm_state->hdcp_enable) {
+ DEV_DBG("%s: HDCP not enabled\n", __func__);
+ return -EINVAL;
+ }
+
+ /* HDCP_INT_CTRL[0x0118]
+ * [0] AUTH_SUCCESS_INT [R] HDCP Authentication Success
+ * interrupt status
+ * [1] AUTH_SUCCESS_ACK [W] Acknowledge bit for HDCP
+ * Authentication Success bit - write 1 to clear
+ * [2] AUTH_SUCCESS_MASK [R/W] Mask bit for HDCP Authentication
+ * Success interrupt - set to 1 to enable interrupt */
+ hdcp_int_val = HDMI_INP_ND(0x0118);
+ if ((hdcp_int_val & (1 << 2)) && (hdcp_int_val & (1 << 0))) {
+ /* AUTH_SUCCESS_INT */
+ HDMI_OUTP(0x0118, (hdcp_int_val | (1 << 1)) & ~(1 << 0));
+ DEV_INFO("HDCP: AUTH_SUCCESS_INT received\n");
+ complete_all(&hdmi_msm_state->hdcp_success_done);
+ return 0;
+ }
+
+ /* [4] AUTH_FAIL_INT [R] HDCP Authentication Lost
+ * interrupt Status
+ * [5] AUTH_FAIL_ACK [W] Acknowledge bit for HDCP
+ * Authentication Lost bit - write 1 to clear
+ * [6] AUTH_FAIL_MASK [R/W] Mask bit fo HDCP Authentication
+ * Lost interrupt set to 1 to enable interrupt
+ * [7] AUTH_FAIL_INFO_ACK [W] Acknowledge bit for HDCP
+ * Authentication Failure Info field - write 1 to clear */
+ if ((hdcp_int_val & (1 << 6)) && (hdcp_int_val & (1 << 4))) {
+ /* AUTH_FAIL_INT */
+ /* Clear and Disable */
+ uint32 link_status = HDMI_INP_ND(0x011C);
+ HDMI_OUTP(0x0118, (hdcp_int_val | (1 << 5))
+ & ~((1 << 6) | (1 << 4)));
+ DEV_INFO("HDCP: AUTH_FAIL_INT received, LINK0_STATUS=0x%08x\n",
+ link_status);
+ if (hdmi_msm_state->full_auth_done) {
+ switch_set_state(&external_common_state->sdev, 0);
+ DEV_INFO("Hdmi state switched to %d: %s\n",
+ external_common_state->sdev.state, __func__);
+
+ envp[0] = "HDCP_STATE=FAIL";
+ envp[1] = NULL;
+ DEV_INFO("HDMI HPD:QDSP OFF\n");
+ kobject_uevent_env(external_common_state->uevent_kobj,
+ KOBJ_CHANGE, envp);
+
+ mutex_lock(&hdcp_auth_state_mutex);
+ hdmi_msm_state->full_auth_done = FALSE;
+ mutex_unlock(&hdcp_auth_state_mutex);
+ /* Calling reauth only when authentication
+ * is sucessful or else we always go into
+ * the reauth loop. Also, No need to reauthenticate
+ * if authentication failed because of cable disconnect
+ */
+ if (((link_status & 0xF0) >> 4) != 0x7) {
+ DEV_DBG("Reauthenticate From %s HDCP FAIL INT ",
+ __func__);
+ queue_work(hdmi_work_queue,
+ &hdmi_msm_state->hdcp_reauth_work);
+ } else {
+ DEV_INFO("HDCP: HDMI cable disconnected\n");
+ }
+ }
+
+ /* Clear AUTH_FAIL_INFO as well */
+ HDMI_OUTP(0x0118, (hdcp_int_val | (1 << 7)));
+ return 0;
+ }
+
+ /* [8] DDC_XFER_REQ_INT [R] HDCP DDC Transfer Request
+ * interrupt status
+ * [9] DDC_XFER_REQ_ACK [W] Acknowledge bit for HDCP DDC
+ * Transfer Request bit - write 1 to clear
+ * [10] DDC_XFER_REQ_MASK [R/W] Mask bit for HDCP DDC Transfer
+ * Request interrupt - set to 1 to enable interrupt */
+ if ((hdcp_int_val & (1 << 10)) && (hdcp_int_val & (1 << 8))) {
+ /* DDC_XFER_REQ_INT */
+ HDMI_OUTP_ND(0x0118, (hdcp_int_val | (1 << 9)) & ~(1 << 8));
+ if (!(hdcp_int_val & (1 << 12)))
+ return 0;
+ }
+ /* [12] DDC_XFER_DONE_INT [R] HDCP DDC Transfer done interrupt
+ * status
+ * [13] DDC_XFER_DONE_ACK [W] Acknowledge bit for HDCP DDC
+ * Transfer done bit - write 1 to clear
+ * [14] DDC_XFER_DONE_MASK [R/W] Mask bit for HDCP DDC Transfer
+ * done interrupt - set to 1 to enable interrupt */
+ if ((hdcp_int_val & (1 << 14)) && (hdcp_int_val & (1 << 12))) {
+ /* DDC_XFER_DONE_INT */
+ HDMI_OUTP_ND(0x0118, (hdcp_int_val | (1 << 13)) & ~(1 << 12));
+ DEV_INFO("HDCP: DDC_XFER_DONE received\n");
+ return 0;
+ }
+
+ return rc;
+}
static irqreturn_t hdmi_msm_isr(int irq, void *dev_id)
{
@@ -936,10 +1035,6 @@
#endif
uint32 ddc_int_ctrl;
uint32 audio_int_val;
-#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
- uint32 hdcp_int_val;
- char *envp[2];
-#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
static uint32 fifo_urun_int_occurred;
static uint32 sample_drop_int_occurred;
const uint32 occurrence_limit = 5;
@@ -1057,96 +1152,8 @@
return IRQ_HANDLED;
}
-#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
- /* HDCP_INT_CTRL[0x0118]
- * [0] AUTH_SUCCESS_INT [R] HDCP Authentication Success
- * interrupt status
- * [1] AUTH_SUCCESS_ACK [W] Acknowledge bit for HDCP
- * Authentication Success bit - write 1 to clear
- * [2] AUTH_SUCCESS_MASK [R/W] Mask bit for HDCP Authentication
- * Success interrupt - set to 1 to enable interrupt */
- hdcp_int_val = HDMI_INP_ND(0x0118);
- if ((hdcp_int_val & (1 << 2)) && (hdcp_int_val & (1 << 0))) {
- /* AUTH_SUCCESS_INT */
- HDMI_OUTP(0x0118, (hdcp_int_val | (1 << 1)) & ~(1 << 0));
- DEV_INFO("HDCP: AUTH_SUCCESS_INT received\n");
- complete_all(&hdmi_msm_state->hdcp_success_done);
+ if (!hdmi_msm_process_hdcp_interrupts())
return IRQ_HANDLED;
- }
- /* [4] AUTH_FAIL_INT [R] HDCP Authentication Lost
- * interrupt Status
- * [5] AUTH_FAIL_ACK [W] Acknowledge bit for HDCP
- * Authentication Lost bit - write 1 to clear
- * [6] AUTH_FAIL_MASK [R/W] Mask bit fo HDCP Authentication
- * Lost interrupt set to 1 to enable interrupt
- * [7] AUTH_FAIL_INFO_ACK [W] Acknowledge bit for HDCP
- * Authentication Failure Info field - write 1 to clear */
- if ((hdcp_int_val & (1 << 6)) && (hdcp_int_val & (1 << 4))) {
- /* AUTH_FAIL_INT */
- /* Clear and Disable */
- uint32 link_status = HDMI_INP_ND(0x011C);
- HDMI_OUTP(0x0118, (hdcp_int_val | (1 << 5))
- & ~((1 << 6) | (1 << 4)));
- DEV_INFO("HDCP: AUTH_FAIL_INT received, LINK0_STATUS=0x%08x\n",
- link_status);
- if (hdmi_msm_state->full_auth_done) {
- switch_set_state(&external_common_state->sdev, 0);
- DEV_INFO("Hdmi state switched to %d: %s\n",
- external_common_state->sdev.state, __func__);
-
- envp[0] = "HDCP_STATE=FAIL";
- envp[1] = NULL;
- DEV_INFO("HDMI HPD:QDSP OFF\n");
- kobject_uevent_env(external_common_state->uevent_kobj,
- KOBJ_CHANGE, envp);
-
- mutex_lock(&hdcp_auth_state_mutex);
- hdmi_msm_state->full_auth_done = FALSE;
- mutex_unlock(&hdcp_auth_state_mutex);
- /* Calling reauth only when authentication
- * is sucessful or else we always go into
- * the reauth loop. Also, No need to reauthenticate
- * if authentication failed because of cable disconnect
- */
- if (((link_status & 0xF0) >> 4) != 0x7) {
- DEV_DBG("Reauthenticate From %s HDCP FAIL INT ",
- __func__);
- queue_work(hdmi_work_queue,
- &hdmi_msm_state->hdcp_reauth_work);
- } else {
- DEV_INFO("HDCP: HDMI cable disconnected\n");
- }
- }
-
- /* Clear AUTH_FAIL_INFO as well */
- HDMI_OUTP(0x0118, (hdcp_int_val | (1 << 7)));
- return IRQ_HANDLED;
- }
- /* [8] DDC_XFER_REQ_INT [R] HDCP DDC Transfer Request
- * interrupt status
- * [9] DDC_XFER_REQ_ACK [W] Acknowledge bit for HDCP DDC
- * Transfer Request bit - write 1 to clear
- * [10] DDC_XFER_REQ_MASK [R/W] Mask bit for HDCP DDC Transfer
- * Request interrupt - set to 1 to enable interrupt */
- if ((hdcp_int_val & (1 << 10)) && (hdcp_int_val & (1 << 8))) {
- /* DDC_XFER_REQ_INT */
- HDMI_OUTP_ND(0x0118, (hdcp_int_val | (1 << 9)) & ~(1 << 8));
- if (!(hdcp_int_val & (1 << 12)))
- return IRQ_HANDLED;
- }
- /* [12] DDC_XFER_DONE_INT [R] HDCP DDC Transfer done interrupt
- * status
- * [13] DDC_XFER_DONE_ACK [W] Acknowledge bit for HDCP DDC
- * Transfer done bit - write 1 to clear
- * [14] DDC_XFER_DONE_MASK [R/W] Mask bit for HDCP DDC Transfer
- * done interrupt - set to 1 to enable interrupt */
- if ((hdcp_int_val & (1 << 14)) && (hdcp_int_val & (1 << 12))) {
- /* DDC_XFER_DONE_INT */
- HDMI_OUTP_ND(0x0118, (hdcp_int_val | (1 << 13)) & ~(1 << 12));
- DEV_INFO("HDCP: DDC_XFER_DONE received\n");
- return IRQ_HANDLED;
- }
-#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
/* Process CEC Interrupt */
@@ -1305,7 +1312,7 @@
if (external_common_state->hdmi_sink == 0) {
/* HDMI_DVI_SEL */
reg_val |= 0x00000002;
- if (external_common_state->present_hdcp)
+ if (hdmi_msm_state->hdcp_enable)
/* HDMI Encryption */
reg_val |= 0x00000004;
/* HDMI_CTRL */
@@ -1313,7 +1320,7 @@
/* HDMI_DVI_SEL */
reg_val &= ~0x00000002;
} else {
- if (external_common_state->present_hdcp)
+ if (hdmi_msm_state->hdcp_enable)
/* HDMI_Encryption_ON */
reg_val |= 0x00000006;
else
@@ -1393,7 +1400,6 @@
return 0;
}
-#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
static int hdmi_msm_ddc_write(uint32 dev_addr, uint32 offset,
const uint8 *data_buf, uint32 data_len, const char *what)
{
@@ -1589,7 +1595,6 @@
error:
return status;
}
-#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
static int hdmi_msm_ddc_read_retry(uint32 dev_addr, uint32 offset,
uint8 *data_buf, uint32 data_len, uint32 request_len, int retry,
@@ -2150,9 +2155,13 @@
return status;
}
-#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
static void hdcp_auth_info(uint32 auth_info)
{
+ if (!hdmi_msm_state->hdcp_enable) {
+ DEV_DBG("%s: HDCP not enabled\n", __func__);
+ return;
+ }
+
switch (auth_info) {
case 0:
DEV_INFO("%s: None", __func__);
@@ -2187,6 +2196,11 @@
static void hdcp_key_state(uint32 key_state)
{
+ if (!hdmi_msm_state->hdcp_enable) {
+ DEV_DBG("%s: HDCP not enabled\n", __func__);
+ return;
+ }
+
switch (key_state) {
case 0:
DEV_WARN("%s: No HDCP Keys", __func__);
@@ -2230,6 +2244,11 @@
{
int hdcp_link_status = HDMI_INP(0x011C);
+ if (!hdmi_msm_state->hdcp_enable) {
+ DEV_DBG("%s: HDCP not enabled\n", __func__);
+ return;
+ }
+
/* Disable HDCP interrupts */
HDMI_OUTP(0x0118, 0x0);
@@ -2257,6 +2276,11 @@
int failure;
int nack0;
+ if (!hdmi_msm_state->hdcp_enable) {
+ DEV_DBG("%s: HDCP not enabled\n", __func__);
+ return;
+ }
+
/*
* Check for any DDC transfer failures
* 0x0128 HDCP_DDC_STATUS
@@ -2367,6 +2391,11 @@
static uint8 buf[0xFF];
memset(buf, 0, sizeof(buf));
+ if (!hdmi_msm_state->hdcp_enable) {
+ DEV_DBG("%s: HDCP not enabled\n", __func__);
+ return 0;
+ }
+
if (!is_part1_done) {
is_part1_done = TRUE;
@@ -2654,6 +2683,11 @@
int ret;
uint8 buf[4];
+ if (!hdmi_msm_state->hdcp_enable) {
+ DEV_DBG("%s: HDCP not enabled\n", __func__);
+ return 0;
+ }
+
snprintf(what, sizeof(what), "V' H0");
ret = hdmi_msm_ddc_read(0x74, 0x20, buf, 4, 5, what, TRUE);
if (ret) {
@@ -2746,6 +2780,11 @@
boolean ksv_done = FALSE;
+ if (!hdmi_msm_state->hdcp_enable) {
+ DEV_DBG("%s: HDCP not enabled\n", __func__);
+ return 0;
+ }
+
memset(buf, 0, sizeof(buf));
memset(kvs_fifo, 0, sizeof(kvs_fifo));
@@ -2938,6 +2977,12 @@
{
int ret = 0;
int poll = 3000;
+
+ if (!hdmi_msm_state->hdcp_enable) {
+ DEV_DBG("%s: HDCP not enabled\n", __func__);
+ return 0;
+ }
+
while (poll) {
/* 0x011C HDCP_LINK0_STATUS
[30:28] KEYS_STATE = 3 = "Valid"
@@ -2968,7 +3013,7 @@
uint32 found_repeater = 0x0;
char *envp[2];
- if (!hdmi_msm_has_hdcp()) {
+ if (!hdmi_msm_state->hdcp_enable) {
DEV_INFO("%s: HDCP NOT ENABLED\n", __func__);
return;
}
@@ -3069,7 +3114,6 @@
DEV_INFO("Hdmi state switched to %d: %s\n",
external_common_state->sdev.state, __func__);
}
-#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
static void hdmi_msm_video_setup(int video_format)
{
@@ -3181,6 +3225,9 @@
/* HDMI_ACR_PKT_CTRL[0x0024] */
uint32 acr_pck_ctrl_reg = HDMI_INP(0x0024);
+ /* Clear N/CTS selection bits */
+ acr_pck_ctrl_reg &= ~(3 << 4);
+
if (enabled) {
const struct hdmi_disp_mode_timing_type *timing =
hdmi_common_get_supported_mode(video_format);
@@ -3585,13 +3632,14 @@
void hdmi_msm_audio_sample_rate_reset(int rate)
{
+ if (msm_hdmi_sample_rate == rate)
+ return;
+
msm_hdmi_sample_rate = rate;
-#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
- if (hdmi_msm_has_hdcp())
+ if (hdmi_msm_state->hdcp_enable)
hdcp_deauthenticate();
else
-#endif
hdmi_msm_turn_on();
}
EXPORT_SYMBOL(hdmi_msm_audio_sample_rate_reset);
@@ -4161,12 +4209,10 @@
#endif
hdmi_msm_spd_infoframe_packetsetup();
-#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
- if (hdmi_msm_state->reauth) {
+ if (hdmi_msm_state->hdcp_enable && hdmi_msm_state->reauth) {
hdmi_msm_hdcp_enable();
hdmi_msm_state->reauth = FALSE ;
}
-#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
/* re-initialize CEC if enabled */
@@ -4211,12 +4257,15 @@
}
}
-#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
static void hdmi_msm_hdcp_timer(unsigned long data)
{
+ if (!hdmi_msm_state->hdcp_enable) {
+ DEV_DBG("%s: HDCP not enabled\n", __func__);
+ return;
+ }
+
queue_work(hdmi_work_queue, &hdmi_msm_state->hdcp_work);
}
-#endif
#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
static void hdmi_msm_cec_read_timer_func(unsigned long data)
@@ -4381,12 +4430,14 @@
mutex_unlock(&external_common_state_hpd_mutex);
hdmi_msm_turn_on();
- /* Kick off HDCP Authentication */
- mutex_lock(&hdcp_auth_state_mutex);
- hdmi_msm_state->reauth = FALSE;
- hdmi_msm_state->full_auth_done = FALSE;
- mutex_unlock(&hdcp_auth_state_mutex);
- mod_timer(&hdmi_msm_state->hdcp_timer, jiffies + HZ/2);
+ if (hdmi_msm_state->hdcp_enable) {
+ /* Kick off HDCP Authentication */
+ mutex_lock(&hdcp_auth_state_mutex);
+ hdmi_msm_state->reauth = FALSE;
+ hdmi_msm_state->full_auth_done = FALSE;
+ mutex_unlock(&hdcp_auth_state_mutex);
+ mod_timer(&hdmi_msm_state->hdcp_timer, jiffies + HZ/2);
+ }
} else
mutex_unlock(&external_common_state_hpd_mutex);
@@ -4410,16 +4461,14 @@
external_common_state->sdev.state, __func__);
if (on) {
hdmi_msm_read_edid();
- if (hdmi_msm_has_hdcp())
- hdmi_msm_state->reauth = FALSE ;
+ hdmi_msm_state->reauth = FALSE ;
/* Build EDID table */
hdmi_msm_turn_on();
DEV_INFO("HDMI HPD: CONNECTED: send ONLINE\n");
kobject_uevent(external_common_state->uevent_kobj,
KOBJ_ONLINE);
- hdmi_msm_hdcp_enable();
envp[0] = 0;
- if (!hdmi_msm_has_hdcp()) {
+ if (!hdmi_msm_state->hdcp_enable) {
/* Send Audio for HDMI Compliance Cases*/
envp[0] = "HDCP_STATE=PASS";
envp[1] = NULL;
@@ -4429,6 +4478,8 @@
switch_set_state(&external_common_state->sdev, 1);
DEV_INFO("Hdmi state switched to %d: %s\n",
external_common_state->sdev.state, __func__);
+ } else {
+ hdmi_msm_hdcp_enable();
}
} else {
DEV_INFO("HDMI HPD: DISCONNECTED: send OFFLINE\n");
@@ -4452,7 +4503,6 @@
if (!hdmi_msm_state->hdmi_app_clk)
return -ENODEV;
-#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
mutex_lock(&hdmi_msm_state_mutex);
if (hdmi_msm_state->hdcp_activating) {
hdmi_msm_state->panel_power_on = FALSE;
@@ -4461,13 +4511,10 @@
return 0;
}
mutex_unlock(&hdmi_msm_state_mutex);
-#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
DEV_INFO("power: OFF (audio off, Reset Core)\n");
hdmi_msm_audio_off();
-#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
hdcp_deauthenticate();
-#endif
hdmi_msm_powerdown_phy();
hdmi_msm_state->panel_power_on = FALSE;
@@ -4479,6 +4526,28 @@
return hdmi_msm_state->is_mhl_enabled;
}
+void hdmi_msm_config_hdcp_feature(void)
+{
+ if (hdcp_feature_on && hdmi_msm_has_hdcp()) {
+ init_timer(&hdmi_msm_state->hdcp_timer);
+ hdmi_msm_state->hdcp_timer.function = hdmi_msm_hdcp_timer;
+ hdmi_msm_state->hdcp_timer.data = (uint32)NULL;
+ hdmi_msm_state->hdcp_timer.expires = 0xffffffffL;
+
+ init_completion(&hdmi_msm_state->hdcp_success_done);
+ INIT_WORK(&hdmi_msm_state->hdcp_reauth_work,
+ hdmi_msm_hdcp_reauth_work);
+ INIT_WORK(&hdmi_msm_state->hdcp_work, hdmi_msm_hdcp_work);
+ hdmi_msm_state->hdcp_enable = TRUE;
+ } else {
+ del_timer(&hdmi_msm_state->hdcp_timer);
+ hdmi_msm_state->hdcp_enable = FALSE;
+ }
+ external_common_state->present_hdcp = hdmi_msm_state->hdcp_enable;
+ DEV_INFO("%s: HDCP Feature: %s\n", __func__,
+ hdmi_msm_state->hdcp_enable ? "Enabled" : "Disabled");
+}
+
static int __devinit hdmi_msm_probe(struct platform_device *pdev)
{
int rc;
@@ -4591,15 +4660,6 @@
hdmi_msm_state->hpd_state_timer.expires = 0xffffffffL;
-#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
- init_timer(&hdmi_msm_state->hdcp_timer);
- hdmi_msm_state->hdcp_timer.function =
- hdmi_msm_hdcp_timer;
- hdmi_msm_state->hdcp_timer.data = (uint32)NULL;
-
- hdmi_msm_state->hdcp_timer.expires = 0xffffffffL;
-#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
-
#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
init_timer(&hdmi_msm_state->cec_read_timer);
hdmi_msm_state->cec_read_timer.function =
@@ -4626,22 +4686,7 @@
goto error;
}
- if (hdmi_msm_has_hdcp()) {
- /* Don't Set Encryption in case of non HDCP builds */
- external_common_state->present_hdcp = FALSE;
-#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
- external_common_state->present_hdcp = TRUE;
-#endif
- } else {
- external_common_state->present_hdcp = FALSE;
-#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
- /*
- * If the device is not hdcp capable do
- * not start hdcp timer.
- */
- del_timer(&hdmi_msm_state->hdcp_timer);
-#endif
- }
+ hdmi_msm_config_hdcp_feature();
/* Initialize hdmi node and register with switch driver */
if (hdmi_prim_display)
@@ -4825,11 +4870,6 @@
hdmi_common_init_panel_info(&hdmi_msm_panel_data.panel_info);
init_completion(&hdmi_msm_state->ddc_sw_done);
INIT_WORK(&hdmi_msm_state->hpd_state_work, hdmi_msm_hpd_state_work);
-#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
- init_completion(&hdmi_msm_state->hdcp_success_done);
- INIT_WORK(&hdmi_msm_state->hdcp_reauth_work, hdmi_msm_hdcp_reauth_work);
- INIT_WORK(&hdmi_msm_state->hdcp_work, hdmi_msm_hdcp_work);
-#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
INIT_WORK(&hdmi_msm_state->cec_latch_detect_work,
@@ -4853,9 +4893,6 @@
" RELEASE"
#endif
" AUDIO EDID HPD HDCP"
-#ifndef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
- ":0"
-#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
" DVI"
#ifndef CONFIG_FB_MSM_HDMI_MSM_PANEL_DVI_SUPPORT
":0"
@@ -4885,6 +4922,15 @@
return rv;
pr_debug("%s: HDCP feature = %d\n", __func__, hdcp_feature_on);
+ if (hdmi_msm_state) {
+ if ((HDMI_INP(0x0250) & 0x2)) {
+ pr_err("%s: Unable to set HDCP feature", __func__);
+ pr_err("%s: HDMI panel is currently turned on",
+ __func__);
+ } else if (hdcp_feature_on != hdmi_msm_state->hdcp_enable) {
+ hdmi_msm_config_hdcp_feature();
+ }
+ }
return 0;
}
diff --git a/drivers/video/msm/hdmi_msm.h b/drivers/video/msm/hdmi_msm.h
index 5d27412..20bd492 100644
--- a/drivers/video/msm/hdmi_msm.h
+++ b/drivers/video/msm/hdmi_msm.h
@@ -64,13 +64,12 @@
struct timer_list hpd_state_timer;
struct completion ddc_sw_done;
-#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
+ bool hdcp_enable;
boolean hdcp_activating;
boolean reauth ;
struct work_struct hdcp_reauth_work, hdcp_work;
struct completion hdcp_success_done;
struct timer_list hdcp_timer;
-#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
boolean cec_enabled;
diff --git a/drivers/video/msm/mddi_quickvx.c b/drivers/video/msm/mddi_quickvx.c
index 95e7d41..37c147d 100644
--- a/drivers/video/msm/mddi_quickvx.c
+++ b/drivers/video/msm/mddi_quickvx.c
@@ -263,22 +263,10 @@
int ql_mddi_write(uint32 address, uint32 value)
{
- uint32 regval = 0;
int ret = 0;
ret = mddi_queue_register_write(address, value, TRUE, 0);
- if (!ret) {
- ret = mddi_queue_register_read(address, ®val, TRUE, 0);
- if (regval != value) {
- MDDI_MSG_DEBUG("\nMismatch: ql_mddi_write[0x%x]->0x%x "
- "r0x%x\n", address, value, regval);
- } else {
- MDDI_MSG_DEBUG("\nMatch: ql_mddi_write[0x%x]->0x%x "
- "r0x%x\n", address, value, regval);
- }
- }
-
return ret;
}
@@ -294,8 +282,6 @@
int ql_send_spi_cmd_to_lcd(uint32 index, uint32 cmd)
{
- int retry, ret;
- uint32 readval;
MDDI_MSG_DEBUG("\n %s(): index 0x%x, cmd 0x%x", __func__, index, cmd);
/* do the index phase */
@@ -308,18 +294,6 @@
/* set start */
ql_mddi_write(QUICKVX_SPI_CTRL_REG, QL_SPI_CTRL_LCD_START);
- retry = 0;
-
- do {
- ret = ql_mddi_read(QUICKVX_SPI_CTRL_REG, &readval);
-
- if (ret || ++retry > 5) {
- MDDI_MSG_DEBUG("\n ql_send_spi_cmd_to_lcd: retry "
- "timeout at index phase, ret = %d", ret);
- return -EIO;
- }
- mddi_wait(1);
- } while ((readval & QL_SPI_CTRL_MASK_rTxDone) == 0);
/* do the command phase */
/* send 24 bits in the cmd phase */
@@ -331,18 +305,6 @@
/* set start */
ql_mddi_write(QUICKVX_SPI_CTRL_REG, QL_SPI_CTRL_LCD_START);
- retry = 0;
-
- do {
- ret = ql_mddi_read(QUICKVX_SPI_CTRL_REG, &readval);
-
- if (ret || ++retry > 5) {
- MDDI_MSG_DEBUG("\n ql_send_spi_cmd_to_lcd: retry "
- "timeout at cmd phase, ret = %d", ret);
- return -EIO;
- }
- mddi_wait(1);
- } while ((readval & QL_SPI_CTRL_MASK_rTxDone) == 0);
return 0;
}
@@ -350,8 +312,6 @@
int ql_send_spi_data_from_lcd(uint32 index, uint32 *value)
{
- int retry, ret;
- uint32 readval;
MDDI_MSG_DEBUG("\n %s(): index 0x%x", __func__, index);
/* do the index phase */
@@ -364,19 +324,6 @@
/* set start */
ql_mddi_write(QUICKVX_SPI_CTRL_REG, QL_SPI_CTRL_LCD_START);
- retry = 0;
-
- do {
- ret = ql_mddi_read(QUICKVX_SPI_CTRL_REG, &readval);
-
- if (ret || ++retry > 5) {
- MDDI_MSG_DEBUG("\n ql_send_spi_cmd_to_lcd: retry "
- "timeout at index phase, ret = %d", ret);
- return -EIO;
- }
- mddi_wait(1);
- } while ((readval & QL_SPI_CTRL_MASK_rTxDone) == 0);
-
/* do the command phase */
/* send 8 bits and read 24 bits in the cmd phase, so total 32 bits */
ql_mddi_write(QUICKVX_SPI_TLEN_REG, 31);
@@ -387,29 +334,9 @@
/* set start */
ql_mddi_write(QUICKVX_SPI_CTRL_REG, QL_SPI_CTRL_LCD_START);
- retry = 0;
- do {
- ret = ql_mddi_read(QUICKVX_SPI_CTRL_REG, &readval);
+ return 0;
- if (ret || ++retry > 5) {
- MDDI_MSG_DEBUG("\n ql_send_spi_cmd_to_lcd: retry "
- "timeout at cmd phase, ret = %d", ret);
- return -EIO;
- }
- mddi_wait(1);
- } while ((readval & QL_SPI_CTRL_MASK_rTxDone) == 0);
-
- /* value will appear at lower 16 bits */
- ret = ql_mddi_read(QUICKVX_SPI_RX0_REG, value);
-
- if (!ret) {
- *value = *value & 0xffff;
- MDDI_MSG_DEBUG("\n QUICKVX_SPI_RX0_REG value = 0x%x", *value);
- } else
- MDDI_MSG_DEBUG("\n Read QUICKVX_SPI_RX0_REG Failed");
-
- return ret;
}
/* Global Variables */
diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c
index 6c0d08d..712d41b 100644
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -2214,6 +2214,8 @@
mfd->panel.type == LCDC_PANEL ||
mfd->panel.type == LVDS_PANEL)
mdp4_lcdc_off(pdev);
+ else if (mfd->panel.type == MDDI_PANEL)
+ mdp4_mddi_off(pdev);
mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
ret = panel_next_off(pdev);
@@ -2268,6 +2270,9 @@
mfd->panel.type == LCDC_PANEL ||
mfd->panel.type == LVDS_PANEL) {
mdp4_lcdc_on(pdev);
+ } else if (mfd->panel.type == MDDI_PANEL) {
+ mdp_vsync_cfg_regs(mfd, FALSE);
+ mdp4_mddi_on(pdev);
}
mdp_clk_ctrl(0);
@@ -2668,10 +2673,12 @@
mfd->ov0_wb_buf->size = mdp_pdata->ov0_wb_size;
mfd->ov1_wb_buf->size = mdp_pdata->ov1_wb_size;
mfd->mem_hid = mdp_pdata->mem_hid;
+ mfd->avtimer_phy = mdp_pdata->avtimer_phy;
} else {
mfd->ov0_wb_buf->size = 0;
mfd->ov1_wb_buf->size = 0;
mfd->mem_hid = 0;
+ mfd->avtimer_phy = 0;
}
/* initialize Post Processing data*/
@@ -2706,6 +2713,9 @@
mdp_vsync_resync_workqueue_handler);
mfd->hw_refresh = FALSE;
+ if (mfd->panel.type == MDDI_PANEL)
+ mdp4_mddi_rdptr_init(0);
+
if (mfd->panel.type == EXT_MDDI_PANEL) {
/* 15 fps -> 66 msec */
mfd->refresh_timer_duration = (66 * HZ / 1000);
@@ -2948,6 +2958,7 @@
mdp_clk_ctrl(0);
return -ENODEV;
}
+ mdp4_wfd_init(0);
pdata->on = mdp4_overlay_writeback_on;
pdata->off = mdp4_overlay_writeback_off;
mfd->dma_fnc = mdp4_writeback_overlay;
diff --git a/drivers/video/msm/mdp.h b/drivers/video/msm/mdp.h
index d939c62..b4a7f79 100644
--- a/drivers/video/msm/mdp.h
+++ b/drivers/video/msm/mdp.h
@@ -796,6 +796,10 @@
{
return 0;
}
+static inline int mdp4_mddi_off(struct platform_device *pdev)
+{
+ return 0;
+}
static inline int mdp4_dsi_cmd_on(struct platform_device *pdev)
{
return 0;
@@ -808,6 +812,19 @@
{
return 0;
}
+static inline int mdp4_mddi_on(struct platform_device *pdev)
+{
+ return 0;
+}
+#endif
+
+
+#ifndef CONFIG_FB_MSM_MDDI
+static inline void mdp4_mddi_rdptr_init(int cndx)
+{
+ /* empty */
+}
+
#endif
void set_cont_splashScreen_status(int);
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
index b7d4c32..3ea196a 100644
--- a/drivers/video/msm/mdp4.h
+++ b/drivers/video/msm/mdp4.h
@@ -306,10 +306,16 @@
uint32 is_fg; /* control alpha & color key */
uint32 srcp0_addr; /* interleave, luma */
uint32 srcp0_ystride;
+ struct file *srcp0_file;
+ int put0_need;
uint32 srcp1_addr; /* pseudoplanar, chroma plane */
uint32 srcp1_ystride;
+ struct file *srcp1_file;
+ int put1_need;
uint32 srcp2_addr; /* planar color 2*/
uint32 srcp2_ystride;
+ struct file *srcp2_file;
+ int put2_need;
uint32 srcp3_addr; /* alpha/color 3 */
uint32 srcp3_ystride;
uint32 fetch_plane;
@@ -387,6 +393,7 @@
ulong intr_dsi_err;
ulong kickoff_ov0;
ulong kickoff_ov1;
+ ulong kickoff_ov2;
ulong kickoff_dmap;
ulong kickoff_dmae;
ulong kickoff_dmas;
@@ -587,12 +594,13 @@
void mdp4_overlay_dmap_xy(struct mdp4_overlay_pipe *pipe);
void mdp4_overlay_dmae_cfg(struct msm_fb_data_type *mfd, int atv);
void mdp4_overlay_dmae_xy(struct mdp4_overlay_pipe *pipe);
-int mdp4_overlay_pipe_staged(int mixer);
+int mdp4_overlay_pipe_staged(struct mdp4_overlay_pipe *pipe);
void mdp4_lcdc_primary_vsyn(void);
void mdp4_overlay0_done_lcdc(int cndx);
-void mdp4_overlay0_done_mddi(struct mdp_dma_data *dma);
+void mdp4_overlay0_done_mddi(int cndx);
void mdp4_dma_p_done_mddi(struct mdp_dma_data *dma);
void mdp4_dmap_done_dsi_cmd(int cndx);
+void mdp4_dmap_done_mddi(int cndx);
void mdp4_dmap_done_dsi_video(int cndx);
void mdp4_dmap_done_lcdc(int cndx);
void mdp4_overlay1_done_dtv(void);
@@ -603,6 +611,7 @@
void mdp4_overlay_lcdc_vsync_push(struct msm_fb_data_type *mfd,
struct mdp4_overlay_pipe *pipe);
void mdp4_mddi_overlay_dmas_restore(void);
+void mdp4_dtv_set_avparams(struct mdp4_overlay_pipe *pipe, int id);
#ifndef CONFIG_FB_MSM_MIPI_DSI
void mdp4_mddi_dma_busy_wait(struct msm_fb_data_type *mfd);
@@ -666,6 +675,12 @@
void mdp4_dsi_video_overlay_blt(struct msm_fb_data_type *mfd,
struct msmfb_overlay_blt *req);
void mdp4_dsi_video_base_swap(int cndx, struct mdp4_overlay_pipe *pipe);
+static inline void mdp4_mddi_blt_start(struct msm_fb_data_type *mfd)
+{
+}
+static inline void mdp4_mddi_blt_stop(struct msm_fb_data_type *mfd)
+{
+}
#ifdef CONFIG_FB_MSM_MDP40
static inline void mdp3_dsi_cmd_dma_busy_wait(struct msm_fb_data_type *mfd)
@@ -674,6 +689,8 @@
}
#endif
#else /* CONFIG_FB_MSM_MIPI_DSI */
+void mdp4_mddi_blt_start(struct msm_fb_data_type *mfd);
+void mdp4_mddi_blt_stop(struct msm_fb_data_type *mfd);
int mdp4_mddi_overlay_blt_offset(struct msm_fb_data_type *mfd,
struct msmfb_overlay_blt *req);
void mdp4_mddi_overlay_blt(struct msm_fb_data_type *mfd,
@@ -681,6 +698,7 @@
int mdp4_mddi_overlay_blt_start(struct msm_fb_data_type *mfd);
int mdp4_mddi_overlay_blt_stop(struct msm_fb_data_type *mfd);
void mdp4_mddi_blt_dmap_busy_wait(struct msm_fb_data_type *mfd);
+void mdp4_mddi_rdptr_init(int cndx);
static inline int mdp4_dsi_overlay_blt_start(struct msm_fb_data_type *mfd)
{
return -ENODEV;
@@ -770,11 +788,37 @@
{
/* empty */
}
-#else /* CONFIG_FB_MSM_MIPI_DSI */
+#else /* CONFIG_FB_MSM_MDP303 */
void mdp4_dsi_cmd_del_timer(void);
+static inline int mdp4_mddi_on(struct platform_device *pdev)
+{
+ return 0;
+}
+static inline int mdp4_mddi_off(struct platform_device *pdev)
+{
+ return 0;
+}
+static inline void mdp4_mddi_wait4vsync(int cndx, long long *vtime)
+{
+}
+static inline void mdp4_mddi_vsync_ctrl(struct fb_info *info,
+ int enable)
+{
+}
+static inline void mdp4_mddi_pipe_queue(int cndx,
+ struct mdp4_overlay_pipe *pipe)
+{
+}
#endif
#else /* CONFIG_FB_MSM_MIPI_DSI */
+int mdp4_mddi_off(struct platform_device *pdev);
+int mdp4_mddi_on(struct platform_device *pdev);
+void mdp4_mddi_wait4vsync(int cndx, long long *vtime);
+void mdp4_mddi_vsync_ctrl(struct fb_info *info, int enable);
+void mdp4_mddi_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe);
+void mdp4_overlay_update_mddi(struct msm_fb_data_type *mfd);
+
static inline int mdp4_dsi_cmd_on(struct platform_device *pdev)
{
return 0;
@@ -878,7 +922,7 @@
int mdp4_overlay_writeback_on(struct platform_device *pdev);
int mdp4_overlay_writeback_off(struct platform_device *pdev);
void mdp4_writeback_overlay(struct msm_fb_data_type *mfd);
-void mdp4_overlay1_done_writeback(struct mdp_dma_data *dma);
+void mdp4_overlay2_done_wfd(struct mdp_dma_data *dma);
int mdp4_writeback_start(struct fb_info *info);
int mdp4_writeback_stop(struct fb_info *info);
@@ -928,19 +972,17 @@
u32 mdp4_get_mixer_num(u32 panel_type);
#ifndef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL
-static inline void mdp4_writeback_dma_busy_wait(struct msm_fb_data_type *mfd)
+static inline void mdp4_wfd_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe)
{
/* empty */
}
-static inline void mdp4_writeback_kickoff_video(struct msm_fb_data_type *mfd,
- struct mdp4_overlay_pipe *pipe)
+static inline void mdp4_wfd_init(int cndx)
{
/* empty */
}
#else
-void mdp4_writeback_dma_busy_wait(struct msm_fb_data_type *mfd);
-void mdp4_writeback_kickoff_video(struct msm_fb_data_type *mfd,
- struct mdp4_overlay_pipe *pipe);
+void mdp4_wfd_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe);
+void mdp4_wfd_init(int cndx);
#endif
#endif /* MDP_H */
diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c
index 2aafa6f..7c87c44 100644
--- a/drivers/video/msm/mdp4_overlay.c
+++ b/drivers/video/msm/mdp4_overlay.c
@@ -191,6 +191,19 @@
if (pipe == NULL)
return;
+ if (pipe->flags & MDP_MEMORY_ID_TYPE_FB) {
+ if (pipe->put0_need)
+ fput_light(pipe->srcp0_file, pipe->put0_need);
+ if (pipe->put1_need)
+ fput_light(pipe->srcp1_file, pipe->put1_need);
+ if (pipe->put2_need)
+ fput_light(pipe->srcp2_file, pipe->put2_need);
+
+ pr_debug("%s: ndx=%d flags=%x put=%d\n", __func__,
+ pipe->pipe_ndx, pipe->flags, pipe->put0_need);
+ return;
+ }
+
mutex_lock(&iommu_mutex);
mixer = pipe->mixer_num;
iom = &pipe->iommu;
@@ -1555,36 +1568,19 @@
mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
}
-int mdp4_overlay_pipe_staged(int mixer)
+int mdp4_overlay_pipe_staged(struct mdp4_overlay_pipe *pipe)
{
- uint32 data, mask, i, off;
- int p1, p2;
+ uint32 data, mask;
+ int mixer;
- if (mixer == MDP4_MIXER2)
- off = 0x100F0;
- else
- off = 0x10100;
+ mixer = pipe->mixer_num;
+ data = ctrl->mixer_cfg[mixer];
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
- data = inpdw(MDP_BASE + off);
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
- p1 = 0;
- p2 = 0;
- for (i = 0; i < 8; i++) {
- mask = data & 0x0f;
- if (mask) {
- if (mask <= 4)
- p1++;
- else
- p2++;
- }
- data >>= 4;
- }
+ mask = 0x0f;
+ mask <<= (4 * pipe->pipe_num);
+ data &= mask;
- if (mixer)
- return p2;
- else
- return p1;
+ return data;
}
int mdp4_mixer_info(int mixer_num, struct mdp_mixer_info *info)
@@ -1946,7 +1942,7 @@
struct mdp4_overlay_pipe *d_pipe;
struct mdp4_overlay_pipe *s_pipe;
struct blend_cfg *blend;
- int i, off, ptype, alpha_drop = 0;
+ int i, off, alpha_drop = 0;
int d_alpha, s_alpha;
unsigned char *overlay_base;
uint32 c0, c1, c2, base_premulti;
@@ -1976,7 +1972,8 @@
if (s_pipe->pipe_type == OVERLAY_TYPE_VIDEO &&
((s_pipe->op_mode & MDP4_OP_SCALEY_EN) ||
(s_pipe->op_mode & MDP4_OP_SCALEX_EN)) &&
- !(s_pipe->op_mode & MDP4_OP_SCALEY_PIXEL_RPT))
+ !(s_pipe->op_mode & (MDP4_OP_SCALEX_PIXEL_RPT |
+ MDP4_OP_SCALEY_PIXEL_RPT)))
alpha_drop = 1;
d_pipe = mdp4_background_layer(mixer, s_pipe);
@@ -1992,7 +1989,8 @@
blend->bg_alpha = 0x0ff - s_pipe->alpha;
blend->fg_alpha = s_pipe->alpha;
blend->co3_sel = 1; /* use fg alpha */
-
+ pr_debug("%s: bg alpha %d, fg alpha %d\n",
+ __func__, blend->bg_alpha, blend->fg_alpha);
if (s_pipe->is_fg) {
if (s_pipe->alpha == 0xff) {
blend->solidfill = 1;
@@ -2008,30 +2006,21 @@
MDP4_BLEND_FG_ALPHA_FG_PIXEL;
else
blend->fg_alpha = 0xff;
+ blend->op |= MDP4_BLEND_BG_INV_ALPHA;
} else
blend->op = MDP4_BLEND_BG_ALPHA_FG_CONST;
-
- blend->op |= MDP4_BLEND_BG_INV_ALPHA;
} else if (d_alpha) {
- ptype = mdp4_overlay_format2type(s_pipe->src_format);
- if (ptype == OVERLAY_TYPE_VIDEO) {
- blend->op = (MDP4_BLEND_FG_ALPHA_BG_PIXEL |
- MDP4_BLEND_FG_INV_ALPHA);
- if ((!(s_pipe->flags & MDP_BLEND_FG_PREMULT)) &&
- ((i != MDP4_MIXER_STAGE0) ||
- (!base_premulti)))
- blend->op |=
- MDP4_BLEND_BG_ALPHA_BG_PIXEL;
- else
- blend->fg_alpha = 0xff;
+ blend->op = (MDP4_BLEND_FG_ALPHA_BG_PIXEL |
+ MDP4_BLEND_FG_INV_ALPHA);
+ if ((!(d_pipe->flags & MDP_BLEND_FG_PREMULT)) &&
+ ((i != MDP4_MIXER_STAGE0) ||
+ (!base_premulti)))
+ blend->op |=
+ MDP4_BLEND_BG_ALPHA_BG_PIXEL;
+ else
+ blend->fg_alpha = 0xff;
- blend->co3_sel = 0; /* use bg alpha */
- } else {
- /* s_pipe is rgb without alpha */
- blend->op = (MDP4_BLEND_FG_ALPHA_FG_CONST |
- MDP4_BLEND_BG_ALPHA_BG_CONST);
- blend->bg_alpha = 0;
- }
+ blend->co3_sel = 0; /* use bg alpha */
}
if (s_pipe->transp != MDP_TRANSP_NOP) {
@@ -2428,6 +2417,11 @@
* zorder 2 == stage 2 == 4
*/
if (req->id == MSMFB_NEW_REQUEST) { /* new request */
+ if (mdp4_overlay_pipe_staged(pipe)) {
+ pr_err("%s: ndx=%d still staged\n", __func__,
+ pipe->pipe_ndx);
+ return -EPERM;
+ }
pipe->pipe_used++;
pipe->mixer_num = mixer;
pr_debug("%s: zorder=%d pipe ndx=%d num=%d\n", __func__,
@@ -2863,6 +2857,8 @@
mdp4_dsi_video_blt_start(mfd);
else if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD)
mdp4_dsi_cmd_blt_start(mfd);
+ else if (ctrl->panel_mode & MDP4_PANEL_MDDI)
+ mdp4_mddi_blt_start(mfd);
pr_info("%s mixer0 start blt [%d] from %d to %d.\n",
__func__,
flag,
@@ -2911,6 +2907,8 @@
mdp4_dsi_video_blt_stop(mfd);
else if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD)
mdp4_dsi_cmd_blt_stop(mfd);
+ else if (ctrl->panel_mode & MDP4_PANEL_MDDI)
+ mdp4_mddi_blt_stop(mfd);
pr_info("%s mixer0 stop blt [%d] from %d to %d.\n",
__func__,
flag,
@@ -2955,14 +2953,13 @@
if (file == NULL)
return -EINVAL;
+ pipe->flags |= MDP_MEMORY_ID_TYPE_FB;
if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) {
fb_num = MINOR(file->f_dentry->d_inode->i_rdev);
if (get_fb_phys_info(start, len, fb_num,
DISPLAY_SUBSYSTEM_ID)) {
ret = -1;
} else {
- pr_warn("%s: mdp4_overlay play with FB memory\n",
- __func__);
*srcp_file = file;
*p_need = put_needed;
}
@@ -3176,20 +3173,14 @@
else {
/* mixer 0 */
ctrl->mixer0_played = 0;
- if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
- if (mfd->panel_power_on)
- mdp4_mddi_blt_dmap_busy_wait(mfd);
- }
+
}
mdp4_overlay_reg_flush(pipe, 1);
mdp4_mixer_stage_down(pipe, 0);
if (pipe->mixer_num == MDP4_MIXER0) {
- if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
- if (mfd->panel_power_on)
- mdp4_mddi_overlay_restore();
- }
+
} else { /* mixer1, DTV, ATV */
if (ctrl->panel_mode & MDP4_PANEL_DTV)
mdp4_overlay_dtv_unset(mfd, pipe);
@@ -3213,6 +3204,8 @@
mdp4_dsi_cmd_wait4vsync(0, vtime);
else if (ctrl->panel_mode & MDP4_PANEL_LCDC)
mdp4_lcdc_wait4vsync(0, vtime);
+ else if (ctrl->panel_mode & MDP4_PANEL_MDDI)
+ mdp4_mddi_wait4vsync(0, vtime);
} else if (hdmi_prim_display || info->node == 1) {
mdp4_dtv_wait4vsync(0, vtime);
}
@@ -3236,6 +3229,8 @@
mdp4_dsi_cmd_vsync_ctrl(info, cmd);
else if (ctrl->panel_mode & MDP4_PANEL_LCDC)
mdp4_lcdc_vsync_ctrl(info, cmd);
+ else if (ctrl->panel_mode & MDP4_PANEL_MDDI)
+ mdp4_mddi_vsync_ctrl(info, cmd);
} else if (hdmi_prim_display || info->node == 1)
mdp4_dtv_vsync_ctrl(info, cmd);
@@ -3320,11 +3315,8 @@
struct mdp4_overlay_pipe *pipe;
ulong start, addr;
ulong len = 0;
- struct file *srcp0_file = NULL;
- struct file *srcp1_file = NULL, *srcp2_file = NULL;
struct ion_handle *srcp0_ihdl = NULL;
struct ion_handle *srcp1_ihdl = NULL, *srcp2_ihdl = NULL;
- int ps0_need, p_need;
uint32_t overlay_version = 0;
int ret = 0;
@@ -3349,8 +3341,8 @@
mutex_lock(&mfd->dma->ov_mutex);
img = &req->data;
- get_img(img, info, pipe, 0, &start, &len, &srcp0_file,
- &ps0_need, &srcp0_ihdl);
+ get_img(img, info, pipe, 0, &start, &len, &pipe->srcp0_file,
+ &pipe->put0_need, &srcp0_ihdl);
if (len == 0) {
pr_err("%s: pmem Error\n", __func__);
ret = -1;
@@ -3372,8 +3364,9 @@
if (pipe->fetch_plane == OVERLAY_PLANE_PSEUDO_PLANAR) {
if (overlay_version > 0) {
img = &req->plane1_data;
- get_img(img, info, pipe, 1, &start, &len, &srcp1_file,
- &p_need, &srcp1_ihdl);
+ get_img(img, info, pipe, 1, &start, &len,
+ &pipe->srcp1_file, &pipe->put1_need,
+ &srcp1_ihdl);
if (len == 0) {
pr_err("%s: Error to get plane1\n", __func__);
ret = -EINVAL;
@@ -3404,8 +3397,9 @@
} else if (pipe->fetch_plane == OVERLAY_PLANE_PLANAR) {
if (overlay_version > 0) {
img = &req->plane1_data;
- get_img(img, info, pipe, 1, &start, &len, &srcp1_file,
- &p_need, &srcp1_ihdl);
+ get_img(img, info, pipe, 1, &start, &len,
+ &pipe->srcp1_file, &pipe->put1_need,
+ &srcp1_ihdl);
if (len == 0) {
pr_err("%s: Error to get plane1\n", __func__);
ret = -EINVAL;
@@ -3414,8 +3408,9 @@
pipe->srcp1_addr = start + img->offset;
img = &req->plane2_data;
- get_img(img, info, pipe, 2, &start, &len, &srcp2_file,
- &p_need, &srcp2_ihdl);
+ get_img(img, info, pipe, 2, &start, &len,
+ &pipe->srcp2_file, &pipe->put2_need,
+ &srcp2_ihdl);
if (len == 0) {
pr_err("%s: Error to get plane2\n", __func__);
ret = -EINVAL;
@@ -3456,10 +3451,6 @@
mdp4_overlay_mdp_perf_req(mfd, ctrl->plist);
- if (pipe->mixer_num == MDP4_MIXER2 ||
- ctrl->panel_mode & MDP4_PANEL_MDDI)
- goto mddi;
-
if (pipe->mixer_num == MDP4_MIXER0) {
if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) {
/* cndx = 0 */
@@ -3471,58 +3462,27 @@
/* cndx = 0 */
mdp4_lcdc_pipe_queue(0, pipe);
}
+ if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
+ /* cndx = 0 */
+ mdp4_mddi_pipe_queue(0, pipe);
+ }
} else if (pipe->mixer_num == MDP4_MIXER1) {
- if (ctrl->panel_mode & MDP4_PANEL_DTV)
+ if (ctrl->panel_mode & MDP4_PANEL_DTV) {
mdp4_dtv_pipe_queue(0, pipe);/* cndx = 0 */
+ mdp4_dtv_set_avparams(pipe, img->memory_id);
+ }
+ } else if (pipe->mixer_num == MDP4_MIXER2) {
+ ctrl->mixer2_played++;
+ if (ctrl->panel_mode & MDP4_PANEL_WRITEBACK)
+ mdp4_wfd_pipe_queue(0, pipe);/* cndx = 0 */
}
mutex_unlock(&mfd->dma->ov_mutex);
return ret;
-mddi:
- if (pipe->pipe_type == OVERLAY_TYPE_VIDEO) {
- mdp4_overlay_vg_setup(pipe); /* video/graphic pipe */
- } else {
- mdp4_overlay_rgb_setup(pipe); /* rgb pipe */
- }
-
- mdp4_mixer_stage_up(pipe, 0);
-
- if (pipe->mixer_num == MDP4_MIXER2) {
- ctrl->mixer2_played++;
- if (ctrl->panel_mode & MDP4_PANEL_WRITEBACK) {
- mdp4_writeback_dma_busy_wait(mfd);
- mdp4_writeback_kickoff_video(mfd, pipe);
- }
- } else if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
- if (pipe->flags & MDP_OV_PLAY_NOWAIT) {
- mdp4_stat.overlay_play[pipe->mixer_num]++;
- mutex_unlock(&mfd->dma->ov_mutex);
- goto end;
- }
- mdp4_mixer_stage_commit(pipe->mixer_num);
- mdp4_mddi_dma_busy_wait(mfd);
- mdp4_mddi_kickoff_video(mfd, pipe);
- }
-
- if (!(pipe->flags & MDP_OV_PLAY_NOWAIT))
- mdp4_iommu_unmap(pipe);
- mdp4_stat.overlay_play[pipe->mixer_num]++;
-
end:
mutex_unlock(&mfd->dma->ov_mutex);
-#ifdef CONFIG_ANDROID_PMEM
- if (srcp0_file)
- put_pmem_file(srcp0_file);
- if (srcp1_file)
- put_pmem_file(srcp1_file);
- if (srcp2_file)
- put_pmem_file(srcp2_file);
-#endif
- /* only source may use frame buffer */
- if (img->flags & MDP_MEMORY_ID_TYPE_FB)
- fput_light(srcp0_file, ps0_need);
return ret;
}
diff --git a/drivers/video/msm/mdp4_overlay_dsi_cmd.c b/drivers/video/msm/mdp4_overlay_dsi_cmd.c
index a937734..c5442a7 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_cmd.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_cmd.c
@@ -516,9 +516,9 @@
vctrl = &vsync_ctrl_db[cndx];
pr_debug("%s: ISR, cpu=%d\n", __func__, smp_processor_id());
vctrl->rdptr_intr_tot++;
- vctrl->vsync_time = ktime_get();
spin_lock(&vctrl->spin_lock);
+ vctrl->vsync_time = ktime_get();
complete_all(&vctrl->vsync_comp);
vctrl->wait_vsync_cnt = 0;
@@ -539,6 +539,8 @@
vctrl = &vsync_ctrl_db[cndx];
pipe = vctrl->base_pipe;
+ if (pipe == NULL)
+ return;
/* blt enabled */
spin_lock(&vctrl->spin_lock);
@@ -578,6 +580,8 @@
vctrl = &vsync_ctrl_db[cndx];
pipe = vctrl->base_pipe;
+ if (pipe == NULL)
+ return;
spin_lock(&vctrl->spin_lock);
vsync_irq_disable(INTR_OVERLAY0_DONE, MDP_OVERLAY0_TERM);
@@ -643,6 +647,7 @@
struct vsycn_ctrl *vctrl;
ssize_t ret = 0;
unsigned long flags;
+ u64 vsync_tick;
cndx = 0;
vctrl = &vsync_ctrl_db[0];
@@ -657,10 +662,15 @@
vctrl->wait_vsync_cnt++;
spin_unlock_irqrestore(&vctrl->spin_lock, flags);
- wait_for_completion(&vctrl->vsync_comp);
+ ret = wait_for_completion_interruptible(&vctrl->vsync_comp);
+ if (ret)
+ return ret;
- ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu",
- ktime_to_ns(vctrl->vsync_time));
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ vsync_tick = ktime_to_ns(vctrl->vsync_time);
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+ ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_tick);
buf[strlen(buf) + 1] = '\0';
return ret;
}
@@ -1022,6 +1032,8 @@
struct msm_fb_data_type *mfd;
struct vsycn_ctrl *vctrl;
struct mdp4_overlay_pipe *pipe;
+ struct vsync_update *vp;
+ int undx;
pr_debug("%s+:\n", __func__);
@@ -1054,6 +1066,16 @@
mdp_clk_ctrl(0);
}
+ undx = vctrl->update_ndx;
+ vp = &vctrl->vlist[undx];
+ if (vp->update_cnt) {
+ /*
+ * pipe's iommu will be freed at next overlay play
+ * and iommu_drop statistic will be increased by one
+ */
+ vp->update_cnt = 0; /* empty queue */
+ }
+
vctrl->clk_enabled = 0;
vctrl->vsync_enabled = 0;
vctrl->clk_control = 0;
@@ -1107,14 +1129,18 @@
unsigned long flags;
long long tick;
+ mutex_lock(&mfd->dma->ov_mutex);
vctrl = &vsync_ctrl_db[cndx];
- if (!mfd->panel_power_on)
+ if (!mfd->panel_power_on) {
+ mutex_unlock(&mfd->dma->ov_mutex);
return;
+ }
pipe = vctrl->base_pipe;
if (pipe == NULL) {
pr_err("%s: NO base pipe\n", __func__);
+ mutex_unlock(&mfd->dma->ov_mutex);
return;
}
@@ -1122,6 +1148,7 @@
if (!vctrl->clk_enabled) {
pr_err("%s: mdp clocks disabled\n", __func__);
mutex_unlock(&vctrl->update_lock);
+ mutex_unlock(&mfd->dma->ov_mutex);
return;
}
@@ -1144,12 +1171,9 @@
}
mdp4_overlay_mdp_perf_upd(mfd, 1);
-
- mutex_lock(&mfd->dma->ov_mutex);
mdp4_dsi_cmd_pipe_commit(cndx, 0);
+ mdp4_dsi_cmd_wait4vsync(cndx, &tick);
+ mdp4_overlay_mdp_perf_upd(mfd, 0);
mutex_unlock(&mfd->dma->ov_mutex);
- mdp4_dsi_cmd_wait4vsync(cndx, &tick);
-
- mdp4_overlay_mdp_perf_upd(mfd, 0);
}
diff --git a/drivers/video/msm/mdp4_overlay_dsi_video.c b/drivers/video/msm/mdp4_overlay_dsi_video.c
index 736a353..a83c340 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_video.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_video.c
@@ -194,8 +194,6 @@
}
spin_unlock_irqrestore(&vctrl->spin_lock, flags);
- mdp4_overlay_mdp_perf_upd(vctrl->mfd, 1);
-
if (vctrl->blt_change) {
pipe = vctrl->base_pipe;
spin_lock_irqsave(&vctrl->spin_lock, flags);
@@ -382,6 +380,7 @@
struct vsycn_ctrl *vctrl;
ssize_t ret = 0;
unsigned long flags;
+ u64 vsync_tick;
cndx = 0;
vctrl = &vsync_ctrl_db[0];
@@ -395,10 +394,15 @@
INIT_COMPLETION(vctrl->vsync_comp);
vctrl->wait_vsync_cnt++;
spin_unlock_irqrestore(&vctrl->spin_lock, flags);
- wait_for_completion(&vctrl->vsync_comp);
+ ret = wait_for_completion_interruptible(&vctrl->vsync_comp);
+ if (ret)
+ return ret;
- ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu",
- ktime_to_ns(vctrl->vsync_time));
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ vsync_tick = ktime_to_ns(vctrl->vsync_time);
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+ ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_tick);
buf[strlen(buf) + 1] = '\0';
return ret;
}
@@ -689,8 +693,9 @@
struct msm_fb_data_type *mfd;
struct vsycn_ctrl *vctrl;
struct mdp4_overlay_pipe *pipe;
+ struct vsync_update *vp;
unsigned long flags;
- int need_wait = 0;
+ int undx, need_wait = 0;
mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
vctrl = &vsync_ctrl_db[cndx];
@@ -723,6 +728,16 @@
vsync_irq_disable(INTR_PRIMARY_VSYNC, MDP_PRIM_VSYNC_TERM);
}
+ undx = vctrl->update_ndx;
+ vp = &vctrl->vlist[undx];
+ if (vp->update_cnt) {
+ /*
+ * pipe's iommu will be freed at next overlay play
+ * and iommu_drop statistic will be increased by one
+ */
+ vp->update_cnt = 0; /* empty queue */
+ }
+
if (pipe) {
/* sanity check, free pipes besides base layer */
mdp4_overlay_unset_mixer(pipe->mixer_num);
@@ -907,9 +922,10 @@
cndx = 0;
vctrl = &vsync_ctrl_db[cndx];
pr_debug("%s: cpu=%d\n", __func__, smp_processor_id());
- vctrl->vsync_time = ktime_get();
spin_lock(&vctrl->spin_lock);
+ vctrl->vsync_time = ktime_get();
+
if (vctrl->wait_vsync_cnt) {
complete_all(&vctrl->vsync_comp);
vctrl->wait_vsync_cnt = 0;
@@ -931,6 +947,8 @@
}
vctrl = &vsync_ctrl_db[cndx];
pipe = vctrl->base_pipe;
+ if (pipe == NULL)
+ return;
spin_lock(&vctrl->spin_lock);
vsync_irq_disable(INTR_DMA_P_DONE, MDP_DMAP_TERM);
@@ -966,6 +984,8 @@
vctrl = &vsync_ctrl_db[cndx];
pipe = vctrl->base_pipe;
+ if (pipe == NULL)
+ return;
spin_lock(&vctrl->spin_lock);
vsync_irq_disable(INTR_OVERLAY0_DONE, MDP_OVERLAY0_TERM);
@@ -1057,11 +1077,15 @@
struct vsycn_ctrl *vctrl;
struct mdp4_overlay_pipe *pipe;
+ mutex_lock(&mfd->dma->ov_mutex);
+
vctrl = &vsync_ctrl_db[cndx];
pipe = vctrl->base_pipe;
- if (!pipe || !mfd->panel_power_on)
+ if (!pipe || !mfd->panel_power_on) {
+ mutex_unlock(&mfd->dma->ov_mutex);
return;
+ }
pr_debug("%s: cpu=%d pid=%d\n", __func__,
smp_processor_id(), current->pid);
@@ -1080,10 +1104,7 @@
mdp_update_pm(mfd, vsync_ctrl_db[0].vsync_time);
mdp4_overlay_mdp_perf_upd(mfd, 1);
-
- mutex_lock(&mfd->dma->ov_mutex);
mdp4_dsi_video_pipe_commit(cndx, 0);
- mutex_unlock(&mfd->dma->ov_mutex);
if (pipe->ov_blt_addr)
mdp4_dsi_video_wait4ov(cndx);
@@ -1091,5 +1112,6 @@
mdp4_dsi_video_wait4dmap(cndx);
mdp4_overlay_mdp_perf_upd(mfd, 0);
+ mutex_unlock(&mfd->dma->ov_mutex);
}
diff --git a/drivers/video/msm/mdp4_overlay_dtv.c b/drivers/video/msm/mdp4_overlay_dtv.c
index c70f83d..67690cf 100644
--- a/drivers/video/msm/mdp4_overlay_dtv.c
+++ b/drivers/video/msm/mdp4_overlay_dtv.c
@@ -33,7 +33,6 @@
#include "mdp4.h"
#define DTV_BASE 0xD0000
-
static int dtv_enabled;
/*#define DEBUG*/
@@ -55,14 +54,6 @@
static int first_pixel_start_x;
static int first_pixel_start_y;
-void mdp4_dtv_base_swap(int cndx, struct mdp4_overlay_pipe *pipe)
-{
-#ifdef BYPASS4
- if (hdmi_prim_display)
- dtv_pipe = pipe;
-#endif
-}
-
#define MAX_CONTROLLER 1
static struct vsycn_ctrl {
@@ -85,6 +76,10 @@
struct vsync_update vlist[2];
int vsync_irq_enabled;
ktime_t vsync_time;
+ uint32 *avtimer;
+ int vg1fd;
+ int vg2fd;
+ unsigned long long avtimer_tick;
} vsync_ctrl_db[MAX_CONTROLLER];
static void vsync_irq_enable(int intr, int term)
@@ -322,9 +317,14 @@
struct vsycn_ctrl *vctrl;
ssize_t ret = 0;
unsigned long flags;
+ char ch = '\0';
+ int vg1fd = -1, vg2fd = -1;
+ unsigned long long avtimer_tick = 0;
+ u64 vsync_tick = 0;
cndx = 0;
vctrl = &vsync_ctrl_db[0];
+ memset(buf, 0, 64);
if (atomic_read(&vctrl->suspend) > 0 ||
!external_common_state->hpd_state ||
@@ -336,13 +336,31 @@
INIT_COMPLETION(vctrl->vsync_comp);
vctrl->wait_vsync_cnt++;
spin_unlock_irqrestore(&vctrl->spin_lock, flags);
- wait_for_completion(&vctrl->vsync_comp);
- ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu",
- ktime_to_ns(vctrl->vsync_time));
- buf[strlen(buf) + 1] = '\0';
+ ret = wait_for_completion_interruptible(&vctrl->vsync_comp);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ vg1fd = vctrl->vg1fd;
+ vg2fd = vctrl->vg2fd;
+ avtimer_tick = vctrl->avtimer_tick;
+ vsync_tick = ktime_to_ns(vctrl->vsync_time);
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+ ret = snprintf(buf, PAGE_SIZE,
+ "VSYNC=%llu%c"
+ "AVSYNCTP=%llu%c"
+ "VG1MEMID=%d%c"
+ "VG2MEMID=%d",
+ vsync_tick,
+ ch, avtimer_tick,
+ ch, vg1fd,
+ ch, vg2fd);
+
return ret;
}
+
void mdp4_dtv_vsync_init(int cndx)
{
struct vsycn_ctrl *vctrl;
@@ -369,6 +387,24 @@
spin_lock_init(&vctrl->spin_lock);
}
+void mdp4_dtv_base_swap(int cndx, struct mdp4_overlay_pipe *pipe)
+{
+ struct vsycn_ctrl *vctrl;
+
+ if (!hdmi_prim_display) {
+ pr_err("%s: failed, hdmi is not primary\n", __func__);
+ return;
+ }
+
+ if (cndx >= MAX_CONTROLLER) {
+ pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+ return;
+ }
+
+ vctrl = &vsync_ctrl_db[cndx];
+ vctrl->base_pipe = pipe;
+}
+
static int mdp4_dtv_start(struct msm_fb_data_type *mfd)
{
int dtv_width;
@@ -587,6 +623,13 @@
pr_debug("%s: kobject_uevent(KOBJ_ADD)\n", __func__);
vctrl->sysfs_created = 1;
}
+
+ if (mfd->avtimer_phy && (vctrl->avtimer == NULL)) {
+ vctrl->avtimer = (uint32 *)ioremap(mfd->avtimer_phy, 8);
+ if (vctrl->avtimer == NULL)
+ pr_err(" avtimer ioremap fail\n");
+ }
+
pr_info("%s:\n", __func__);
return ret;
@@ -597,8 +640,10 @@
struct msm_fb_data_type *mfd;
int ret = 0;
int cndx = 0;
+ int undx;
struct vsycn_ctrl *vctrl;
struct mdp4_overlay_pipe *pipe;
+ struct vsync_update *vp;
mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
@@ -644,6 +689,21 @@
vsync_irq_disable(INTR_PRIMARY_VSYNC, MDP_PRIM_VSYNC_TERM);
}
+ undx = vctrl->update_ndx;
+ vp = &vctrl->vlist[undx];
+ if (vp->update_cnt) {
+ /*
+ * pipe's iommu will be freed at next overlay play
+ * and iommu_drop statistic will be increased by one
+ */
+ vp->update_cnt = 0; /* empty queue */
+ }
+
+ if (vctrl->avtimer != NULL) {
+ iounmap(vctrl->avtimer);
+ vctrl->avtimer = NULL;
+ }
+
ret = panel_next_off(pdev);
mdp_footswitch_ctrl(FALSE);
@@ -822,7 +882,7 @@
struct vsycn_ctrl *vctrl;
vctrl = &vsync_ctrl_db[cndx];
- if (vctrl->base_pipe != NULL)
+ if (vctrl->base_pipe == NULL)
return 0;
if (pipe->mixer_stage == MDP4_MIXER_STAGE_BASE &&
@@ -830,6 +890,12 @@
result = mdp4_dtv_stop(mfd);
vctrl->base_pipe = NULL;
}
+
+ if (pipe->pipe_num == OVERLAY_PIPE_VG1)
+ vctrl->vg1fd = -1;
+ else if (pipe->pipe_num == OVERLAY_PIPE_VG2)
+ vctrl->vg2fd = -1;
+
return result;
}
@@ -839,13 +905,24 @@
{
int cndx;
struct vsycn_ctrl *vctrl;
+ uint32 *tp, LSW;
cndx = 0;
vctrl = &vsync_ctrl_db[cndx];
pr_debug("%s: cpu=%d\n", __func__, smp_processor_id());
- vctrl->vsync_time = ktime_get();
spin_lock(&vctrl->spin_lock);
+ vctrl->vsync_time = ktime_get();
+ vctrl->avtimer_tick = 0;
+
+ if (vctrl->avtimer && ((vctrl->vg1fd > 0) || (vctrl->vg2fd > 0))) {
+ tp = vctrl->avtimer;
+ LSW = inpdw(tp);
+ tp++;
+ vctrl->avtimer_tick = (unsigned long long) inpdw(tp);
+ vctrl->avtimer_tick = ((vctrl->avtimer_tick << 32) | LSW);
+ }
+
if (vctrl->wait_vsync_cnt) {
complete_all(&vctrl->vsync_comp);
vctrl->wait_vsync_cnt = 0;
@@ -870,6 +947,8 @@
vctrl = &vsync_ctrl_db[cndx];
pipe = vctrl->base_pipe;
+ if (pipe == NULL)
+ return;
pr_debug("%s: cpu=%d\n", __func__, smp_processor_id());
spin_lock(&vctrl->spin_lock);
@@ -910,6 +989,8 @@
vctrl = &vsync_ctrl_db[cndx];
pipe = vctrl->base_pipe;
+ if (pipe == NULL)
+ return;
spin_lock(&vctrl->spin_lock);
if (pipe->ov_blt_addr == 0) {
@@ -1034,8 +1115,11 @@
struct vsycn_ctrl *vctrl;
struct mdp4_overlay_pipe *pipe;
- if (!mfd->panel_power_on)
+ mutex_lock(&mfd->dma->ov_mutex);
+ if (!mfd->panel_power_on) {
+ mutex_unlock(&mfd->dma->ov_mutex);
return;
+ }
vctrl = &vsync_ctrl_db[cndx];
if (vctrl->base_pipe == NULL)
@@ -1045,6 +1129,7 @@
if (pipe == NULL) {
pr_warn("%s: dtv_pipe == NULL\n", __func__);
+ mutex_unlock(&mfd->dma->ov_mutex);
return;
}
@@ -1060,10 +1145,24 @@
mdp4_dtv_pipe_queue(0, pipe);
}
mdp_update_pm(mfd, vsync_ctrl_db[0].vsync_time);
-
- mutex_lock(&mfd->dma->ov_mutex);
mdp4_overlay_mdp_perf_upd(mfd, 1);
mdp4_dtv_pipe_commit(cndx, 0);
mdp4_overlay_mdp_perf_upd(mfd, 0);
mutex_unlock(&mfd->dma->ov_mutex);
}
+
+void mdp4_dtv_set_avparams(struct mdp4_overlay_pipe *pipe, int id)
+{
+ struct vsycn_ctrl *vctrl;
+
+ if (pipe == NULL) {
+ pr_warn("%s: dtv_pipe == NULL\n", __func__);
+ return;
+ }
+ vctrl = &vsync_ctrl_db[0];
+ if (pipe->pipe_num == OVERLAY_PIPE_VG1)
+ vctrl->vg1fd = id;
+ else if (pipe->pipe_num == OVERLAY_PIPE_VG2)
+ vctrl->vg2fd = id;
+}
+
diff --git a/drivers/video/msm/mdp4_overlay_lcdc.c b/drivers/video/msm/mdp4_overlay_lcdc.c
index a5ce869..9e0c411 100644
--- a/drivers/video/msm/mdp4_overlay_lcdc.c
+++ b/drivers/video/msm/mdp4_overlay_lcdc.c
@@ -366,6 +366,7 @@
struct vsycn_ctrl *vctrl;
ssize_t ret = 0;
unsigned long flags;
+ u64 vsync_tick;
cndx = 0;
vctrl = &vsync_ctrl_db[0];
@@ -379,10 +380,15 @@
INIT_COMPLETION(vctrl->vsync_comp);
vctrl->wait_vsync_cnt++;
spin_unlock_irqrestore(&vctrl->spin_lock, flags);
- wait_for_completion(&vctrl->vsync_comp);
+ ret = wait_for_completion_interruptible(&vctrl->vsync_comp);
+ if (ret)
+ return ret;
- ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu",
- ktime_to_ns(vctrl->vsync_time));
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ vsync_tick = ktime_to_ns(vctrl->vsync_time);
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+ ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_tick);
buf[strlen(buf) + 1] = '\0';
return ret;
}
@@ -673,8 +679,9 @@
struct msm_fb_data_type *mfd;
struct vsycn_ctrl *vctrl;
struct mdp4_overlay_pipe *pipe;
+ struct vsync_update *vp;
unsigned long flags;
- int need_wait = 0;
+ int undx, need_wait = 0;
mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
vctrl = &vsync_ctrl_db[cndx];
@@ -707,6 +714,16 @@
vsync_irq_disable(INTR_PRIMARY_VSYNC, MDP_PRIM_VSYNC_TERM);
}
+ undx = vctrl->update_ndx;
+ vp = &vctrl->vlist[undx];
+ if (vp->update_cnt) {
+ /*
+ * pipe's iommu will be freed at next overlay play
+ * and iommu_drop statistic will be increased by one
+ */
+ vp->update_cnt = 0; /* empty queue */
+ }
+
if (pipe) {
/* sanity check, free pipes besides base layer */
mdp4_overlay_unset_mixer(pipe->mixer_num);
@@ -793,9 +810,10 @@
cndx = 0;
vctrl = &vsync_ctrl_db[cndx];
pr_debug("%s: cpu=%d\n", __func__, smp_processor_id());
- vctrl->vsync_time = ktime_get();
spin_lock(&vctrl->spin_lock);
+ vctrl->vsync_time = ktime_get();
+
if (vctrl->wait_vsync_cnt) {
complete_all(&vctrl->vsync_comp);
vctrl->wait_vsync_cnt = 0;
@@ -818,6 +836,8 @@
}
vctrl = &vsync_ctrl_db[cndx];
pipe = vctrl->base_pipe;
+ if (pipe == NULL)
+ return;
spin_lock(&vctrl->spin_lock);
vsync_irq_disable(INTR_DMA_P_DONE, MDP_DMAP_TERM);
@@ -856,6 +876,8 @@
vctrl = &vsync_ctrl_db[cndx];
pipe = vctrl->base_pipe;
+ if (pipe == NULL)
+ return;
spin_lock(&vctrl->spin_lock);
vsync_irq_disable(INTR_OVERLAY0_DONE, MDP_OVERLAY0_TERM);
@@ -943,12 +965,15 @@
struct vsycn_ctrl *vctrl;
struct mdp4_overlay_pipe *pipe;
+ mutex_lock(&mfd->dma->ov_mutex);
vctrl = &vsync_ctrl_db[cndx];
pipe = vctrl->base_pipe;
- if (!pipe || !mfd->panel_power_on)
+ if (!pipe || !mfd->panel_power_on) {
+ mutex_unlock(&mfd->dma->ov_mutex);
return;
+ }
pr_debug("%s: cpu=%d pid=%d\n", __func__,
smp_processor_id(), current->pid);
@@ -968,9 +993,8 @@
mdp4_overlay_mdp_perf_upd(mfd, 1);
- mutex_lock(&mfd->dma->ov_mutex);
+
mdp4_lcdc_pipe_commit(cndx, 0);
- mutex_unlock(&mfd->dma->ov_mutex);
if (pipe->ov_blt_addr)
mdp4_lcdc_wait4ov(cndx);
@@ -978,4 +1002,5 @@
mdp4_lcdc_wait4dmap(cndx);
mdp4_overlay_mdp_perf_upd(mfd, 0);
+ mutex_unlock(&mfd->dma->ov_mutex);
}
diff --git a/drivers/video/msm/mdp4_overlay_mddi.c b/drivers/video/msm/mdp4_overlay_mddi.c
index be4a89a..ca84eca 100644
--- a/drivers/video/msm/mdp4_overlay_mddi.c
+++ b/drivers/video/msm/mdp4_overlay_mddi.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-2012, 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,38 +17,680 @@
#include <linux/time.h>
#include <linux/init.h>
#include <linux/interrupt.h>
-#include <linux/hrtimer.h>
#include <linux/delay.h>
-#include <mach/hardware.h>
#include <linux/io.h>
-
-#include <asm/system.h>
-#include <asm/mach-types.h>
#include <linux/semaphore.h>
#include <linux/spinlock.h>
-
#include <linux/fb.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
#include "mdp.h"
#include "msm_fb.h"
#include "mdp4.h"
-static struct mdp4_overlay_pipe *mddi_pipe;
-static struct msm_fb_data_type *mddi_mfd;
-static int busy_wait_cnt;
+static int mddi_state;
+
+#define TOUT_PERIOD HZ /* 1 second */
+#define MS_100 (HZ/10) /* 100 ms */
static int vsync_start_y_adjust = 4;
-static int dmap_vsync_enable;
+#define MAX_CONTROLLER 1
+#define VSYNC_EXPIRE_TICK 8
-void mdp_dmap_vsync_set(int enable)
+static struct vsycn_ctrl {
+ struct device *dev;
+ int inited;
+ int update_ndx;
+ int expire_tick;
+ int blt_wait;
+ u32 ov_koff;
+ u32 ov_done;
+ u32 dmap_koff;
+ u32 dmap_done;
+ uint32 rdptr_intr_tot;
+ uint32 rdptr_sirq_tot;
+ atomic_t suspend;
+ int wait_vsync_cnt;
+ int blt_change;
+ int blt_free;
+ int blt_end;
+ int uevent;
+ struct mutex update_lock;
+ struct completion ov_comp;
+ struct completion dmap_comp;
+ struct completion vsync_comp;
+ spinlock_t spin_lock;
+ struct msm_fb_data_type *mfd;
+ struct mdp4_overlay_pipe *base_pipe;
+ struct vsync_update vlist[2];
+ int vsync_enabled;
+ int clk_enabled;
+ int clk_control;
+ int new_update;
+ ktime_t vsync_time;
+ struct work_struct vsync_work;
+ struct work_struct clk_work;
+} vsync_ctrl_db[MAX_CONTROLLER];
+
+static void vsync_irq_enable(int intr, int term)
{
- dmap_vsync_enable = enable;
+ unsigned long flag;
+
+ spin_lock_irqsave(&mdp_spin_lock, flag);
+ /* no need to clear other interrupts for comamnd mode */
+ mdp_intr_mask |= intr;
+ outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+ mdp_enable_irq(term);
+ spin_unlock_irqrestore(&mdp_spin_lock, flag);
}
-int mdp_dmap_vsync_get(void)
+static void vsync_irq_disable(int intr, int term)
{
- return dmap_vsync_enable;
+ unsigned long flag;
+
+ spin_lock_irqsave(&mdp_spin_lock, flag);
+ /* no need to clrear other interrupts for comamnd mode */
+ mdp_intr_mask &= ~intr;
+ outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+ mdp_disable_irq_nosync(term);
+ spin_unlock_irqrestore(&mdp_spin_lock, flag);
+}
+
+static void mdp4_mddi_blt_ov_update(struct mdp4_overlay_pipe *pipe)
+{
+ uint32 off, addr;
+ int bpp;
+ char *overlay_base;
+
+ if (pipe->ov_blt_addr == 0)
+ return;
+
+ bpp = 3; /* overlay ouput is RGB888 */
+ off = 0;
+ if (pipe->ov_cnt & 0x01)
+ off = pipe->src_height * pipe->src_width * bpp;
+ addr = pipe->ov_blt_addr + off;
+ /* overlay 0 */
+ overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */
+ outpdw(overlay_base + 0x000c, addr);
+ outpdw(overlay_base + 0x001c, addr);
+}
+
+static void mdp4_mddi_blt_dmap_update(struct mdp4_overlay_pipe *pipe)
+{
+ uint32 off, addr;
+ int bpp;
+
+ if (pipe->ov_blt_addr == 0)
+ return;
+
+ bpp = 3; /* overlay ouput is RGB888 */
+ off = 0;
+ if (pipe->dmap_cnt & 0x01)
+ off = pipe->src_height * pipe->src_width * bpp;
+ addr = pipe->dma_blt_addr + off;
+
+ /* dmap */
+ MDP_OUTP(MDP_BASE + 0x90008, addr);
+}
+
+static void mdp4_mddi_wait4dmap(int cndx);
+static void mdp4_mddi_wait4ov(int cndx);
+
+static void mdp4_mddi_do_blt(struct msm_fb_data_type *mfd, int enable)
+{
+ unsigned long flags;
+ int cndx = 0;
+ struct vsycn_ctrl *vctrl;
+ struct mdp4_overlay_pipe *pipe;
+ int need_wait;
+
+ vctrl = &vsync_ctrl_db[cndx];
+ pipe = vctrl->base_pipe;
+
+ mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0);
+
+ if (mfd->ov0_wb_buf->write_addr == 0) {
+ pr_err("%s: no blt_base assigned\n", __func__);
+ return;
+ }
+
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ if (enable && pipe->ov_blt_addr == 0) {
+ vctrl->blt_change++;
+ if (vctrl->dmap_koff != vctrl->dmap_done) {
+ INIT_COMPLETION(vctrl->dmap_comp);
+ need_wait = 1;
+ }
+ } else if (enable == 0 && pipe->ov_blt_addr) {
+ vctrl->blt_change++;
+ if (vctrl->ov_koff != vctrl->dmap_done) {
+ INIT_COMPLETION(vctrl->dmap_comp);
+ need_wait = 1;
+ }
+ }
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+ if (need_wait)
+ mdp4_mddi_wait4dmap(0);
+
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ if (enable && pipe->ov_blt_addr == 0) {
+ pipe->ov_blt_addr = mfd->ov0_wb_buf->write_addr;
+ pipe->dma_blt_addr = mfd->ov0_wb_buf->read_addr;
+ pipe->ov_cnt = 0;
+ pipe->dmap_cnt = 0;
+ vctrl->ov_koff = vctrl->dmap_koff;
+ vctrl->ov_done = vctrl->dmap_done;
+ vctrl->blt_free = 0;
+ vctrl->blt_wait = 0;
+ vctrl->blt_end = 0;
+ mdp4_stat.blt_mddi++;
+ } else if (enable == 0 && pipe->ov_blt_addr) {
+ pipe->ov_blt_addr = 0;
+ pipe->dma_blt_addr = 0;
+ vctrl->blt_end = 1;
+ vctrl->blt_free = 4; /* 4 commits to free wb buf */
+ }
+
+ pr_debug("%s: changed=%d enable=%d ov_blt_addr=%x\n", __func__,
+ vctrl->blt_change, enable, (int)pipe->ov_blt_addr);
+
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+}
+
+/*
+ * mdp4_mddi_do_update:
+ * called from thread context
+ */
+void mdp4_mddi_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe)
+{
+ struct vsycn_ctrl *vctrl;
+ struct vsync_update *vp;
+ struct mdp4_overlay_pipe *pp;
+ int undx;
+
+ if (cndx >= MAX_CONTROLLER) {
+ pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+ return;
+ }
+
+ vctrl = &vsync_ctrl_db[cndx];
+
+ if (atomic_read(&vctrl->suspend) > 0)
+ return;
+
+ mutex_lock(&vctrl->update_lock);
+ undx = vctrl->update_ndx;
+ vp = &vctrl->vlist[undx];
+
+ pp = &vp->plist[pipe->pipe_ndx - 1]; /* ndx start form 1 */
+
+ pr_debug("%s: vndx=%d pipe_ndx=%d expire=%x pid=%d\n", __func__,
+ undx, pipe->pipe_ndx, vctrl->expire_tick, current->pid);
+
+ *pp = *pipe; /* clone it */
+ vp->update_cnt++;
+
+ mutex_unlock(&vctrl->update_lock);
+ mdp4_stat.overlay_play[pipe->mixer_num]++;
+}
+
+static void mdp4_mddi_blt_ov_update(struct mdp4_overlay_pipe *pipe);
+
+int mdp4_mddi_pipe_commit(void)
+{
+ int i, undx;
+ int mixer = 0;
+ struct vsycn_ctrl *vctrl;
+ struct vsync_update *vp;
+ struct mdp4_overlay_pipe *pipe;
+ struct mdp4_overlay_pipe *real_pipe;
+ unsigned long flags;
+ int need_dmap_wait = 0;
+ int need_ov_wait = 0;
+ int cnt = 0;
+
+ vctrl = &vsync_ctrl_db[0];
+
+ mutex_lock(&vctrl->update_lock);
+ undx = vctrl->update_ndx;
+ vp = &vctrl->vlist[undx];
+ pipe = vctrl->base_pipe;
+ mixer = pipe->mixer_num;
+
+ if (vp->update_cnt == 0) {
+ mutex_unlock(&vctrl->update_lock);
+ return cnt;
+ }
+
+ vctrl->update_ndx++;
+ vctrl->update_ndx &= 0x01;
+ vp->update_cnt = 0; /* reset */
+ if (vctrl->blt_free) {
+ vctrl->blt_free--;
+ if (vctrl->blt_free == 0)
+ mdp4_free_writeback_buf(vctrl->mfd, mixer);
+ }
+ mutex_unlock(&vctrl->update_lock);
+
+ /* free previous committed iommu back to pool */
+ mdp4_overlay_iommu_unmap_freelist(mixer);
+
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ if (pipe->ov_blt_addr) {
+ /* Blt */
+ if (vctrl->blt_wait)
+ need_dmap_wait = 1;
+ if (vctrl->ov_koff != vctrl->ov_done) {
+ INIT_COMPLETION(vctrl->ov_comp);
+ need_ov_wait = 1;
+ }
+ } else {
+ /* direct out */
+ if (vctrl->dmap_koff != vctrl->dmap_done) {
+ INIT_COMPLETION(vctrl->dmap_comp);
+ pr_debug("%s: wait, ok=%d od=%d dk=%d dd=%d cpu=%d\n",
+ __func__, vctrl->ov_koff, vctrl->ov_done,
+ vctrl->dmap_koff, vctrl->dmap_done, smp_processor_id());
+ need_dmap_wait = 1;
+ }
+ }
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+ if (need_dmap_wait) {
+ pr_debug("%s: wait4dmap\n", __func__);
+ mdp4_mddi_wait4dmap(0);
+ }
+
+ if (need_ov_wait) {
+ pr_debug("%s: wait4ov\n", __func__);
+ mdp4_mddi_wait4ov(0);
+ }
+
+ if (pipe->ov_blt_addr) {
+ if (vctrl->blt_end) {
+ vctrl->blt_end = 0;
+ pipe->ov_blt_addr = 0;
+ pipe->dma_blt_addr = 0;
+ }
+ }
+
+ if (vctrl->blt_change) {
+ mdp4_overlayproc_cfg(pipe);
+ mdp4_overlay_dmap_xy(pipe);
+ vctrl->blt_change = 0;
+ }
+
+ pipe = vp->plist;
+ for (i = 0; i < OVERLAY_PIPE_MAX; i++, pipe++) {
+ if (pipe->pipe_used) {
+ cnt++;
+ real_pipe = mdp4_overlay_ndx2pipe(pipe->pipe_ndx);
+ if (real_pipe && real_pipe->pipe_used) {
+ /* pipe not unset */
+ mdp4_overlay_vsync_commit(pipe);
+ }
+ /* free previous iommu to freelist
+ * which will be freed at next
+ * pipe_commit
+ */
+ mdp4_overlay_iommu_pipe_free(pipe->pipe_ndx, 0);
+ pipe->pipe_used = 0; /* clear */
+ }
+ }
+
+ mdp4_mixer_stage_commit(mixer);
+
+ pipe = vctrl->base_pipe;
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ if (pipe->ov_blt_addr) {
+ mdp4_mddi_blt_ov_update(pipe);
+ pipe->ov_cnt++;
+ vctrl->ov_koff++;
+ vsync_irq_enable(INTR_OVERLAY0_DONE, MDP_OVERLAY0_TERM);
+ } else {
+ vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM);
+ vctrl->dmap_koff++;
+ }
+ pr_debug("%s: kickoff\n", __func__);
+ /* kickoff overlay engine */
+ mdp4_stat.kickoff_ov0++;
+ outpdw(MDP_BASE + 0x0004, 0);
+ mb(); /* make sure kickoff ececuted */
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+ mdp4_stat.overlay_commit[pipe->mixer_num]++;
+
+ return cnt;
+}
+
+void mdp4_mddi_vsync_ctrl(struct fb_info *info, int enable)
+{
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+ struct vsycn_ctrl *vctrl;
+ unsigned long flags;
+ int clk_set_on = 0;
+ int cndx = 0;
+
+ vctrl = &vsync_ctrl_db[cndx];
+
+ pr_debug("%s: clk_enabled=%d vsycn_enabeld=%d req=%d\n", __func__,
+ vctrl->clk_enabled, vctrl->vsync_enabled, enable);
+
+ mutex_lock(&vctrl->update_lock);
+
+ if (vctrl->vsync_enabled == enable) {
+ mutex_unlock(&vctrl->update_lock);
+ return;
+ }
+
+ vctrl->vsync_enabled = enable;
+
+ if (enable) {
+ if (vctrl->clk_enabled == 0) {
+ pr_debug("%s: SET_CLK_ON\n", __func__);
+ mdp_clk_ctrl(1);
+ vctrl->clk_enabled = 1;
+ clk_set_on = 1;
+ }
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ vctrl->clk_control = 0;
+ vctrl->expire_tick = 0;
+ vctrl->uevent = 1;
+ vctrl->new_update = 1;
+ if (clk_set_on) {
+ vsync_irq_enable(INTR_PRIMARY_RDPTR,
+ MDP_PRIM_RDPTR_TERM);
+ }
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+ mdp4_overlay_update_mddi(mfd);
+ } else {
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ vctrl->clk_control = 1;
+ vctrl->uevent = 0;
+ if (vctrl->clk_enabled)
+ vctrl->expire_tick = VSYNC_EXPIRE_TICK;
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+ }
+ mutex_unlock(&vctrl->update_lock);
+}
+
+void mdp4_mddi_wait4vsync(int cndx, long long *vtime)
+{
+ struct vsycn_ctrl *vctrl;
+ struct mdp4_overlay_pipe *pipe;
+ unsigned long flags;
+
+ if (cndx >= MAX_CONTROLLER) {
+ pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+ return;
+ }
+
+ vctrl = &vsync_ctrl_db[cndx];
+ pipe = vctrl->base_pipe;
+
+ if (atomic_read(&vctrl->suspend) > 0) {
+ *vtime = -1;
+ return;
+ }
+
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ if (vctrl->wait_vsync_cnt == 0)
+ INIT_COMPLETION(vctrl->vsync_comp);
+ vctrl->wait_vsync_cnt++;
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+ wait_for_completion(&vctrl->vsync_comp);
+ mdp4_stat.wait4vsync0++;
+
+ *vtime = ktime_to_ns(vctrl->vsync_time);
+}
+
+static void mdp4_mddi_wait4dmap(int cndx)
+{
+ struct vsycn_ctrl *vctrl;
+
+ if (cndx >= MAX_CONTROLLER) {
+ pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+ return;
+ }
+
+ vctrl = &vsync_ctrl_db[cndx];
+
+ if (atomic_read(&vctrl->suspend) > 0)
+ return;
+
+ wait_for_completion(&vctrl->dmap_comp);
+}
+
+static void mdp4_mddi_wait4ov(int cndx)
+{
+ struct vsycn_ctrl *vctrl;
+
+ if (cndx >= MAX_CONTROLLER) {
+ pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+ return;
+ }
+
+ vctrl = &vsync_ctrl_db[cndx];
+
+ if (atomic_read(&vctrl->suspend) > 0)
+ return;
+
+ wait_for_completion(&vctrl->ov_comp);
+}
+
+/*
+ * primary_rdptr_isr:
+ * called from interrupt context
+ */
+static void primary_rdptr_isr(int cndx)
+{
+ struct vsycn_ctrl *vctrl;
+
+ vctrl = &vsync_ctrl_db[cndx];
+ pr_debug("%s: ISR, cpu=%d\n", __func__, smp_processor_id());
+ vctrl->rdptr_intr_tot++;
+ vctrl->vsync_time = ktime_get();
+
+ spin_lock(&vctrl->spin_lock);
+
+ if (vctrl->uevent)
+ schedule_work(&vctrl->vsync_work);
+
+ if (vctrl->wait_vsync_cnt) {
+ complete(&vctrl->vsync_comp);
+ vctrl->wait_vsync_cnt = 0;
+ }
+
+ if (vctrl->expire_tick) {
+ vctrl->expire_tick--;
+ if (vctrl->expire_tick == 0)
+ schedule_work(&vctrl->clk_work);
+ }
+ spin_unlock(&vctrl->spin_lock);
+}
+
+void mdp4_dmap_done_mddi(int cndx)
+{
+ struct vsycn_ctrl *vctrl;
+ struct mdp4_overlay_pipe *pipe;
+ int diff;
+
+ vctrl = &vsync_ctrl_db[cndx];
+ pipe = vctrl->base_pipe;
+
+ /* blt enabled */
+ spin_lock(&vctrl->spin_lock);
+ vsync_irq_disable(INTR_DMA_P_DONE, MDP_DMAP_TERM);
+ vctrl->dmap_done++;
+ diff = vctrl->ov_done - vctrl->dmap_done;
+ pr_debug("%s: ov_koff=%d ov_done=%d dmap_koff=%d dmap_done=%d cpu=%d\n",
+ __func__, vctrl->ov_koff, vctrl->ov_done, vctrl->dmap_koff,
+ vctrl->dmap_done, smp_processor_id());
+ complete_all(&vctrl->dmap_comp);
+ if (diff <= 0) {
+ if (vctrl->blt_wait)
+ vctrl->blt_wait = 0;
+ spin_unlock(&vctrl->spin_lock);
+ return;
+ }
+
+ /* kick dmap */
+ mdp4_mddi_blt_dmap_update(pipe);
+ pipe->dmap_cnt++;
+ mdp4_stat.kickoff_dmap++;
+ vctrl->dmap_koff++;
+ vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM);
+ outpdw(MDP_BASE + 0x000c, 0); /* kickoff dmap engine */
+ mb(); /* make sure kickoff executed */
+ spin_unlock(&vctrl->spin_lock);
+}
+
+/*
+ * mdp4_overlay0_done_mddi: called from isr
+ */
+void mdp4_overlay0_done_mddi(int cndx)
+{
+ struct vsycn_ctrl *vctrl;
+ struct mdp4_overlay_pipe *pipe;
+ int diff;
+
+ vctrl = &vsync_ctrl_db[cndx];
+ pipe = vctrl->base_pipe;
+
+ spin_lock(&vctrl->spin_lock);
+ vsync_irq_disable(INTR_OVERLAY0_DONE, MDP_OVERLAY0_TERM);
+ vctrl->ov_done++;
+ complete_all(&vctrl->ov_comp);
+ diff = vctrl->ov_done - vctrl->dmap_done;
+
+ pr_debug("%s: ov_koff=%d ov_done=%d dmap_koff=%d dmap_done=%d cpu=%d\n",
+ __func__, vctrl->ov_koff, vctrl->ov_done, vctrl->dmap_koff,
+ vctrl->dmap_done, smp_processor_id());
+
+ if (pipe->ov_blt_addr == 0) {
+ /* blt disabled */
+ spin_unlock(&vctrl->spin_lock);
+ return;
+ }
+
+ if (diff > 1) {
+ /*
+ * two overlay_done and none dmap_done yet
+ * let dmap_done kickoff dmap
+ * and put pipe_commit to wait
+ */
+ vctrl->blt_wait = 1;
+ pr_debug("%s: blt_wait set\n", __func__);
+ spin_unlock(&vctrl->spin_lock);
+ return;
+ }
+ mdp4_mddi_blt_dmap_update(pipe);
+ pipe->dmap_cnt++;
+ mdp4_stat.kickoff_dmap++;
+ vctrl->dmap_koff++;
+ vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM);
+ outpdw(MDP_BASE + 0x000c, 0); /* kickoff dmap engine */
+ mb(); /* make sure kickoff executed */
+ spin_unlock(&vctrl->spin_lock);
+}
+
+static void clk_ctrl_work(struct work_struct *work)
+{
+ struct vsycn_ctrl *vctrl =
+ container_of(work, typeof(*vctrl), clk_work);
+ unsigned long flags;
+
+ mutex_lock(&vctrl->update_lock);
+ if (vctrl->clk_control && vctrl->clk_enabled) {
+ pr_debug("%s: SET_CLK_OFF\n", __func__);
+ mdp_clk_ctrl(0);
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ vsync_irq_disable(INTR_PRIMARY_RDPTR, MDP_PRIM_RDPTR_TERM);
+ vctrl->clk_enabled = 0;
+ vctrl->clk_control = 0;
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+ }
+ mutex_unlock(&vctrl->update_lock);
+}
+
+static void send_vsync_work(struct work_struct *work)
+{
+ struct vsycn_ctrl *vctrl =
+ container_of(work, typeof(*vctrl), vsync_work);
+ char buf[64];
+ char *envp[2];
+
+ snprintf(buf, sizeof(buf), "VSYNC=%llu",
+ ktime_to_ns(vctrl->vsync_time));
+ envp[0] = buf;
+ envp[1] = NULL;
+ kobject_uevent_env(&vctrl->dev->kobj, KOBJ_CHANGE, envp);
+}
+
+
+void mdp4_mddi_rdptr_init(int cndx)
+{
+ struct vsycn_ctrl *vctrl;
+
+ if (cndx >= MAX_CONTROLLER) {
+ pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+ return;
+ }
+
+ vctrl = &vsync_ctrl_db[cndx];
+ if (vctrl->inited)
+ return;
+
+ vctrl->inited = 1;
+ vctrl->update_ndx = 0;
+ mutex_init(&vctrl->update_lock);
+ init_completion(&vctrl->ov_comp);
+ init_completion(&vctrl->dmap_comp);
+ init_completion(&vctrl->vsync_comp);
+ spin_lock_init(&vctrl->spin_lock);
+ INIT_WORK(&vctrl->vsync_work, send_vsync_work);
+ INIT_WORK(&vctrl->clk_work, clk_ctrl_work);
+}
+
+void mdp4_primary_rdptr(void)
+{
+ primary_rdptr_isr(0);
+}
+
+void mdp4_overlay_mddi_state_set(int state)
+{
+ unsigned long flag;
+
+ spin_lock_irqsave(&mdp_spin_lock, flag);
+ mddi_state = state;
+ spin_unlock_irqrestore(&mdp_spin_lock, flag);
+}
+
+int mdp4_overlay_mddi_state_get(void)
+{
+ return mddi_state;
+}
+
+static __u32 msm_fb_line_length(__u32 fb_index, __u32 xres, int bpp)
+{
+ /*
+ * The adreno GPU hardware requires that the pitch be aligned to
+ * 32 pixels for color buffers, so for the cases where the GPU
+ * is writing directly to fb0, the framebuffer pitch
+ * also needs to be 32 pixel aligned
+ */
+
+ if (fb_index == 0)
+ return ALIGN(xres, 32) * bpp;
+ else
+ return xres * bpp;
}
void mdp4_mddi_vsync_enable(struct msm_fb_data_type *mfd,
@@ -61,13 +703,6 @@
if ((mfd->use_mdp_vsync) && (mfd->ibuf.vsync_enable) &&
(mfd->panel_info.lcd.vsync_enable)) {
- if (mdp_hw_revision < MDP4_REVISION_V2_1) {
- /* need dmas dmap switch */
- if (which == 0 && dmap_vsync_enable == 0 &&
- mfd->panel_info.lcd.rev < 2) /* dma_p */
- return;
- }
-
if (vsync_start_y_adjust <= pipe->dst_y)
start_y = pipe->dst_y - vsync_start_y_adjust;
else
@@ -88,633 +723,337 @@
}
}
-#define WHOLESCREEN
+void mdp4_mddi_base_swap(int cndx, struct mdp4_overlay_pipe *pipe)
+{
+ struct vsycn_ctrl *vctrl;
-void mdp4_overlay_update_lcd(struct msm_fb_data_type *mfd)
+ if (cndx >= MAX_CONTROLLER) {
+ pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+ return;
+ }
+
+ vctrl = &vsync_ctrl_db[cndx];
+ vctrl->base_pipe = pipe;
+}
+
+static void mdp4_overlay_setup_pipe_addr(struct msm_fb_data_type *mfd,
+ struct mdp4_overlay_pipe *pipe)
{
MDPIBUF *iBuf = &mfd->ibuf;
+ struct fb_info *fbi;
+ int bpp;
uint8 *src;
+
+ /* whole screen for base layer */
+ src = (uint8 *) iBuf->buf;
+ fbi = mfd->fbi;
+
+ if (pipe->is_3d) {
+ bpp = fbi->var.bits_per_pixel / 8;
+ pipe->src_height = pipe->src_height_3d;
+ pipe->src_width = pipe->src_width_3d;
+ pipe->src_h = pipe->src_height_3d;
+ pipe->src_w = pipe->src_width_3d;
+ pipe->dst_h = pipe->src_height_3d;
+ pipe->dst_w = pipe->src_width_3d;
+ pipe->srcp0_ystride = msm_fb_line_length(0,
+ pipe->src_width, bpp);
+ } else {
+ /* 2D */
+ pipe->src_height = fbi->var.yres;
+ pipe->src_width = fbi->var.xres;
+ pipe->src_h = fbi->var.yres;
+ pipe->src_w = fbi->var.xres;
+ pipe->dst_h = fbi->var.yres;
+ pipe->dst_w = fbi->var.xres;
+ pipe->srcp0_ystride = fbi->fix.line_length;
+ }
+ pipe->src_y = 0;
+ pipe->src_x = 0;
+ pipe->dst_y = 0;
+ pipe->dst_x = 0;
+ pipe->srcp0_addr = (uint32)src;
+}
+
+void mdp4_overlay_update_mddi(struct msm_fb_data_type *mfd)
+{
int ptype;
uint32 mddi_ld_param;
uint16 mddi_vdo_packet_reg;
struct mdp4_overlay_pipe *pipe;
+ uint32 data;
int ret;
+ int cndx = 0;
+ struct vsycn_ctrl *vctrl;
if (mfd->key != MFD_KEY)
return;
- mddi_mfd = mfd; /* keep it */
+ vctrl = &vsync_ctrl_db[cndx];
- /* MDP cmd block enable */
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
-
- if (mddi_pipe == NULL) {
+ if (vctrl->base_pipe == NULL) {
ptype = mdp4_overlay_format2type(mfd->fb_imgType);
+
if (ptype < 0)
- printk(KERN_INFO "%s: format2type failed\n", __func__);
+ pr_err("%s: format2type failed\n", __func__);
+
pipe = mdp4_overlay_pipe_alloc(ptype, MDP4_MIXER0);
- if (pipe == NULL)
- printk(KERN_INFO "%s: pipe_alloc failed\n", __func__);
+ if (pipe == NULL) {
+ pr_err("%s: pipe_alloc failed\n", __func__);
+ return;
+ }
pipe->pipe_used++;
+ pipe->mixer_stage = MDP4_MIXER_STAGE_BASE;
pipe->mixer_num = MDP4_MIXER0;
pipe->src_format = mfd->fb_imgType;
mdp4_overlay_panel_mode(pipe->mixer_num, MDP4_PANEL_MDDI);
ret = mdp4_overlay_format2pipe(pipe);
if (ret < 0)
- printk(KERN_INFO "%s: format2type failed\n", __func__);
+ pr_err("%s: format2type failed\n", __func__);
- mddi_pipe = pipe; /* keep it */
- mddi_ld_param = 0;
- mddi_vdo_packet_reg = mfd->panel_info.mddi.vdopkt;
-
- if (mdp_hw_revision == MDP4_REVISION_V2_1) {
- uint32 data;
-
- data = inpdw(MDP_BASE + 0x0028);
- data &= ~0x0300; /* bit 8, 9, MASTER4 */
- if (mfd->fbi->var.xres == 540) /* qHD, 540x960 */
- data |= 0x0200;
- else
- data |= 0x0100;
-
- MDP_OUTP(MDP_BASE + 0x00028, data);
- }
-
- if (mfd->panel_info.type == MDDI_PANEL) {
- if (mfd->panel_info.pdest == DISPLAY_1)
- mddi_ld_param = 0;
- else
- mddi_ld_param = 1;
- } else {
- mddi_ld_param = 2;
- }
-
- MDP_OUTP(MDP_BASE + 0x00090, mddi_ld_param);
-
- if (mfd->panel_info.bpp == 24)
- MDP_OUTP(MDP_BASE + 0x00094,
- (MDDI_VDO_PACKET_DESC_24 << 16) | mddi_vdo_packet_reg);
- else if (mfd->panel_info.bpp == 16)
- MDP_OUTP(MDP_BASE + 0x00094,
- (MDDI_VDO_PACKET_DESC_16 << 16) | mddi_vdo_packet_reg);
- else
- MDP_OUTP(MDP_BASE + 0x00094,
- (MDDI_VDO_PACKET_DESC << 16) | mddi_vdo_packet_reg);
-
- MDP_OUTP(MDP_BASE + 0x00098, 0x01);
+ vctrl->base_pipe = pipe; /* keep it */
mdp4_init_writeback_buf(mfd, MDP4_MIXER0);
pipe->ov_blt_addr = 0;
pipe->dma_blt_addr = 0;
} else {
- pipe = mddi_pipe;
+ pipe = vctrl->base_pipe;
}
- /* 0 for dma_p, client_id = 0 */
- MDP_OUTP(MDP_BASE + 0x00090, 0);
+ MDP_OUTP(MDP_BASE + 0x021c, 10); /* read pointer */
+ mddi_ld_param = 0;
+ mddi_vdo_packet_reg = mfd->panel_info.mddi.vdopkt;
- src = (uint8 *) iBuf->buf;
+ if (mdp_hw_revision == MDP4_REVISION_V2_1) {
+ data = inpdw(MDP_BASE + 0x0028);
+ data &= ~0x0300; /* bit 8, 9, MASTER4 */
+ if (mfd->fbi->var.xres == 540) /* qHD, 540x960 */
+ data |= 0x0200;
+ else
+ data |= 0x0100;
-#ifdef WHOLESCREEN
-
- {
- struct fb_info *fbi;
-
- fbi = mfd->fbi;
- pipe->src_height = fbi->var.yres;
- pipe->src_width = fbi->var.xres;
- pipe->src_h = fbi->var.yres;
- pipe->src_w = fbi->var.xres;
- pipe->src_y = 0;
- pipe->src_x = 0;
- pipe->dst_h = fbi->var.yres;
- pipe->dst_w = fbi->var.xres;
- pipe->dst_y = 0;
- pipe->dst_x = 0;
- pipe->srcp0_addr = (uint32)src;
- pipe->srcp0_ystride = fbi->fix.line_length;
+ MDP_OUTP(MDP_BASE + 0x00028, data);
}
-#else
- if (mdp4_overlay_active(MDP4_MIXER0)) {
- struct fb_info *fbi;
-
- fbi = mfd->fbi;
- pipe->src_height = fbi->var.yres;
- pipe->src_width = fbi->var.xres;
- pipe->src_h = fbi->var.yres;
- pipe->src_w = fbi->var.xres;
- pipe->src_y = 0;
- pipe->src_x = 0;
- pipe->dst_h = fbi->var.yres;
- pipe->dst_w = fbi->var.xres;
- pipe->dst_y = 0;
- pipe->dst_x = 0;
- pipe->srcp0_addr = (uint32) src;
- pipe->srcp0_ystride = fbi->fix.line_length;
+ if (mfd->panel_info.type == MDDI_PANEL) {
+ if (mfd->panel_info.pdest == DISPLAY_1)
+ mddi_ld_param = 0;
+ else
+ mddi_ld_param = 1;
} else {
- /* starting input address */
- src += (iBuf->dma_x + iBuf->dma_y * iBuf->ibuf_width)
- * iBuf->bpp;
-
- pipe->src_height = iBuf->dma_h;
- pipe->src_width = iBuf->dma_w;
- pipe->src_h = iBuf->dma_h;
- pipe->src_w = iBuf->dma_w;
- pipe->src_y = 0;
- pipe->src_x = 0;
- pipe->dst_h = iBuf->dma_h;
- pipe->dst_w = iBuf->dma_w;
- pipe->dst_y = iBuf->dma_y;
- pipe->dst_x = iBuf->dma_x;
- pipe->srcp0_addr = (uint32) src;
- pipe->srcp0_ystride = iBuf->ibuf_width * iBuf->bpp;
+ mddi_ld_param = 2;
}
-#endif
- pipe->mixer_stage = MDP4_MIXER_STAGE_BASE;
+ MDP_OUTP(MDP_BASE + 0x00090, mddi_ld_param);
+
+ if (mfd->panel_info.bpp == 24)
+ MDP_OUTP(MDP_BASE + 0x00094,
+ (MDDI_VDO_PACKET_DESC_24 << 16) | mddi_vdo_packet_reg);
+ else if (mfd->panel_info.bpp == 16)
+ MDP_OUTP(MDP_BASE + 0x00094,
+ (MDDI_VDO_PACKET_DESC_16 << 16) | mddi_vdo_packet_reg);
+ else
+ MDP_OUTP(MDP_BASE + 0x00094,
+ (MDDI_VDO_PACKET_DESC << 16) | mddi_vdo_packet_reg);
+
+ MDP_OUTP(MDP_BASE + 0x00098, 0x01);
+
+
+ mdp4_overlay_setup_pipe_addr(mfd, pipe);
mdp4_overlay_rgb_setup(pipe);
- mdp4_mixer_stage_up(pipe, 1);
+ mdp4_overlay_reg_flush(pipe, 1);
+
+ mdp4_mixer_stage_up(pipe, 0);
mdp4_overlayproc_cfg(pipe);
mdp4_overlay_dmap_xy(pipe);
mdp4_overlay_dmap_cfg(mfd, 0);
+
mdp4_mixer_stage_commit(pipe->mixer_num);
- mdp4_mddi_vsync_enable(mfd, pipe, 0);
- /* MDP cmd block disable */
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+ wmb();
}
-int mdp4_mddi_overlay_blt_start(struct msm_fb_data_type *mfd)
+void mdp4_mddi_blt_start(struct msm_fb_data_type *mfd)
{
- unsigned long flag;
-
- pr_debug("%s: blt_end=%d blt_addr=%x pid=%d\n",
- __func__, mddi_pipe->blt_end,
- (int)mddi_pipe->ov_blt_addr, current->pid);
-
- mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0);
-
- if (mfd->ov0_wb_buf->write_addr == 0) {
- pr_info("%s: no blt_base assigned\n", __func__);
- return -EBUSY;
- }
-
- if (mddi_pipe->ov_blt_addr == 0) {
- mdp4_mddi_dma_busy_wait(mfd);
- spin_lock_irqsave(&mdp_spin_lock, flag);
- mddi_pipe->blt_end = 0;
- mddi_pipe->blt_cnt = 0;
- mddi_pipe->ov_cnt = 0;
- mddi_pipe->dmap_cnt = 0;
- mddi_pipe->ov_blt_addr = mfd->ov0_wb_buf->write_addr;
- mddi_pipe->dma_blt_addr = mfd->ov0_wb_buf->write_addr;
- mdp4_stat.blt_mddi++;
- spin_unlock_irqrestore(&mdp_spin_lock, flag);
- return 0;
+ mdp4_mddi_do_blt(mfd, 1);
}
- return -EBUSY;
-}
-
-int mdp4_mddi_overlay_blt_stop(struct msm_fb_data_type *mfd)
+void mdp4_mddi_blt_stop(struct msm_fb_data_type *mfd)
{
- unsigned long flag;
-
- pr_debug("%s: blt_end=%d blt_addr=%x\n",
- __func__, mddi_pipe->blt_end, (int)mddi_pipe->ov_blt_addr);
-
- if ((mddi_pipe->blt_end == 0) && mddi_pipe->ov_blt_addr) {
- spin_lock_irqsave(&mdp_spin_lock, flag);
- mddi_pipe->blt_end = 1; /* mark as end */
- spin_unlock_irqrestore(&mdp_spin_lock, flag);
- return 0;
- }
-
- return -EBUSY;
-}
-
-int mdp4_mddi_overlay_blt_offset(struct msm_fb_data_type *mfd,
- struct msmfb_overlay_blt *req)
-{
- req->offset = 0;
- req->width = mddi_pipe->src_width;
- req->height = mddi_pipe->src_height;
- req->bpp = mddi_pipe->bpp;
-
- return sizeof(*req);
+ mdp4_mddi_do_blt(mfd, 0);
}
void mdp4_mddi_overlay_blt(struct msm_fb_data_type *mfd,
struct msmfb_overlay_blt *req)
{
- if (req->enable)
- mdp4_mddi_overlay_blt_start(mfd);
- else if (req->enable == 0)
- mdp4_mddi_overlay_blt_stop(mfd);
-
+ mdp4_mddi_do_blt(mfd, req->enable);
}
-void mdp4_blt_xy_update(struct mdp4_overlay_pipe *pipe)
+int mdp4_mddi_on(struct platform_device *pdev)
{
- uint32 off, addr, addr2;
- int bpp;
- char *overlay_base;
+ int ret = 0;
+ int cndx = 0;
+ struct msm_fb_data_type *mfd;
+ struct vsycn_ctrl *vctrl;
- if (pipe->ov_blt_addr == 0)
- return;
+ pr_debug("%s+:\n", __func__);
+ mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
-#ifdef BLT_RGB565
- bpp = 2; /* overlay ouput is RGB565 */
-#else
- bpp = 3; /* overlay ouput is RGB888 */
-#endif
- off = 0;
- if (pipe->dmap_cnt & 0x01)
- off = pipe->src_height * pipe->src_width * bpp;
+ vctrl = &vsync_ctrl_db[cndx];
+ vctrl->mfd = mfd;
+ vctrl->dev = mfd->fbi->dev;
- addr = pipe->ov_blt_addr + off;
+ mdp_clk_ctrl(1);
+ mdp4_overlay_update_mddi(mfd);
+ mdp_clk_ctrl(0);
- /* dmap */
- MDP_OUTP(MDP_BASE + 0x90008, addr);
+ mdp4_iommu_attach();
- off = 0;
- if (pipe->ov_cnt & 0x01)
- off = pipe->src_height * pipe->src_width * bpp;
- addr2 = pipe->ov_blt_addr + off;
- /* overlay 0 */
- overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */
- outpdw(overlay_base + 0x000c, addr2);
- outpdw(overlay_base + 0x001c, addr2);
+ atomic_set(&vctrl->suspend, 0);
+ pr_debug("%s-:\n", __func__);
+
+ return ret;
}
-void mdp4_primary_rdptr(void)
+int mdp4_mddi_off(struct platform_device *pdev)
{
-}
+ int ret = 0;
+ int cndx = 0;
+ struct msm_fb_data_type *mfd;
+ struct vsycn_ctrl *vctrl;
+ struct mdp4_overlay_pipe *pipe;
-/*
- * mdp4_dmap_done_mddi: called from isr
- */
-void mdp4_dma_p_done_mddi(struct mdp_dma_data *dma)
-{
- int diff;
+ pr_debug("%s+:\n", __func__);
- mddi_pipe->dmap_cnt++;
- diff = mddi_pipe->ov_cnt - mddi_pipe->dmap_cnt;
- pr_debug("%s: ov_cnt=%d dmap_cnt=%d\n",
- __func__, mddi_pipe->ov_cnt, mddi_pipe->dmap_cnt);
+ mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
- if (diff <= 0) {
- spin_lock(&mdp_spin_lock);
- dma->dmap_busy = FALSE;
- complete(&dma->dmap_comp);
- spin_unlock(&mdp_spin_lock);
-
- if (mddi_pipe->blt_end) {
- mddi_pipe->blt_end = 0;
- mddi_pipe->ov_blt_addr = 0;
- mddi_pipe->dma_blt_addr = 0;
- pr_debug("%s: END, ov_cnt=%d dmap_cnt=%d\n", __func__,
- mddi_pipe->ov_cnt, mddi_pipe->dmap_cnt);
- mdp_intr_mask &= ~INTR_DMA_P_DONE;
- outp32(MDP_INTR_ENABLE, mdp_intr_mask);
- }
-
- mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, TRUE);
- mdp_disable_irq_nosync(MDP_DMA2_TERM); /* disable intr */
- return;
+ vctrl = &vsync_ctrl_db[cndx];
+ pipe = vctrl->base_pipe;
+ if (pipe == NULL) {
+ pr_err("%s: NO base pipe\n", __func__);
+ return ret;
}
- spin_lock(&mdp_spin_lock);
- dma->busy = FALSE;
- spin_unlock(&mdp_spin_lock);
- complete(&dma->comp);
- if (busy_wait_cnt)
- busy_wait_cnt--;
+ atomic_set(&vctrl->suspend, 1);
- pr_debug("%s: kickoff dmap\n", __func__);
+ /* sanity check, free pipes besides base layer */
+ mdp4_overlay_unset_mixer(pipe->mixer_num);
+ mdp4_mixer_stage_down(pipe, 1);
+ mdp4_overlay_pipe_free(pipe);
+ vctrl->base_pipe = NULL;
- mdp4_blt_xy_update(mddi_pipe);
- /* kick off dmap */
- outpdw(MDP_BASE + 0x000c, 0x0);
- mdp4_stat.kickoff_dmap++;
- mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, TRUE);
-}
-
-/*
- * mdp4_overlay0_done_mddi: called from isr
- */
-void mdp4_overlay0_done_mddi(struct mdp_dma_data *dma)
-{
- int diff;
-
- if (mddi_pipe->ov_blt_addr == 0) {
- mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, TRUE);
- spin_lock(&mdp_spin_lock);
- dma->busy = FALSE;
- spin_unlock(&mdp_spin_lock);
- complete(&dma->comp);
-
- if (busy_wait_cnt)
- busy_wait_cnt--;
- mdp_disable_irq_nosync(MDP_OVERLAY0_TERM);
-
- return;
+ if (vctrl->clk_enabled) {
+ /*
+ * in case of suspend, vsycn_ctrl off is not
+ * received from frame work which left clock on
+ * then, clock need to be turned off here
+ */
+ mdp_clk_ctrl(0);
}
- /* blt enabled */
- if (mddi_pipe->blt_end == 0)
- mddi_pipe->ov_cnt++;
+ vctrl->clk_enabled = 0;
+ vctrl->vsync_enabled = 0;
+ vctrl->clk_control = 0;
+ vctrl->expire_tick = 0;
+ vctrl->uevent = 0;
- pr_debug("%s: ov_cnt=%d dmap_cnt=%d\n",
- __func__, mddi_pipe->ov_cnt, mddi_pipe->dmap_cnt);
+ vsync_irq_disable(INTR_PRIMARY_RDPTR, MDP_PRIM_RDPTR_TERM);
- if (mddi_pipe->blt_cnt == 0) {
- /* first kickoff since blt enabled */
- mdp_intr_mask |= INTR_DMA_P_DONE;
- outp32(MDP_INTR_ENABLE, mdp_intr_mask);
- }
+ pr_debug("%s-:\n", __func__);
- mddi_pipe->blt_cnt++;
-
- diff = mddi_pipe->ov_cnt - mddi_pipe->dmap_cnt;
- if (diff >= 2) {
- mdp_disable_irq_nosync(MDP_OVERLAY0_TERM);
- return;
- }
-
- spin_lock(&mdp_spin_lock);
- dma->busy = FALSE;
- dma->dmap_busy = TRUE;
- spin_unlock(&mdp_spin_lock);
- complete(&dma->comp);
-
- if (busy_wait_cnt)
- busy_wait_cnt--;
-
- pr_debug("%s: kickoff dmap\n", __func__);
-
- mdp4_blt_xy_update(mddi_pipe);
- mdp_enable_irq(MDP_DMA2_TERM); /* enable intr */
- /* kick off dmap */
- outpdw(MDP_BASE + 0x000c, 0x0);
- mdp4_stat.kickoff_dmap++;
- mdp_disable_irq_nosync(MDP_OVERLAY0_TERM);
-}
-
-void mdp4_mddi_overlay_restore(void)
-{
- if (mddi_mfd == NULL)
- return;
-
- pr_debug("%s: resotre, pid=%d\n", __func__, current->pid);
-
- if (mddi_mfd->panel_power_on == 0)
- return;
- if (mddi_mfd && mddi_pipe) {
- mdp4_mddi_dma_busy_wait(mddi_mfd);
- mdp4_overlay_update_lcd(mddi_mfd);
-
- if (mddi_pipe->ov_blt_addr)
- mdp4_mddi_blt_dmap_busy_wait(mddi_mfd);
- mdp4_mddi_overlay_kickoff(mddi_mfd, mddi_pipe);
- mddi_mfd->dma_update_flag = 1;
- }
- if (mdp_hw_revision < MDP4_REVISION_V2_1) /* need dmas dmap switch */
- mdp4_mddi_overlay_dmas_restore();
-}
-
-void mdp4_mddi_blt_dmap_busy_wait(struct msm_fb_data_type *mfd)
-{
- unsigned long flag;
- int need_wait = 0;
-
- spin_lock_irqsave(&mdp_spin_lock, flag);
- if (mfd->dma->dmap_busy == TRUE) {
- INIT_COMPLETION(mfd->dma->dmap_comp);
- need_wait++;
- }
- spin_unlock_irqrestore(&mdp_spin_lock, flag);
-
- if (need_wait) {
- /* wait until DMA finishes the current job */
- wait_for_completion(&mfd->dma->dmap_comp);
- }
-}
-
-/*
- * mdp4_mddi_cmd_dma_busy_wait: check mddi link activity
- * mddi link is a shared resource and it can only be used
- * while it is in idle state.
- * ov_mutex need to be acquired before call this function.
- */
-void mdp4_mddi_dma_busy_wait(struct msm_fb_data_type *mfd)
-{
- unsigned long flag;
- int need_wait = 0;
-
- pr_debug("%s: START, pid=%d\n", __func__, current->pid);
- spin_lock_irqsave(&mdp_spin_lock, flag);
- if (mfd->dma->busy == TRUE) {
- if (busy_wait_cnt == 0)
- INIT_COMPLETION(mfd->dma->comp);
- busy_wait_cnt++;
- need_wait++;
- }
- spin_unlock_irqrestore(&mdp_spin_lock, flag);
-
-
- if (need_wait) {
- /* wait until DMA finishes the current job */
- pr_debug("%s: PENDING, pid=%d\n", __func__, current->pid);
- wait_for_completion(&mfd->dma->comp);
- }
- pr_debug("%s: DONE, pid=%d\n", __func__, current->pid);
-}
-
-void mdp4_mddi_kickoff_video(struct msm_fb_data_type *mfd,
- struct mdp4_overlay_pipe *pipe)
-{
/*
- * a video kickoff may happen before UI kickoff after
- * blt enabled. mdp4_overlay_update_lcd() need
- * to be called before kickoff.
- * vice versa for blt disabled.
+ * footswitch off
+ * this will casue all mdp register
+ * to be reset to default
+ * after footswitch on later
*/
- if (mddi_pipe->ov_blt_addr && mddi_pipe->blt_cnt == 0)
- mdp4_overlay_update_lcd(mfd); /* first time */
- else if (mddi_pipe->ov_blt_addr == 0 && mddi_pipe->blt_cnt) {
- mdp4_overlay_update_lcd(mfd); /* last time */
- mddi_pipe->blt_cnt = 0;
- }
- pr_debug("%s: blt_addr=%d blt_cnt=%d\n",
- __func__, (int)mddi_pipe->ov_blt_addr, mddi_pipe->blt_cnt);
-
- if (mddi_pipe->ov_blt_addr)
- mdp4_mddi_blt_dmap_busy_wait(mddi_mfd);
- mdp4_mddi_overlay_kickoff(mfd, pipe);
+ return ret;
}
-void mdp4_mddi_kickoff_ui(struct msm_fb_data_type *mfd,
- struct mdp4_overlay_pipe *pipe)
+void mdp_mddi_overlay_suspend(struct msm_fb_data_type *mfd)
{
- pr_debug("%s: pid=%d\n", __func__, current->pid);
- mdp4_mddi_overlay_kickoff(mfd, pipe);
-}
+ int cndx = 0;
+ struct vsycn_ctrl *vctrl;
+ struct mdp4_overlay_pipe *pipe;
+ vctrl = &vsync_ctrl_db[cndx];
+ pipe = vctrl->base_pipe;
+ /* dis-engage rgb0 from mixer0 */
+ if (pipe) {
+ if (mfd->ref_cnt == 0) {
+ /* adb stop */
+ if (pipe->pipe_type == OVERLAY_TYPE_BF)
+ mdp4_overlay_borderfill_stage_down(pipe);
-void mdp4_mddi_overlay_kickoff(struct msm_fb_data_type *mfd,
- struct mdp4_overlay_pipe *pipe)
-{
- unsigned long flag;
-
- mdp_enable_irq(MDP_OVERLAY0_TERM);
- spin_lock_irqsave(&mdp_spin_lock, flag);
- mfd->dma->busy = TRUE;
- if (mddi_pipe->ov_blt_addr)
- mfd->dma->dmap_busy = TRUE;
- spin_unlock_irqrestore(&mdp_spin_lock, flag);
- /* start OVERLAY pipe */
- mdp_pipe_kickoff(MDP_OVERLAY0_TERM, mfd);
- mdp4_stat.kickoff_ov0++;
-}
-
-void mdp4_dma_s_update_lcd(struct msm_fb_data_type *mfd,
- struct mdp4_overlay_pipe *pipe)
-{
- MDPIBUF *iBuf = &mfd->ibuf;
- uint32 outBpp = iBuf->bpp;
- uint16 mddi_vdo_packet_reg;
- uint32 dma_s_cfg_reg;
-
- dma_s_cfg_reg = 0;
-
- if (mfd->fb_imgType == MDP_RGBA_8888)
- dma_s_cfg_reg |= DMA_PACK_PATTERN_BGR; /* on purpose */
- else if (mfd->fb_imgType == MDP_BGR_565)
- dma_s_cfg_reg |= DMA_PACK_PATTERN_BGR;
- else
- dma_s_cfg_reg |= DMA_PACK_PATTERN_RGB;
-
- if (outBpp == 4)
- dma_s_cfg_reg |= (1 << 26); /* xRGB8888 */
- else if (outBpp == 2)
- dma_s_cfg_reg |= DMA_IBUF_FORMAT_RGB565;
-
- dma_s_cfg_reg |= DMA_DITHER_EN;
-
- /* MDP cmd block enable */
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
- /* PIXELSIZE */
- MDP_OUTP(MDP_BASE + 0xa0004, (pipe->dst_h << 16 | pipe->dst_w));
- MDP_OUTP(MDP_BASE + 0xa0008, pipe->srcp0_addr); /* ibuf address */
- MDP_OUTP(MDP_BASE + 0xa000c, pipe->srcp0_ystride);/* ystride */
-
- if (mfd->panel_info.bpp == 24) {
- dma_s_cfg_reg |= DMA_DSTC0G_8BITS | /* 666 18BPP */
- DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS;
- } else if (mfd->panel_info.bpp == 18) {
- dma_s_cfg_reg |= DMA_DSTC0G_6BITS | /* 666 18BPP */
- DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
- } else {
- dma_s_cfg_reg |= DMA_DSTC0G_6BITS | /* 565 16BPP */
- DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
- }
-
- MDP_OUTP(MDP_BASE + 0xa0010, (pipe->dst_y << 16) | pipe->dst_x);
-
- /* 1 for dma_s, client_id = 0 */
- MDP_OUTP(MDP_BASE + 0x00090, 1);
-
- mddi_vdo_packet_reg = mfd->panel_info.mddi.vdopkt;
-
- if (mfd->panel_info.bpp == 24)
- MDP_OUTP(MDP_BASE + 0x00094,
- (MDDI_VDO_PACKET_DESC_24 << 16) | mddi_vdo_packet_reg);
- else if (mfd->panel_info.bpp == 16)
- MDP_OUTP(MDP_BASE + 0x00094,
- (MDDI_VDO_PACKET_DESC_16 << 16) | mddi_vdo_packet_reg);
- else
- MDP_OUTP(MDP_BASE + 0x00094,
- (MDDI_VDO_PACKET_DESC << 16) | mddi_vdo_packet_reg);
-
- MDP_OUTP(MDP_BASE + 0x00098, 0x01);
-
- MDP_OUTP(MDP_BASE + 0xa0000, dma_s_cfg_reg);
-
- mdp4_mddi_vsync_enable(mfd, pipe, 1);
-
- /* MDP cmd block disable */
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
-}
-
-void mdp4_mddi_dma_s_kickoff(struct msm_fb_data_type *mfd,
- struct mdp4_overlay_pipe *pipe)
-{
- mdp_enable_irq(MDP_DMA_S_TERM);
-
- if (mddi_pipe->ov_blt_addr == 0)
- mfd->dma->busy = TRUE;
-
- mfd->ibuf_flushed = TRUE;
- /* start dma_s pipe */
- mdp_pipe_kickoff(MDP_DMA_S_TERM, mfd);
- mdp4_stat.kickoff_dmas++;
-
- /* wait until DMA finishes the current job */
- wait_for_completion(&mfd->dma->comp);
- mdp_disable_irq(MDP_DMA_S_TERM);
-}
-
-void mdp4_mddi_overlay_dmas_restore(void)
-{
- /* mutex held by caller */
- if (mddi_mfd && mddi_pipe) {
- mdp4_mddi_dma_busy_wait(mddi_mfd);
- mdp4_dma_s_update_lcd(mddi_mfd, mddi_pipe);
- mdp4_mddi_dma_s_kickoff(mddi_mfd, mddi_pipe);
- mddi_mfd->dma_update_flag = 1;
+ /* pipe == rgb1 */
+ mdp4_overlay_unset_mixer(pipe->mixer_num);
+ vctrl->base_pipe = NULL;
+ } else {
+ mdp4_mixer_stage_down(pipe, 1);
+ mdp4_overlay_iommu_pipe_free(pipe->pipe_ndx, 1);
+ }
}
}
void mdp4_mddi_overlay(struct msm_fb_data_type *mfd)
{
- mutex_lock(&mfd->dma->ov_mutex);
+ int cndx = 0;
+ struct vsycn_ctrl *vctrl;
+ struct mdp4_overlay_pipe *pipe;
+ unsigned long flags;
+ long long xx;
- if (mfd && mfd->panel_power_on) {
- mdp4_mddi_dma_busy_wait(mfd);
+ vctrl = &vsync_ctrl_db[cndx];
- if (mddi_pipe && mddi_pipe->ov_blt_addr)
- mdp4_mddi_blt_dmap_busy_wait(mfd);
- mdp4_overlay_mdp_perf_upd(mfd, 0);
- mdp4_overlay_update_lcd(mfd);
+ if (!mfd->panel_power_on)
+ return;
- mdp4_overlay_mdp_perf_upd(mfd, 1);
- if (mdp_hw_revision < MDP4_REVISION_V2_1) {
- /* dmas dmap switch */
- if (mdp4_overlay_mixer_play(mddi_pipe->mixer_num)
- == 0) {
- mdp4_dma_s_update_lcd(mfd, mddi_pipe);
- mdp4_mddi_dma_s_kickoff(mfd, mddi_pipe);
- } else
- mdp4_mddi_kickoff_ui(mfd, mddi_pipe);
- } else /* no dams dmap switch */
- mdp4_mddi_kickoff_ui(mfd, mddi_pipe);
-
- /* signal if pan function is waiting for the update completion */
- if (mfd->pan_waiting) {
- mfd->pan_waiting = FALSE;
- complete(&mfd->pan_comp);
- }
+ pipe = vctrl->base_pipe;
+ if (pipe == NULL) {
+ pr_err("%s: NO base pipe\n", __func__);
+ return;
}
+
+ mutex_lock(&vctrl->update_lock);
+ if (!vctrl->clk_enabled) {
+ pr_err("%s: mdp clocks disabled\n", __func__);
+ mutex_unlock(&vctrl->update_lock);
+ return;
+
+ }
+ mutex_unlock(&vctrl->update_lock);
+
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ if (vctrl->expire_tick) {
+ /*
+ * in the middle of shutting clocks down
+ * delay to allow pan display to go through
+ */
+ vctrl->expire_tick = VSYNC_EXPIRE_TICK;
+ }
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+ if (pipe->mixer_stage == MDP4_MIXER_STAGE_BASE) {
+ mdp4_mddi_vsync_enable(mfd, pipe, 0);
+ mdp4_overlay_setup_pipe_addr(mfd, pipe);
+ mdp4_mddi_pipe_queue(0, pipe);
+ }
+
+ mdp4_overlay_mdp_perf_upd(mfd, 1);
+
+ mutex_lock(&mfd->dma->ov_mutex);
+ mdp4_mddi_pipe_commit();
mutex_unlock(&mfd->dma->ov_mutex);
+ mdp4_mddi_wait4vsync(0, &xx);
+
+ mdp4_overlay_mdp_perf_upd(mfd, 0);
}
int mdp4_mddi_overlay_cursor(struct fb_info *info, struct fb_cursor *cursor)
@@ -722,7 +1061,6 @@
struct msm_fb_data_type *mfd = info->par;
mutex_lock(&mfd->dma->ov_mutex);
if (mfd && mfd->panel_power_on) {
- mdp4_mddi_dma_busy_wait(mfd);
mdp_hw_cursor_update(info, cursor);
}
mutex_unlock(&mfd->dma->ov_mutex);
diff --git a/drivers/video/msm/mdp4_overlay_writeback.c b/drivers/video/msm/mdp4_overlay_writeback.c
index ee7e9ce..18c6635 100644
--- a/drivers/video/msm/mdp4_overlay_writeback.c
+++ b/drivers/video/msm/mdp4_overlay_writeback.c
@@ -45,9 +45,49 @@
WITH_CLIENT
};
-static struct mdp4_overlay_pipe *writeback_pipe;
-static struct msm_fb_data_type *writeback_mfd;
-static int busy_wait_cnt;
+#define MAX_CONTROLLER 1
+#define VSYNC_EXPIRE_TICK 0
+
+static struct vsycn_ctrl {
+ struct device *dev;
+ int inited;
+ int update_ndx;
+ u32 ov_koff;
+ u32 ov_done;
+ atomic_t suspend;
+ struct mutex update_lock;
+ struct completion ov_comp;
+ spinlock_t spin_lock;
+ struct msm_fb_data_type *mfd;
+ struct mdp4_overlay_pipe *base_pipe;
+ struct vsync_update vlist[2];
+} vsync_ctrl_db[MAX_CONTROLLER];
+
+static void vsync_irq_enable(int intr, int term)
+{
+ unsigned long flag;
+
+ spin_lock_irqsave(&mdp_spin_lock, flag);
+ /* no need to clrear other interrupts for comamnd mode */
+ mdp_intr_mask |= intr;
+ outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+ mdp_enable_irq(term);
+ spin_unlock_irqrestore(&mdp_spin_lock, flag);
+}
+
+static void vsync_irq_disable(int intr, int term)
+{
+ unsigned long flag;
+
+ spin_lock_irqsave(&mdp_spin_lock, flag);
+ /* no need to clrear other interrupts for comamnd mode */
+ mdp_intr_mask &= ~intr;
+ outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+ mdp_disable_irq_nosync(term);
+ spin_unlock_irqrestore(&mdp_spin_lock, flag);
+}
+
+static int mdp4_overlay_writeback_update(struct msm_fb_data_type *mfd);
int mdp4_overlay_writeback_on(struct platform_device *pdev)
{
@@ -58,6 +98,8 @@
int bpp;
int ret;
uint32 data;
+ struct vsycn_ctrl *vctrl;
+ int cndx = 0;
mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
@@ -67,7 +109,9 @@
if (mfd->key != MFD_KEY)
return -EINVAL;
- writeback_mfd = mfd; /* keep it */
+ vctrl = &vsync_ctrl_db[cndx];
+ vctrl->mfd = mfd;
+ vctrl->dev = mfd->fbi->dev;
fbi = mfd->fbi;
@@ -77,12 +121,14 @@
fbi->var.yoffset * fbi->fix.line_length;
/* MDP cmd block enable */
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+ mdp_clk_ctrl(1);
- if (writeback_pipe == NULL) {
+ if (vctrl->base_pipe == NULL) {
pipe = mdp4_overlay_pipe_alloc(OVERLAY_TYPE_BF, MDP4_MIXER2);
- if (pipe == NULL)
+ if (pipe == NULL) {
pr_info("%s: pipe_alloc failed\n", __func__);
+ return -EIO;
+ }
pipe->pipe_used++;
pipe->mixer_stage = MDP4_MIXER_STAGE_BASE;
pipe->mixer_num = MDP4_MIXER2;
@@ -92,11 +138,12 @@
if (ret < 0)
pr_info("%s: format2type failed\n", __func__);
- writeback_pipe = pipe; /* keep it */
+ vctrl->base_pipe = pipe; /* keep it */
} else {
- pipe = writeback_pipe;
+ pipe = vctrl->base_pipe;
}
+
ret = panel_next_on(pdev);
/* MDP_LAYERMIXER_WB_MUX_SEL to use mixer1 axi for mixer2 writeback */
@@ -113,46 +160,68 @@
MDP_OUTP(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x5008,
(0x0 & 0xFFF)); /* 12-bit R */
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+ mdp_clk_ctrl(0);
return ret;
}
int mdp4_overlay_writeback_off(struct platform_device *pdev)
{
- int ret;
- struct msm_fb_data_type *mfd =
- (struct msm_fb_data_type *)platform_get_drvdata(pdev);
- if (mfd && writeback_pipe) {
- mdp4_writeback_dma_busy_wait(mfd);
- mdp4_overlay_pipe_free(writeback_pipe);
- mdp4_overlay_panel_mode_unset(writeback_pipe->mixer_num,
- MDP4_PANEL_WRITEBACK);
- writeback_pipe = NULL;
+ int cndx = 0;
+ struct msm_fb_data_type *mfd;
+ struct vsycn_ctrl *vctrl;
+ struct mdp4_overlay_pipe *pipe;
+ int ret = 0;
+
+ pr_debug("%s+:\n", __func__);
+
+ mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+ vctrl = &vsync_ctrl_db[cndx];
+ pipe = vctrl->base_pipe;
+ if (pipe == NULL) {
+ pr_err("%s: NO base pipe\n", __func__);
+ return ret;
}
+
+ /* sanity check, free pipes besides base layer */
+ mdp4_overlay_unset_mixer(pipe->mixer_num);
+ mdp4_mixer_stage_down(pipe, 1);
+ mdp4_overlay_pipe_free(pipe);
+ vctrl->base_pipe = NULL;
+
ret = panel_next_off(pdev);
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+ mdp_clk_ctrl(1);
/* MDP_LAYERMIXER_WB_MUX_SEL to restore to default cfg*/
outpdw(MDP_BASE + 0x100F4, 0x0);
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+ mdp_clk_ctrl(0);
+ pr_debug("%s-:\n", __func__);
return ret;
}
-int mdp4_overlay_writeback_update(struct msm_fb_data_type *mfd)
+
+static int mdp4_overlay_writeback_update(struct msm_fb_data_type *mfd)
{
struct fb_info *fbi;
uint8 *buf;
unsigned int buf_offset;
struct mdp4_overlay_pipe *pipe;
int bpp;
+ int cndx = 0;
+ struct vsycn_ctrl *vctrl;
if (mfd->key != MFD_KEY)
return -ENODEV;
- if (!writeback_pipe)
- return -EINVAL;
fbi = mfd->fbi;
- pipe = writeback_pipe;
+ vctrl = &vsync_ctrl_db[cndx];
+
+ pipe = vctrl->base_pipe;
+ if (!pipe) {
+ pr_err("%s: no base layer pipe\n", __func__);
+ return -EINVAL;
+ }
bpp = fbi->var.bits_per_pixel / 8;
buf = (uint8 *) fbi->fix.smem_start;
@@ -160,7 +229,7 @@
fbi->var.yoffset * fbi->fix.line_length;
/* MDP cmd block enable */
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+ mdp_clk_ctrl(1);
pipe->src_height = fbi->var.yres;
pipe->src_width = fbi->var.xres;
@@ -184,142 +253,190 @@
mdp4_mixer_stage_up(pipe, 0);
mdp4_overlayproc_cfg(pipe);
- mdp4_mixer_stage_commit(pipe->mixer_num);
/* MDP cmd block disable */
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+ mdp_clk_ctrl(0);
wmb();
return 0;
}
-void mdp4_writeback_dma_busy_wait(struct msm_fb_data_type *mfd)
+
+/*
+ * mdp4_wfd_piep_queue:
+ * called from thread context
+ */
+void mdp4_wfd_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe)
{
- unsigned long flag;
- int need_wait = 0;
+ struct vsycn_ctrl *vctrl;
+ struct vsync_update *vp;
+ struct mdp4_overlay_pipe *pp;
+ int undx;
- spin_lock_irqsave(&mdp_spin_lock, flag);
- if (mfd->dma->busy == TRUE) {
- if (busy_wait_cnt == 0)
- INIT_COMPLETION(mfd->dma->comp);
- busy_wait_cnt = 1;
- need_wait++;
- }
- spin_unlock_irqrestore(&mdp_spin_lock, flag);
-
- if (need_wait) {
- /* wait until DMA finishes the current job */
- pr_debug("%s: pending pid=%d\n",
- __func__, current->pid);
- wait_for_completion(&mfd->dma->comp);
- }
-}
-
-void mdp4_overlay1_done_writeback(struct mdp_dma_data *dma)
-{
- spin_lock(&mdp_spin_lock);
- dma->busy = FALSE;
- if (busy_wait_cnt)
- busy_wait_cnt = 0;
- mdp_disable_irq_nosync(MDP_OVERLAY2_TERM);
- spin_unlock(&mdp_spin_lock);
- complete_all(&dma->comp);
- pr_debug("%s ovdone interrupt\n", __func__);
-
-}
-void mdp4_writeback_overlay_kickoff(struct msm_fb_data_type *mfd,
- struct mdp4_overlay_pipe *pipe)
-{
- unsigned long flag;
- spin_lock_irqsave(&mdp_spin_lock, flag);
- mdp_enable_irq(MDP_OVERLAY2_TERM);
-
- mfd->dma->busy = TRUE;
- outp32(MDP_INTR_CLEAR, INTR_OVERLAY2_DONE);
- mdp_intr_mask |= INTR_OVERLAY2_DONE;
- outp32(MDP_INTR_ENABLE, mdp_intr_mask);
-
- wmb(); /* make sure all registers updated */
- spin_unlock_irqrestore(&mdp_spin_lock, flag);
- /* start OVERLAY pipe */
- mdp_pipe_kickoff(MDP_OVERLAY2_TERM, mfd);
- wmb();
- pr_debug("%s: before ov done interrupt\n", __func__);
-}
-void mdp4_writeback_dma_stop(struct msm_fb_data_type *mfd)
-{
- /* mutex holded by caller */
- if (mfd && writeback_pipe) {
- mdp4_writeback_dma_busy_wait(mfd);
- mdp4_overlay_writeback_update(mfd);
-
- mdp4_writeback_overlay_kickoff(mfd, writeback_pipe);
- }
-}
-
-void mdp4_writeback_kickoff_video(struct msm_fb_data_type *mfd,
- struct mdp4_overlay_pipe *pipe)
-{
- struct msmfb_writeback_data_list *node = NULL;
- mutex_lock(&mfd->unregister_mutex);
- mutex_lock(&mfd->writeback_mutex);
- if (!list_empty(&mfd->writeback_free_queue)
- && mfd->writeback_state != WB_STOPING
- && mfd->writeback_state != WB_STOP) {
- node = list_first_entry(&mfd->writeback_free_queue,
- struct msmfb_writeback_data_list, active_entry);
- }
- if (node) {
- list_del(&(node->active_entry));
- node->state = IN_BUSY_QUEUE;
- mfd->writeback_active_cnt++;
- }
- mutex_unlock(&mfd->writeback_mutex);
-
- writeback_pipe->ov_blt_addr = (ulong) (node ? node->addr : NULL);
-
- /* free previous iommu at freelist back to pool */
- mdp4_overlay_iommu_unmap_freelist(writeback_pipe->mixer_num);
-
- if (!writeback_pipe->ov_blt_addr) {
- pr_err("%s: no writeback buffer 0x%x, %p\n", __func__,
- (unsigned int)writeback_pipe->ov_blt_addr, node);
- mutex_unlock(&mfd->unregister_mutex);
+ if (cndx >= MAX_CONTROLLER) {
+ pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
return;
}
- if (writeback_pipe->blt_cnt == 0)
- mdp4_overlay_writeback_update(mfd);
+ vctrl = &vsync_ctrl_db[cndx];
- pr_debug("%s: pid=%d\n", __func__, current->pid);
+ if (atomic_read(&vctrl->suspend) > 0)
+ return;
- mdp4_mixer_stage_commit(pipe->mixer_num);
+ mutex_lock(&vctrl->update_lock);
+ undx = vctrl->update_ndx;
+ vp = &vctrl->vlist[undx];
- mdp4_writeback_overlay_kickoff(mfd, pipe);
- mdp4_writeback_dma_busy_wait(mfd);
+ pp = &vp->plist[pipe->pipe_ndx - 1]; /* ndx start form 1 */
- /* move current committed iommu to freelist */
- mdp4_overlay_iommu_pipe_free(pipe->pipe_ndx, 0);
+ pr_debug("%s: vndx=%d pipe_ndx=%d pid=%d\n", __func__,
+ undx, pipe->pipe_ndx, current->pid);
- mutex_lock(&mfd->writeback_mutex);
- list_add_tail(&node->active_entry, &mfd->writeback_busy_queue);
- mutex_unlock(&mfd->writeback_mutex);
- mfd->writeback_active_cnt--;
- mutex_unlock(&mfd->unregister_mutex);
- wake_up(&mfd->wait_q);
+ *pp = *pipe; /* clone it */
+ vp->update_cnt++;
+
+ mutex_unlock(&vctrl->update_lock);
+ mdp4_stat.overlay_play[pipe->mixer_num]++;
}
-void mdp4_writeback_kickoff_ui(struct msm_fb_data_type *mfd,
- struct mdp4_overlay_pipe *pipe)
-{
- mdp4_mixer_stage_commit(pipe->mixer_num);
+static void mdp4_wfd_wait4ov(int cndx);
- pr_debug("%s: pid=%d\n", __func__, current->pid);
- mdp4_writeback_overlay_kickoff(mfd, pipe);
+int mdp4_wfd_pipe_commit(void)
+{
+ int i, undx;
+ int mixer = 0;
+ struct vsycn_ctrl *vctrl;
+ struct vsync_update *vp;
+ struct mdp4_overlay_pipe *pipe;
+ struct mdp4_overlay_pipe *real_pipe;
+ unsigned long flags;
+ int cnt = 0;
+
+ vctrl = &vsync_ctrl_db[0];
+
+ mutex_lock(&vctrl->update_lock);
+ undx = vctrl->update_ndx;
+ vp = &vctrl->vlist[undx];
+ pipe = vctrl->base_pipe;
+ mixer = pipe->mixer_num;
+
+ if (vp->update_cnt == 0) {
+ mutex_unlock(&vctrl->update_lock);
+ return cnt;
+ }
+
+ vctrl->update_ndx++;
+ vctrl->update_ndx &= 0x01;
+ vp->update_cnt = 0; /* reset */
+ mutex_unlock(&vctrl->update_lock);
+
+ /* free previous committed iommu back to pool */
+ mdp4_overlay_iommu_unmap_freelist(mixer);
+
+ pipe = vp->plist;
+ for (i = 0; i < OVERLAY_PIPE_MAX; i++, pipe++) {
+ if (pipe->pipe_used) {
+ cnt++;
+ real_pipe = mdp4_overlay_ndx2pipe(pipe->pipe_ndx);
+ if (real_pipe && real_pipe->pipe_used) {
+ /* pipe not unset */
+ mdp4_overlay_vsync_commit(pipe);
+ }
+ /* free previous iommu to freelist
+ * which will be freed at next
+ * pipe_commit
+ */
+ mdp4_overlay_iommu_pipe_free(pipe->pipe_ndx, 0);
+ pipe->pipe_used = 0; /* clear */
+ }
+ }
+
+ mdp4_mixer_stage_commit(mixer);
+
+ pipe = vctrl->base_pipe;
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ vctrl->ov_koff++;
+ INIT_COMPLETION(vctrl->ov_comp);
+ vsync_irq_enable(INTR_OVERLAY2_DONE, MDP_OVERLAY2_TERM);
+ pr_debug("%s: kickoff\n", __func__);
+ /* kickoff overlay engine */
+ mdp4_stat.kickoff_ov2++;
+ outpdw(MDP_BASE + 0x00D0, 0);
+ mb(); /* make sure kickoff executed */
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+ mdp4_stat.overlay_commit[pipe->mixer_num]++;
+
+ return cnt;
+}
+
+void mdp4_wfd_init(int cndx)
+{
+ struct vsycn_ctrl *vctrl;
+
+ if (cndx >= MAX_CONTROLLER) {
+ pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+ return;
+ }
+
+ vctrl = &vsync_ctrl_db[cndx];
+ if (vctrl->inited)
+ return;
+
+ vctrl->inited = 1;
+ vctrl->update_ndx = 0;
+ mutex_init(&vctrl->update_lock);
+ init_completion(&vctrl->ov_comp);
+ spin_lock_init(&vctrl->spin_lock);
+}
+
+static void mdp4_wfd_wait4ov(int cndx)
+{
+ struct vsycn_ctrl *vctrl;
+
+ if (cndx >= MAX_CONTROLLER) {
+ pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+ return;
+ }
+
+ vctrl = &vsync_ctrl_db[cndx];
+
+ if (atomic_read(&vctrl->suspend) > 0)
+ return;
+
+ wait_for_completion(&vctrl->ov_comp);
+}
+
+
+void mdp4_overlay2_done_wfd(struct mdp_dma_data *dma)
+{
+ struct vsycn_ctrl *vctrl;
+ struct mdp4_overlay_pipe *pipe;
+ int cndx = 0;
+
+ vctrl = &vsync_ctrl_db[cndx];
+ pipe = vctrl->base_pipe;
+
+ spin_lock(&vctrl->spin_lock);
+ vsync_irq_disable(INTR_OVERLAY2_DONE, MDP_OVERLAY2_TERM);
+ vctrl->ov_done++;
+ complete(&vctrl->ov_comp);
+
+ pr_debug("%s ovdone interrupt\n", __func__);
+ spin_unlock(&vctrl->spin_lock);
}
void mdp4_writeback_overlay(struct msm_fb_data_type *mfd)
{
- int ret = 0;
struct msmfb_writeback_data_list *node = NULL;
+ struct vsycn_ctrl *vctrl;
+ struct mdp4_overlay_pipe *pipe;
+
+ if (mfd && !mfd->panel_power_on)
+ return;
+
+ pr_debug("%s:+ mfd=%x\n", __func__, (int)mfd);
+
+ vctrl = &vsync_ctrl_db[0];
+ pipe = vctrl->base_pipe;
mutex_lock(&mfd->unregister_mutex);
mutex_lock(&mfd->writeback_mutex);
@@ -336,44 +453,36 @@
}
mutex_unlock(&mfd->writeback_mutex);
- writeback_pipe->ov_blt_addr = (ulong) (node ? node->addr : NULL);
+ pipe->ov_blt_addr = (ulong) (node ? node->addr : NULL);
+
+ if (!pipe->ov_blt_addr) {
+ pr_err("%s: no writeback buffer 0x%x, %p\n", __func__,
+ (unsigned int)pipe->ov_blt_addr, node);
+ mutex_unlock(&mfd->unregister_mutex);
+ return;
+ }
mutex_lock(&mfd->dma->ov_mutex);
- pr_debug("%s in writeback\n", __func__);
- if (writeback_pipe && !writeback_pipe->ov_blt_addr) {
+ if (pipe && !pipe->ov_blt_addr) {
pr_err("%s: no writeback buffer 0x%x\n", __func__,
- (unsigned int)writeback_pipe->ov_blt_addr);
- ret = mdp4_overlay_writeback_update(mfd);
- if (ret)
- pr_err("%s: update failed writeback pipe NULL\n",
- __func__);
+ (unsigned int)pipe->ov_blt_addr);
goto fail_no_blt_addr;
}
- if (mfd && mfd->panel_power_on) {
- pr_debug("%s in before busy wait\n", __func__);
- mdp4_writeback_dma_busy_wait(mfd);
+ if (pipe->pipe_type == OVERLAY_TYPE_RGB)
+ mdp4_wfd_pipe_queue(0, pipe);
- pr_debug("%s in before update\n", __func__);
- ret = mdp4_overlay_writeback_update(mfd);
- if (ret) {
- pr_err("%s: update failed writeback pipe NULL\n",
- __func__);
- goto fail_no_blt_addr;
- }
+ mdp4_overlay_mdp_perf_upd(mfd, 1);
- pr_debug("%s: in writeback pan display 0x%x\n", __func__,
- (unsigned int)writeback_pipe->ov_blt_addr);
- mdp4_writeback_kickoff_ui(mfd, writeback_pipe);
- mdp4_iommu_unmap(writeback_pipe);
+ mdp_clk_ctrl(1);
+ mdp4_overlay_writeback_update(mfd);
- /* signal if pan function is waiting for the
- * update completion */
- if (mfd->pan_waiting) {
- mfd->pan_waiting = FALSE;
- complete(&mfd->pan_comp);
- }
- }
+ mdp4_wfd_pipe_commit();
+
+ mdp4_overlay_mdp_perf_upd(mfd, 0);
+
+ mdp4_wfd_wait4ov(0);
+ mdp_clk_ctrl(0);
mutex_lock(&mfd->writeback_mutex);
list_add_tail(&node->active_entry, &mfd->writeback_busy_queue);
@@ -385,7 +494,9 @@
mdp4_overlay_resource_release();*/
mutex_unlock(&mfd->dma->ov_mutex);
mutex_unlock(&mfd->unregister_mutex);
+ pr_debug("%s:-\n", __func__);
}
+
static int mdp4_overlay_writeback_register_buffer(
struct msm_fb_data_type *mfd, struct msmfb_writeback_data_list *node)
{
diff --git a/drivers/video/msm/mdp4_util.c b/drivers/video/msm/mdp4_util.c
index 87921e6..516722e 100644
--- a/drivers/video/msm/mdp4_util.c
+++ b/drivers/video/msm/mdp4_util.c
@@ -557,7 +557,7 @@
mdp4_dmap_done_dsi_cmd(0);
#else
else { /* MDDI */
- mdp4_dma_p_done_mddi(dma);
+ mdp4_dmap_done_mddi(0);
mdp_pipe_ctrl(MDP_DMA2_BLOCK,
MDP_BLOCK_POWER_OFF, TRUE);
complete(&dma->comp);
@@ -608,7 +608,7 @@
mdp4_overlay0_done_dsi_cmd(0);
#else
if (panel & MDP4_PANEL_MDDI)
- mdp4_overlay0_done_mddi(dma);
+ mdp4_overlay0_done_mddi(0);
#endif
}
mdp_hw_cursor_done();
@@ -635,14 +635,8 @@
if (isr & INTR_OVERLAY2_DONE) {
mdp4_stat.intr_overlay2++;
/* disable DTV interrupt */
- dma = &dma_wb_data;
- spin_lock(&mdp_spin_lock);
- mdp_intr_mask &= ~INTR_OVERLAY2_DONE;
- outp32(MDP_INTR_ENABLE, mdp_intr_mask);
- dma->waiting = FALSE;
- spin_unlock(&mdp_spin_lock);
if (panel & MDP4_PANEL_WRITEBACK)
- mdp4_overlay1_done_writeback(dma);
+ mdp4_overlay2_done_wfd(&dma_wb_data);
}
#endif
#endif /* OVERLAY */
diff --git a/drivers/video/msm/mdp_debugfs.c b/drivers/video/msm/mdp_debugfs.c
index 0fad0a7..54f5ef5 100644
--- a/drivers/video/msm/mdp_debugfs.c
+++ b/drivers/video/msm/mdp_debugfs.c
@@ -719,84 +719,6 @@
.write = pmdh_reg_write,
};
-
-
-#if defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_MDDI)
-static int vsync_reg_open(struct inode *inode, struct file *file)
-{
- /* non-seekable */
- file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
- return 0;
-}
-
-static int vsync_reg_release(struct inode *inode, struct file *file)
-{
- return 0;
-}
-
-static ssize_t vsync_reg_write(
- struct file *file,
- const char __user *buff,
- size_t count,
- loff_t *ppos)
-{
- uint32 enable;
- int cnt;
-
- if (count >= sizeof(debug_buf))
- return -EFAULT;
-
- if (copy_from_user(debug_buf, buff, count))
- return -EFAULT;
-
- debug_buf[count] = 0; /* end of string */
-
- cnt = sscanf(debug_buf, "%x", &enable);
-
- mdp_dmap_vsync_set(enable);
-
- return count;
-}
-
-static ssize_t vsync_reg_read(
- struct file *file,
- char __user *buff,
- size_t count,
- loff_t *ppos)
-{
- char *bp;
- int len = 0;
- int tot = 0;
- int dlen;
-
- if (*ppos)
- return 0; /* the end */
-
- bp = debug_buf;
- dlen = sizeof(debug_buf);
- len = snprintf(bp, dlen, "%x\n", mdp_dmap_vsync_get());
- tot += len;
- bp += len;
- *bp = 0;
- tot++;
-
- if (copy_to_user(buff, debug_buf, tot))
- return -EFAULT;
-
- *ppos += tot; /* increase offset */
-
- return tot;
-}
-
-
-static const struct file_operations vsync_fops = {
- .open = vsync_reg_open,
- .release = vsync_reg_release,
- .read = vsync_reg_read,
- .write = vsync_reg_write,
-};
-#endif
-
static ssize_t emdh_reg_write(
struct file *file,
const char __user *buff,
@@ -1342,15 +1264,6 @@
return -1;
}
-#if defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_MDDI)
- if (debugfs_create_file("vsync", 0644, dent, 0, &vsync_fops)
- == NULL) {
- printk(KERN_ERR "%s(%d): debugfs_create_file: debug fail\n",
- __FILE__, __LINE__);
- return -1;
- }
-#endif
-
dent = debugfs_create_dir("emdh", NULL);
if (IS_ERR(dent)) {
diff --git a/drivers/video/msm/mdss/Makefile b/drivers/video/msm/mdss/Makefile
index ddb6dd9..b4bd31e 100644
--- a/drivers/video/msm/mdss/Makefile
+++ b/drivers/video/msm/mdss/Makefile
@@ -7,14 +7,16 @@
mdss-mdp-objs += mdss_mdp_wb.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss-mdp.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss_fb.o
-obj-$(CONFIG_FB_MSM_MDSS) += mdss_io_util.o
mdss-dsi-objs := mdss_dsi.o mdss_dsi_host.o
mdss-dsi-objs += mdss_dsi_panel.o
mdss-dsi-objs += msm_mdss_io_8974.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss-dsi.o
+obj-$(CONFIG_FB_MSM_MDSS) += mdss_edp.o
+obj-$(CONFIG_FB_MSM_MDSS) += mdss_io_util.o
obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_tx.o
obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_util.o
obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_edid.o
+
obj-$(CONFIG_FB_MSM_MDSS_WRITEBACK) += mdss_wb.o
diff --git a/drivers/video/msm/mdss/mdss_edp.c b/drivers/video/msm/mdss/mdss_edp.c
new file mode 100644
index 0000000..9460d71
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_edp.c
@@ -0,0 +1,526 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/err.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+
+#include <mach/hardware.h>
+#include <mach/dma.h>
+
+#include "mdss_edp.h"
+
+#define RGB_COMPONENTS 3
+#define VDDA_MIN_UV 1800000 /* uV units */
+#define VDDA_MAX_UV 1800000 /* uV units */
+#define VDDA_UA_ON_LOAD 100000 /* uA units */
+#define VDDA_UA_OFF_LOAD 100 /* uA units */
+
+
+static int mdss_edp_get_base_address(struct mdss_edp_drv_pdata *edp_drv);
+static int mdss_edp_get_mmss_cc_base_address(struct mdss_edp_drv_pdata
+ *edp_drv);
+static int mdss_edp_regulator_init(struct mdss_edp_drv_pdata *edp_drv);
+static int mdss_edp_regulator_on(struct mdss_edp_drv_pdata *edp_drv);
+static int mdss_edp_regulator_off(struct mdss_edp_drv_pdata *edp_drv);
+
+static int mdss_edp_gpio_panel_en(struct mdss_edp_drv_pdata *edp_drv);
+
+static void mdss_edp_edid2pinfo(struct mdss_edp_drv_pdata *edp_drv);
+static void mdss_edp_fill_edid_data(struct mdss_edp_drv_pdata *edp_drv);
+static void mdss_edp_fill_dpcd_data(struct mdss_edp_drv_pdata *edp_drv);
+
+static int mdss_edp_device_register(struct mdss_edp_drv_pdata *edp_drv);
+
+static void mdss_edp_config_sync(unsigned char *edp_base);
+static void mdss_edp_config_sw_div(unsigned char *edp_base);
+static void mdss_edp_config_static_mdiv(unsigned char *edp_base);
+static void mdss_edp_enable(unsigned char *edp_base, int enable);
+
+/*
+ * Init regulator needed for edp, 8974_l12
+ */
+static int mdss_edp_regulator_init(struct mdss_edp_drv_pdata *edp_drv)
+{
+ int ret;
+
+ edp_drv->vdda_vreg = devm_regulator_get(&(edp_drv->pdev->dev), "vdda");
+ if (IS_ERR(edp_drv->vdda_vreg)) {
+ pr_err("%s: Could not get 8941_l12, ret = %ld\n", __func__,
+ PTR_ERR(edp_drv->vdda_vreg));
+ return -ENODEV;
+ }
+
+ ret = regulator_set_voltage(edp_drv->vdda_vreg,
+ VDDA_MIN_UV, VDDA_MAX_UV);
+ if (ret) {
+ pr_err("%s: vdda_vreg set_voltage failed, ret=%d\n", __func__,
+ ret);
+ return -EINVAL;
+ }
+
+ ret = mdss_edp_regulator_on(edp_drv);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * Set uA and enable vdda
+ */
+static int mdss_edp_regulator_on(struct mdss_edp_drv_pdata *edp_drv)
+{
+ int ret;
+
+ ret = regulator_set_optimum_mode(edp_drv->vdda_vreg, VDDA_UA_ON_LOAD);
+ if (ret < 0) {
+ pr_err("%s: vdda_vreg set regulator mode failed.\n", __func__);
+ return ret;
+ }
+
+ ret = regulator_enable(edp_drv->vdda_vreg);
+ if (ret) {
+ pr_err("%s: Failed to enable vdda_vreg regulator.\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Disable vdda and set uA
+ */
+static int mdss_edp_regulator_off(struct mdss_edp_drv_pdata *edp_drv)
+{
+ int ret;
+
+ ret = regulator_disable(edp_drv->vdda_vreg);
+ if (ret) {
+ pr_err("%s: Failed to disable vdda_vreg regulator.\n",
+ __func__);
+ return ret;
+ }
+
+ ret = regulator_set_optimum_mode(edp_drv->vdda_vreg, VDDA_UA_OFF_LOAD);
+ if (ret < 0) {
+ pr_err("%s: vdda_vreg set regulator mode failed.\n",
+ __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Enables the gpio that supply power to the panel
+ */
+static int mdss_edp_gpio_panel_en(struct mdss_edp_drv_pdata *edp_drv)
+{
+ int ret = 0;
+
+ edp_drv->gpio_panel_en = of_get_named_gpio(edp_drv->pdev->dev.of_node,
+ "gpio-panel-en", 0);
+ if (!gpio_is_valid(edp_drv->gpio_panel_en)) {
+ pr_err("%s: gpio_panel_en not specified\n", __func__);
+ goto gpio_err;
+ }
+
+ ret = gpio_request(edp_drv->gpio_panel_en, "disp_enable");
+ if (ret) {
+ pr_err("%s: Request reset gpio_panel_en failed, ret=%d\n",
+ __func__, ret);
+ goto gpio_free;
+ }
+
+ ret = gpio_direction_output(edp_drv->gpio_panel_en, 1);
+ if (ret) {
+ pr_err("%s: Set direction for gpio_panel_en failed, ret=%d\n",
+ __func__, ret);
+ goto gpio_free;
+ }
+
+ gpio_set_value(edp_drv->gpio_panel_en, 1);
+
+ return 0;
+
+gpio_free:
+ gpio_free(edp_drv->gpio_panel_en);
+gpio_err:
+ return -ENODEV;
+}
+
+static void mdss_edp_config_sync(unsigned char *edp_base)
+{
+ int ret = 0;
+
+ ret = edp_read(edp_base + 0xc); /* EDP_CONFIGURATION_CTRL */
+ ret &= ~0x733;
+ ret |= (0x55 & 0x733);
+ edp_write(edp_base + 0xc, ret);
+ edp_write(edp_base + 0xc, 0x55); /* EDP_CONFIGURATION_CTRL */
+}
+
+static void mdss_edp_config_sw_div(unsigned char *edp_base)
+{
+ edp_write(edp_base + 0x14, 0x13b); /* EDP_SOFTWARE_MVID */
+ edp_write(edp_base + 0x18, 0x266); /* EDP_SOFTWARE_NVID */
+}
+
+static void mdss_edp_config_static_mdiv(unsigned char *edp_base)
+{
+ int ret = 0;
+
+ ret = edp_read(edp_base + 0xc); /* EDP_CONFIGURATION_CTRL */
+ edp_write(edp_base + 0xc, ret | 0x2); /* EDP_CONFIGURATION_CTRL */
+ edp_write(edp_base + 0xc, 0x57); /* EDP_CONFIGURATION_CTRL */
+}
+
+static void mdss_edp_enable(unsigned char *edp_base, int enable)
+{
+ edp_write(edp_base + 0x8, 0x0); /* EDP_STATE_CTRL */
+ edp_write(edp_base + 0x8, 0x40); /* EDP_STATE_CTRL */
+ edp_write(edp_base + 0x94, enable); /* EDP_TIMING_ENGINE_EN */
+ edp_write(edp_base + 0x4, enable); /* EDP_MAINLINK_CTRL */
+}
+
+int mdss_edp_on(struct mdss_panel_data *pdata)
+{
+ struct mdss_edp_drv_pdata *edp_drv = NULL;
+ int i;
+
+ edp_drv = container_of(pdata, struct mdss_edp_drv_pdata,
+ panel_data);
+ if (!edp_drv) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+
+ mdss_edp_prepare_clocks(edp_drv);
+ mdss_edp_clk_enable(edp_drv);
+ mdss_edp_phy_sw_reset(edp_drv->edp_base);
+ mdss_edp_hw_powerup(edp_drv->edp_base, 1);
+ mdss_edp_pll_configure(edp_drv->edp_base, edp_drv->edid.timing[0].pclk);
+
+ for (i = 0; i < edp_drv->dpcd.max_lane_count; ++i)
+ mdss_edp_enable_lane_bist(edp_drv->edp_base, i, 1);
+
+ mdss_edp_enable_mainlink(edp_drv->edp_base, 1);
+ mdss_edp_config_clk(edp_drv->edp_base, edp_drv->mmss_cc_base);
+
+ mdss_edp_phy_misc_cfg(edp_drv->edp_base);
+ mdss_edp_config_sync(edp_drv->edp_base);
+ mdss_edp_config_sw_div(edp_drv->edp_base);
+ mdss_edp_config_static_mdiv(edp_drv->edp_base);
+ mdss_edp_enable(edp_drv->edp_base, 1);
+
+ return 0;
+}
+
+int mdss_edp_off(struct mdss_panel_data *pdata)
+{
+ struct mdss_edp_drv_pdata *edp_drv = NULL;
+ int ret = 0;
+ int i;
+
+ edp_drv = container_of(pdata, struct mdss_edp_drv_pdata,
+ panel_data);
+ if (!edp_drv) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+
+ mdss_edp_enable(edp_drv->edp_base, 0);
+ mdss_edp_unconfig_clk(edp_drv->edp_base, edp_drv->mmss_cc_base);
+ mdss_edp_enable_mainlink(edp_drv->edp_base, 0);
+
+ for (i = 0; i < edp_drv->dpcd.max_lane_count; ++i)
+ mdss_edp_enable_lane_bist(edp_drv->edp_base, i, 0);
+
+ mdss_edp_hw_powerup(edp_drv->edp_base, 0);
+ mdss_edp_clk_disable(edp_drv);
+ mdss_edp_unprepare_clocks(edp_drv);
+
+ return ret;
+}
+
+/*
+ * Converts from EDID struct to mdss_panel_info
+ */
+static void mdss_edp_edid2pinfo(struct mdss_edp_drv_pdata *edp_drv)
+{
+ struct display_timing_desc *dp;
+ struct mdss_panel_info *pinfo;
+
+ dp = &edp_drv->edid.timing[0];
+ pinfo = &edp_drv->panel_data.panel_info;
+
+ pinfo->clk_rate = dp->pclk;
+
+ pinfo->xres = dp->h_addressable + dp->h_border * 2;
+ pinfo->yres = dp->v_addressable + dp->v_border * 2;
+
+ pinfo->lcdc.h_back_porch = dp->h_blank - dp->h_fporch \
+ - dp->h_sync_pulse;
+ pinfo->lcdc.h_front_porch = dp->h_fporch;
+ pinfo->lcdc.h_pulse_width = dp->h_sync_pulse;
+
+ pinfo->lcdc.v_back_porch = dp->v_blank - dp->v_fporch \
+ - dp->v_sync_pulse;
+ pinfo->lcdc.v_front_porch = dp->v_fporch;
+ pinfo->lcdc.v_pulse_width = dp->v_sync_pulse;
+
+ pinfo->type = EDP_PANEL;
+ pinfo->pdest = DISPLAY_1;
+ pinfo->wait_cycle = 0;
+ pinfo->bpp = edp_drv->edid.color_depth * RGB_COMPONENTS;
+ pinfo->fb_num = 2;
+
+ pinfo->lcdc.border_clr = 0; /* black */
+ pinfo->lcdc.underflow_clr = 0xff; /* blue */
+ pinfo->lcdc.hsync_skew = 0;
+}
+
+static int __devexit mdss_edp_remove(struct platform_device *pdev)
+{
+ struct mdss_edp_drv_pdata *edp_drv = NULL;
+
+ edp_drv = platform_get_drvdata(pdev);
+
+ gpio_free(edp_drv->gpio_panel_en);
+ mdss_edp_regulator_off(edp_drv);
+ iounmap(edp_drv->edp_base);
+ iounmap(edp_drv->mmss_cc_base);
+ edp_drv->edp_base = NULL;
+
+ return 0;
+}
+
+static int mdss_edp_device_register(struct mdss_edp_drv_pdata *edp_drv)
+{
+ int ret;
+
+ mdss_edp_edid2pinfo(edp_drv);
+
+ edp_drv->panel_data.on = mdss_edp_on;
+ edp_drv->panel_data.off = mdss_edp_off;
+
+ ret = mdss_register_panel(&edp_drv->panel_data);
+ if (ret) {
+ dev_err(&(edp_drv->pdev->dev), "unable to register eDP\n");
+ return ret;
+ }
+
+ pr_debug("%s: eDP initialized\n", __func__);
+
+ return 0;
+}
+
+/*
+ * Retrieve edp base address
+ */
+static int mdss_edp_get_base_address(struct mdss_edp_drv_pdata *edp_drv)
+{
+ struct resource *res;
+
+ res = platform_get_resource_byname(edp_drv->pdev, IORESOURCE_MEM,
+ "edp_base");
+ if (!res) {
+ pr_err("%s: Unable to get the MDSS EDP resources", __func__);
+ return -ENOMEM;
+ }
+
+ edp_drv->edp_base = ioremap(res->start, resource_size(res));
+ if (!edp_drv->edp_base) {
+ pr_err("%s: Unable to remap EDP resources", __func__);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int mdss_edp_get_mmss_cc_base_address(struct mdss_edp_drv_pdata
+ *edp_drv)
+{
+ struct resource *res;
+
+ res = platform_get_resource_byname(edp_drv->pdev, IORESOURCE_MEM,
+ "mmss_cc_base");
+ if (!res) {
+ pr_err("%s: Unable to get the MMSS_CC resources", __func__);
+ return -ENOMEM;
+ }
+
+ edp_drv->mmss_cc_base = ioremap(res->start, resource_size(res));
+ if (!edp_drv->mmss_cc_base) {
+ pr_err("%s: Unable to remap MMSS_CC resources", __func__);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void mdss_edp_fill_edid_data(struct mdss_edp_drv_pdata *edp_drv)
+{
+ struct edp_edid *edid = &edp_drv->edid;
+
+ edid->id_name[0] = 'A';
+ edid->id_name[0] = 'U';
+ edid->id_name[0] = 'O';
+ edid->id_name[0] = 0;
+ edid->id_product = 0x305D;
+ edid->version = 1;
+ edid->revision = 4;
+ edid->ext_block_cnt = 0;
+ edid->video_digital = 0x5;
+ edid->color_depth = 6;
+ edid->dpm = 0;
+ edid->color_format = 0;
+ edid->timing[0].pclk = 138500000;
+ edid->timing[0].h_addressable = 1920;
+ edid->timing[0].h_blank = 160;
+ edid->timing[0].v_addressable = 1080;
+ edid->timing[0].v_blank = 30;
+ edid->timing[0].h_fporch = 48;
+ edid->timing[0].h_sync_pulse = 32;
+ edid->timing[0].v_sync_pulse = 14;
+ edid->timing[0].v_fporch = 8;
+ edid->timing[0].width_mm = 256;
+ edid->timing[0].height_mm = 144;
+ edid->timing[0].h_border = 0;
+ edid->timing[0].v_border = 0;
+ edid->timing[0].interlaced = 0;
+ edid->timing[0].stereo = 0;
+ edid->timing[0].sync_type = 1;
+ edid->timing[0].sync_separate = 1;
+ edid->timing[0].vsync_pol = 0;
+ edid->timing[0].hsync_pol = 0;
+
+}
+
+static void mdss_edp_fill_dpcd_data(struct mdss_edp_drv_pdata *edp_drv)
+{
+ struct dpcd_cap *cap = &edp_drv->dpcd;
+
+ cap->max_lane_count = 2;
+ cap->max_link_clk = 270;
+}
+
+
+static int __devinit mdss_edp_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct mdss_edp_drv_pdata *edp_drv;
+
+ if (!pdev->dev.of_node) {
+ pr_err("%s: Failed\n", __func__);
+ return -EPERM;
+ }
+
+ edp_drv = devm_kzalloc(&pdev->dev, sizeof(*edp_drv), GFP_KERNEL);
+ if (edp_drv == NULL) {
+ pr_err("%s: Failed, could not allocate edp_drv", __func__);
+ return -ENOMEM;
+ }
+
+ edp_drv->pdev = pdev;
+ edp_drv->pdev->id = 1;
+ edp_drv->clk_on = 0;
+
+ ret = mdss_edp_get_base_address(edp_drv);
+ if (ret)
+ goto probe_err;
+
+ ret = mdss_edp_get_mmss_cc_base_address(edp_drv);
+ if (ret)
+ goto edp_base_unmap;
+
+ ret = mdss_edp_regulator_init(edp_drv);
+ if (ret)
+ goto mmss_cc_base_unmap;
+
+ ret = mdss_edp_clk_init(edp_drv);
+ if (ret)
+ goto edp_clk_deinit;
+
+ ret = mdss_edp_gpio_panel_en(edp_drv);
+ if (ret)
+ goto edp_clk_deinit;
+
+ mdss_edp_fill_edid_data(edp_drv);
+ mdss_edp_fill_dpcd_data(edp_drv);
+ mdss_edp_device_register(edp_drv);
+
+ return 0;
+
+edp_clk_deinit:
+ mdss_edp_clk_deinit(edp_drv);
+ mdss_edp_regulator_off(edp_drv);
+mmss_cc_base_unmap:
+ iounmap(edp_drv->mmss_cc_base);
+edp_base_unmap:
+ iounmap(edp_drv->edp_base);
+probe_err:
+ return ret;
+
+}
+
+static const struct of_device_id msm_mdss_edp_dt_match[] = {
+ {.compatible = "qcom,mdss-edp"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, msm_mdss_edp_dt_match);
+
+static struct platform_driver mdss_edp_driver = {
+ .probe = mdss_edp_probe,
+ .remove = __devexit_p(mdss_edp_remove),
+ .shutdown = NULL,
+ .driver = {
+ .name = "mdss_edp",
+ .of_match_table = msm_mdss_edp_dt_match,
+ },
+};
+
+static int __init mdss_edp_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&mdss_edp_driver);
+ if (ret) {
+ pr_err("%s driver register failed", __func__);
+ return ret;
+ }
+
+ return ret;
+}
+module_init(mdss_edp_init);
+
+static void __exit mdss_edp_driver_cleanup(void)
+{
+ platform_driver_unregister(&mdss_edp_driver);
+}
+module_exit(mdss_edp_driver_cleanup);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("eDP controller driver");
diff --git a/drivers/video/msm/mdss/mdss_edp.h b/drivers/video/msm/mdss/mdss_edp.h
new file mode 100644
index 0000000..72c061f
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_edp.h
@@ -0,0 +1,113 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MDSS_EDP_H
+#define MDSS_EDP_H
+
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+
+#include "mdss_panel.h"
+
+#define edp_read(offset) readl_relaxed((offset))
+#define edp_write(offset, data) writel_relaxed((data), (offset))
+
+struct display_timing_desc {
+ u32 pclk;
+ u32 h_addressable; /* addressable + boder = active */
+ u32 h_border;
+ u32 h_blank; /* fporch + bporch + sync_pulse = blank */
+ u32 h_fporch;
+ u32 h_sync_pulse;
+ u32 v_addressable; /* addressable + boder = active */
+ u32 v_border;
+ u32 v_blank; /* fporch + bporch + sync_pulse = blank */
+ u32 v_fporch;
+ u32 v_sync_pulse;
+ u32 width_mm;
+ u32 height_mm;
+ u32 interlaced;
+ u32 stereo;
+ u32 sync_type;
+ u32 sync_separate;
+ u32 vsync_pol;
+ u32 hsync_pol;
+};
+
+struct edp_edid {
+ char id_name[4];
+ short id_product;
+ char version;
+ char revision;
+ char video_digital;
+ char color_depth; /* 6, 8, 10, 12 and 14 bits */
+ char color_format; /* RGB 4:4:4, YCrCb 4:4:4, Ycrcb 4:2:2 */
+ char dpm; /* display power management */
+ char sync_digital; /* 1 = digital */
+ char sync_separate; /* 1 = separate */
+ char vsync_pol; /* 0 = negative, 1 = positive */
+ char hsync_pol; /* 0 = negative, 1 = positive */
+ char ext_block_cnt;
+ struct display_timing_desc timing[4];
+};
+
+struct dpcd_cap {
+ char max_lane_count;
+ u32 max_link_clk; /* 162, 270 and 540 Mb, divided by 10 */
+};
+
+struct mdss_edp_drv_pdata {
+ /* device driver */
+ int (*on) (struct mdss_panel_data *pdata);
+ int (*off) (struct mdss_panel_data *pdata);
+ struct platform_device *pdev;
+
+ /* edp specific */
+ struct mdss_panel_data panel_data;
+ unsigned char *edp_base;
+ unsigned char *mmss_cc_base;
+ struct edp_edid edid;
+ struct dpcd_cap dpcd;
+
+ /* regulators */
+ struct regulator *vdda_vreg;
+
+ /* clocks */
+ struct clk *aux_clk;
+ struct clk *pixel_clk;
+ struct clk *ahb_clk;
+ struct clk *link_clk;
+ int clk_on;
+
+ /* gpios */
+ int gpio_panel_en;
+};
+
+void mdss_edp_phy_sw_reset(unsigned char *edp_base);
+void mdss_edp_pll_configure(unsigned char *edp_base, int rate);
+void mdss_edp_enable_lane_bist(unsigned char *edp_base, int lane, int enable);
+void mdss_edp_enable_mainlink(unsigned char *edp_base, int enable);
+void mdss_edp_hw_powerup(unsigned char *edp_base, int enable);
+void mdss_edp_clk_enable(struct mdss_edp_drv_pdata *edp_drv);
+void mdss_edp_clk_disable(struct mdss_edp_drv_pdata *edp_drv);
+int mdss_edp_clk_init(struct mdss_edp_drv_pdata *edp_drv);
+void mdss_edp_clk_deinit(struct mdss_edp_drv_pdata *edp_drv);
+void mdss_edp_prepare_clocks(struct mdss_edp_drv_pdata *edp_drv);
+void mdss_edp_unprepare_clocks(struct mdss_edp_drv_pdata *edp_drv);
+void mdss_edp_config_clk(unsigned char *edp_base, unsigned char *mmss_cc_base);
+void mdss_edp_unconfig_clk(unsigned char *edp_base,
+ unsigned char *mmss_cc_base);
+void mdss_edp_phy_misc_cfg(unsigned char *edp_base);
+
+#endif /* MDSS_EDP_H */
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c
index efb4da6..ca5f890 100644
--- a/drivers/video/msm/mdss/mdss_fb.c
+++ b/drivers/video/msm/mdss/mdss_fb.c
@@ -42,6 +42,9 @@
#include <linux/vmalloc.h>
#include <mach/board.h>
+#include <mach/memory.h>
+#include <mach/msm_memtypes.h>
+#include <mach/iommu_domains.h>
#include "mdss_fb.h"
#include "mdss_mdp.h"
@@ -136,6 +139,9 @@
case WRITEBACK_PANEL:
ret = snprintf(buf, PAGE_SIZE, "writeback panel\n");
break;
+ case EDP_PANEL:
+ ret = snprintf(buf, PAGE_SIZE, "edp panel\n");
+ break;
default:
ret = snprintf(buf, PAGE_SIZE, "unknown panel\n");
break;
@@ -596,37 +602,17 @@
size *= mfd->fb_page;
if (mfd->index == 0) {
- struct ion_client *iclient = mdss_get_ionclient();
-
- if (iclient) {
- mfd->ihdl = ion_alloc(iclient, size, SZ_4K,
- ION_HEAP(ION_CP_MM_HEAP_ID) |
- ION_HEAP(ION_SF_HEAP_ID), 0);
- if (IS_ERR_OR_NULL(mfd->ihdl)) {
- pr_err("unable to alloc fbmem from ion (%p)\n",
- mfd->ihdl);
- return -ENOMEM;
- }
-
- virt = ion_map_kernel(iclient, mfd->ihdl);
- ion_phys(iclient, mfd->ihdl, &phys, &size);
-
- if (is_mdss_iommu_attached()) {
- ion_map_iommu(iclient, mfd->ihdl,
- mdss_get_iommu_domain(),
- 0, SZ_4K, 0, &mfd->iova,
- (unsigned long *) &size,
- 0, 0);
- }
- } else {
- virt = dma_alloc_coherent(NULL, size,
- (dma_addr_t *) &phys, GFP_KERNEL);
- if (!virt) {
- pr_err("unable to alloc fbmem size=%u\n", size);
- return -ENOMEM;
- }
+ virt = allocate_contiguous_memory(size, MEMTYPE_EBI1, SZ_1M, 0);
+ if (!virt) {
+ pr_err("unable to alloc fbmem size=%u\n", size);
+ return -ENOMEM;
}
-
+ phys = memory_pool_node_paddr(virt);
+ if (is_mdss_iommu_attached()) {
+ msm_iommu_map_contig_buffer(phys,
+ mdss_get_iommu_domain(), 0, size, SZ_4K, 0,
+ &(mfd->iova));
+ }
pr_info("allocating %u bytes at %p (%lx phys) for fb %d\n",
size, virt, phys, mfd->index);
} else {
diff --git a/drivers/video/msm/mdss/mdss_fb.h b/drivers/video/msm/mdss/mdss_fb.h
index 80ebc4f..25c39f6 100644
--- a/drivers/video/msm/mdss/mdss_fb.h
+++ b/drivers/video/msm/mdss/mdss_fb.h
@@ -96,6 +96,8 @@
struct mdss_mdp_ctl *ctl;
struct mdss_mdp_wb *wb;
struct list_head overlay_list;
+ struct list_head pipes_used;
+ struct list_head pipes_cleanup;
};
int mdss_fb_get_phys_info(unsigned long *start, unsigned long *len, int fb_num);
diff --git a/drivers/video/msm/mdss/mdss_mdp.c b/drivers/video/msm/mdss/mdss_mdp.c
index 4f641cc..0f6cfe9 100644
--- a/drivers/video/msm/mdss/mdss_mdp.c
+++ b/drivers/video/msm/mdss/mdss_mdp.c
@@ -1081,8 +1081,10 @@
static const struct of_device_id mdss_mdp_dt_match[] = {
{ .compatible = "qcom,mdss_mdp",},
+ {}
};
MODULE_DEVICE_TABLE(of, mdss_mdp_dt_match);
+EXPORT_COMPAT("qcom,mdss_mdp");
static struct platform_driver mdss_mdp_driver = {
.probe = mdss_mdp_probe,
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index 6fd642f..33028cb 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -241,7 +241,9 @@
unsigned long smp[MAX_PLANES];
struct mdss_mdp_data buffers[2];
- struct list_head list;
+ struct list_head used_list;
+ struct list_head cleanup_list;
+
struct mdp_overlay_pp_params pp_cfg;
};
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index 5966989..d21a095 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -90,18 +90,34 @@
*bus_ib_quota = 0;
*clk_rate = 0;
- if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF) {
- struct mdss_panel_info *pinfo = &mixer->ctl->mfd->panel_info;
- v_total = (pinfo->yres + pinfo->lcdc.v_back_porch +
- pinfo->lcdc.v_front_porch + pinfo->lcdc.v_pulse_width);
- v_active = pinfo->yres;
- } else if (mixer->rotator_mode) {
+ if (mixer->rotator_mode) {
pipe = mixer->stage_pipe[0]; /* rotator pipe */
v_total = pipe->flags & MDP_ROT_90 ? pipe->dst.w : pipe->dst.h;
v_active = v_total;
} else {
- v_total = mixer->height;
- v_active = v_total;
+ int is_writeback = false;
+ if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF) {
+ struct mdss_panel_info *pinfo;
+ pinfo = &mixer->ctl->mfd->panel_info;
+ v_total = (pinfo->yres + pinfo->lcdc.v_back_porch +
+ pinfo->lcdc.v_front_porch +
+ pinfo->lcdc.v_pulse_width);
+ v_active = pinfo->yres;
+
+ if (pinfo->type == WRITEBACK_PANEL)
+ is_writeback = true;
+ } else {
+ v_total = mixer->height;
+ v_active = v_total;
+
+ is_writeback = true;
+ }
+ *clk_rate = mixer->width * v_total * fps;
+ if (is_writeback) {
+ /* perf for bus writeback */
+ *bus_ab_quota = fps * mixer->width * mixer->height * 3;
+ *bus_ib_quota = *bus_ab_quota;
+ }
}
for (i = 0; i < MDSS_MDP_MAX_STAGE; i++) {
@@ -118,7 +134,7 @@
if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF)
quota = (quota / v_active) * v_total;
- else
+ else if (mixer->rotator_mode)
quota *= 2; /* bus read + write */
rate = pipe->dst.w;
@@ -344,6 +360,8 @@
mdss_mdp_mixer_free(mixer);
mdss_mdp_ctl_free(ctl);
+ mdss_mdp_ctl_perf_commit(MDSS_MDP_PERF_UPDATE_ALL);
+
return 0;
}
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index ee1b350..452ebdc 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -282,7 +282,7 @@
}
mutex_lock(&mfd->lock);
- list_add(&pipe->list, &mfd->overlay_list);
+ list_add(&pipe->used_list, &mfd->pipes_used);
mutex_unlock(&mfd->lock);
pipe->mixer = mixer;
pipe->mfd = mfd;
@@ -395,24 +395,31 @@
static int mdss_mdp_overlay_kickoff(struct mdss_mdp_ctl *ctl)
{
- int ret;
+ struct mdss_mdp_pipe *pipe, *tmp;
+ struct msm_fb_data_type *mfd = ctl->mfd;
+ int i, ret;
- if (ctl->mfd->kickoff_fnc)
- ret = ctl->mfd->kickoff_fnc(ctl);
+ if (mfd->kickoff_fnc)
+ ret = mfd->kickoff_fnc(ctl);
else
ret = mdss_mdp_display_commit(ctl, NULL);
if (IS_ERR_VALUE(ret))
return ret;
- pr_debug("freeing previous buffers\n");
+ mutex_lock(&mfd->lock);
+ list_for_each_entry_safe(pipe, tmp, &mfd->pipes_cleanup, cleanup_list) {
+ list_del(&pipe->cleanup_list);
+ for (i = 0; i < ARRAY_SIZE(pipe->buffers); i++)
+ mdss_mdp_overlay_free_buf(&pipe->buffers[i]);
- mutex_lock(&ctl->mfd->lock);
- if (!list_empty(&ctl->mfd->overlay_list)) {
- struct mdss_mdp_pipe *pipe;
+ mdss_mdp_pipe_destroy(pipe);
+ }
+
+ if (!list_empty(&mfd->pipes_used)) {
struct mdss_mdp_data *data;
int buf_ndx;
- list_for_each_entry(pipe, &ctl->mfd->overlay_list, list) {
+ list_for_each_entry(pipe, &mfd->pipes_used, used_list) {
buf_ndx = (pipe->play_cnt - 1) & 1; /* prev buffer */
data = &pipe->buffers[buf_ndx];
@@ -423,9 +430,7 @@
}
}
}
- mutex_unlock(&ctl->mfd->lock);
-
- pr_debug("done freeing previous buffers\n");
+ mutex_unlock(&mfd->lock);
return ret;
}
@@ -433,8 +438,7 @@
static int mdss_mdp_overlay_unset(struct msm_fb_data_type *mfd, int ndx)
{
struct mdss_mdp_pipe *pipe;
- struct mdss_mdp_pipe *cleanup_pipes[MDSS_MDP_MAX_SSPP];
- int i, ret = 0, clean_cnt = 0;
+ int i, ret = 0;
u32 pipe_ndx, unset_ndx = 0;
if (!mfd || !mfd->ctl)
@@ -460,28 +464,15 @@
if (pipe_ndx & ndx) {
unset_ndx |= pipe_ndx;
pipe = mdss_mdp_pipe_get_locked(pipe_ndx);
- if (pipe) {
- mutex_lock(&mfd->lock);
- list_del(&pipe->list);
- mutex_unlock(&mfd->lock);
- mdss_mdp_mixer_pipe_unstage(pipe);
- cleanup_pipes[clean_cnt++] = pipe;
- } else {
+ if (!pipe) {
pr_warn("unknown pipe ndx=%x\n", pipe_ndx);
+ continue;
}
- }
- }
-
- if (clean_cnt) {
- int j;
- ret = mdss_mdp_overlay_kickoff(mfd->ctl);
-
- for (i = 0; i < clean_cnt; i++) {
- pipe = cleanup_pipes[i];
- for (j = 0; j < ARRAY_SIZE(pipe->buffers); j++)
- mdss_mdp_overlay_free_buf(&pipe->buffers[i]);
-
- mdss_mdp_pipe_destroy(pipe);
+ mutex_lock(&mfd->lock);
+ list_del(&pipe->used_list);
+ list_add(&pipe->cleanup_list, &mfd->pipes_cleanup);
+ mutex_unlock(&mfd->lock);
+ mdss_mdp_mixer_pipe_unstage(pipe);
}
}
@@ -495,8 +486,8 @@
int cnt = 0;
mutex_lock(&mfd->lock);
- if (!list_empty(&mfd->overlay_list)) {
- list_for_each_entry(pipe, &mfd->overlay_list, list) {
+ if (!list_empty(&mfd->pipes_used)) {
+ list_for_each_entry(pipe, &mfd->pipes_used, used_list) {
if (pipe->ndx & MDSS_MDP_ROT_SESSION_MASK) {
struct mdss_mdp_rotator_session *rot;
rot = mdss_mdp_rotator_session_get(pipe->ndx);
@@ -513,6 +504,7 @@
if (unset_ndx) {
pr_debug("%d pipes need cleanup (%x)\n", cnt, unset_ndx);
mdss_mdp_overlay_unset(mfd, unset_ndx);
+ mdss_mdp_overlay_kickoff(mfd->ctl);
}
return 0;
@@ -603,7 +595,7 @@
ctl = pipe->mixer->ctl;
mdss_mdp_pipe_unlock(pipe);
- if (ret == 0 && !(pipe->flags & MDP_OV_PLAY_NOWAIT))
+ if ((ret == 0) && (mfd->panel_info.type == WRITEBACK_PANEL))
ret = mdss_mdp_overlay_kickoff(ctl);
return ret;
@@ -703,7 +695,6 @@
fbi = mfd->fbi;
if (fbi->fix.smem_len == 0) {
- pr_warn("fb memory not allocated\n");
mdss_mdp_overlay_kickoff(mfd->ctl);
return;
}
@@ -1040,7 +1031,9 @@
ret = -EFAULT;
}
break;
-
+ case MSMFB_OVERLAY_COMMIT:
+ ret = mdss_mdp_overlay_kickoff(mfd->ctl);
+ break;
default:
if (mfd->panel_info.type == WRITEBACK_PANEL)
ret = mdss_mdp_wb_ioctl_handler(mfd, cmd, argp);
@@ -1064,7 +1057,8 @@
if (mfd->panel_info.type == WRITEBACK_PANEL)
mfd->kickoff_fnc = mdss_mdp_wb_kickoff;
- INIT_LIST_HEAD(&mfd->overlay_list);
+ INIT_LIST_HEAD(&mfd->pipes_used);
+ INIT_LIST_HEAD(&mfd->pipes_cleanup);
return 0;
}
diff --git a/drivers/video/msm/mdss/mdss_mdp_rotator.h b/drivers/video/msm/mdss/mdss_mdp_rotator.h
index 1e4b81e0..eb5b47a 100644
--- a/drivers/video/msm/mdss/mdss_mdp_rotator.h
+++ b/drivers/video/msm/mdss/mdss_mdp_rotator.h
@@ -17,7 +17,7 @@
#include "mdss_mdp.h"
-#define MDSS_MDP_ROT_SESSION_MASK 0x80000000
+#define MDSS_MDP_ROT_SESSION_MASK 0x40000000
struct mdss_mdp_rotator_session {
u32 session_id;
diff --git a/drivers/video/msm/mdss/mdss_wb.c b/drivers/video/msm/mdss/mdss_wb.c
index a26d339..d4c924f 100644
--- a/drivers/video/msm/mdss/mdss_wb.c
+++ b/drivers/video/msm/mdss/mdss_wb.c
@@ -73,7 +73,7 @@
pdata->panel_info.type = WRITEBACK_PANEL;
pdata->panel_info.clk_rate = 74250000;
pdata->panel_info.pdest = DISPLAY_3;
- pdata->panel_info.out_format = MDP_Y_CBCR_H2V2;
+ pdata->panel_info.out_format = MDP_Y_CBCR_H2V2_VENUS;
pdata->on = mdss_wb_on;
pdata->off = mdss_wb_off;
diff --git a/drivers/video/msm/mdss/msm_mdss_io_8974.c b/drivers/video/msm/mdss/msm_mdss_io_8974.c
index 1232ec6..f594b17 100644
--- a/drivers/video/msm/mdss/msm_mdss_io_8974.c
+++ b/drivers/video/msm/mdss/msm_mdss_io_8974.c
@@ -20,6 +20,7 @@
#include <mach/msm_iomap.h>
#include "mdss_dsi.h"
+#include "mdss_edp.h"
#define SW_RESET BIT(2)
#define SW_RESET_PLL BIT(0)
@@ -341,3 +342,333 @@
}
}
+
+/* EDP phy configuration settings */
+void mdss_edp_phy_sw_reset(unsigned char *edp_base)
+{
+ /* phy sw reset */
+ edp_write(edp_base + 0x74, 0x100); /* EDP_PHY_CTRL */
+ wmb();
+ usleep(1);
+ edp_write(edp_base + 0x74, 0x000); /* EDP_PHY_CTRL */
+ wmb();
+ usleep(1);
+
+ /* phy PLL sw reset */
+ edp_write(edp_base + 0x74, 0x001); /* EDP_PHY_CTRL */
+ wmb();
+ usleep(1);
+ edp_write(edp_base + 0x74, 0x000); /* EDP_PHY_CTRL */
+ wmb();
+ usleep(1);
+}
+
+void mdss_edp_hw_powerup(unsigned char *edp_base, int enable)
+{
+ int ret = 0;
+
+ if (enable) {
+ /* EDP_PHY_EDPPHY_GLB_PD_CTL */
+ edp_write(edp_base + 0x52c, 0x3f);
+ /* EDP_PHY_EDPPHY_GLB_CFG */
+ edp_write(edp_base + 0x528, 0x1);
+ /* EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG */
+ edp_write(edp_base + 0x620, 0xf);
+ /* EDP_AUX_CTRL */
+ ret = edp_read(edp_base + 0x300);
+ edp_write(edp_base + 0x300, ret | 0x1);
+ } else {
+ /* EDP_PHY_EDPPHY_GLB_PD_CTL */
+ edp_write(edp_base + 0x52c, 0xc0);
+ }
+}
+
+void mdss_edp_pll_configure(unsigned char *edp_base, int rate)
+{
+ if (rate == 810000000) {
+ edp_write(edp_base + 0x60c, 0x18);
+ edp_write(edp_base + 0x664, 0x5);
+ edp_write(edp_base + 0x600, 0x0);
+ edp_write(edp_base + 0x638, 0x36);
+ edp_write(edp_base + 0x63c, 0x69);
+ edp_write(edp_base + 0x640, 0xff);
+ edp_write(edp_base + 0x644, 0x2f);
+ edp_write(edp_base + 0x648, 0x0);
+ edp_write(edp_base + 0x66c, 0x0a);
+ edp_write(edp_base + 0x674, 0x01);
+ edp_write(edp_base + 0x684, 0x5a);
+ edp_write(edp_base + 0x688, 0x0);
+ edp_write(edp_base + 0x68c, 0x60);
+ edp_write(edp_base + 0x690, 0x0);
+ edp_write(edp_base + 0x694, 0x2a);
+ edp_write(edp_base + 0x698, 0x3);
+ edp_write(edp_base + 0x65c, 0x10);
+ edp_write(edp_base + 0x660, 0x1a);
+ edp_write(edp_base + 0x604, 0x0);
+ edp_write(edp_base + 0x624, 0x0);
+ edp_write(edp_base + 0x628, 0x0);
+
+ edp_write(edp_base + 0x620, 0x1);
+ edp_write(edp_base + 0x620, 0x5);
+ edp_write(edp_base + 0x620, 0x7);
+ edp_write(edp_base + 0x620, 0xf);
+
+ } else if (rate == 138500000) {
+ edp_write(edp_base + 0x664, 0x5); /* UNIPHY_PLL_LKDET_CFG2 */
+ edp_write(edp_base + 0x600, 0x1); /* UNIPHY_PLL_REFCLK_CFG */
+ edp_write(edp_base + 0x638, 0x36); /* UNIPHY_PLL_SDM_CFG0 */
+ edp_write(edp_base + 0x63c, 0x62); /* UNIPHY_PLL_SDM_CFG1 */
+ edp_write(edp_base + 0x640, 0x0); /* UNIPHY_PLL_SDM_CFG2 */
+ edp_write(edp_base + 0x644, 0x28); /* UNIPHY_PLL_SDM_CFG3 */
+ edp_write(edp_base + 0x648, 0x0); /* UNIPHY_PLL_SDM_CFG4 */
+ edp_write(edp_base + 0x64c, 0x80); /* UNIPHY_PLL_SSC_CFG0 */
+ edp_write(edp_base + 0x650, 0x0); /* UNIPHY_PLL_SSC_CFG1 */
+ edp_write(edp_base + 0x654, 0x0); /* UNIPHY_PLL_SSC_CFG2 */
+ edp_write(edp_base + 0x658, 0x0); /* UNIPHY_PLL_SSC_CFG3 */
+ edp_write(edp_base + 0x66c, 0xa); /* UNIPHY_PLL_CAL_CFG0 */
+ edp_write(edp_base + 0x674, 0x1); /* UNIPHY_PLL_CAL_CFG2 */
+ edp_write(edp_base + 0x684, 0x5a); /* UNIPHY_PLL_CAL_CFG6 */
+ edp_write(edp_base + 0x688, 0x0); /* UNIPHY_PLL_CAL_CFG7 */
+ edp_write(edp_base + 0x68c, 0x60); /* UNIPHY_PLL_CAL_CFG8 */
+ edp_write(edp_base + 0x690, 0x0); /* UNIPHY_PLL_CAL_CFG9 */
+ edp_write(edp_base + 0x694, 0x46); /* UNIPHY_PLL_CAL_CFG10 */
+ edp_write(edp_base + 0x698, 0x5); /* UNIPHY_PLL_CAL_CFG11 */
+ edp_write(edp_base + 0x65c, 0x10); /* UNIPHY_PLL_LKDET_CFG0 */
+ edp_write(edp_base + 0x660, 0x1a); /* UNIPHY_PLL_LKDET_CFG1 */
+ edp_write(edp_base + 0x604, 0x0); /* UNIPHY_PLL_POSTDIV1_CFG */
+ edp_write(edp_base + 0x624, 0x0); /* UNIPHY_PLL_POSTDIV2_CFG */
+ edp_write(edp_base + 0x628, 0x0); /* UNIPHY_PLL_POSTDIV3_CFG */
+
+ edp_write(edp_base + 0x620, 0x1); /* UNIPHY_PLL_GLB_CFG */
+ edp_write(edp_base + 0x620, 0x5); /* UNIPHY_PLL_GLB_CFG */
+ edp_write(edp_base + 0x620, 0x7); /* UNIPHY_PLL_GLB_CFG */
+ edp_write(edp_base + 0x620, 0xf); /* UNIPHY_PLL_GLB_CFG */
+ } else {
+ pr_err("%s: Unknown configuration rate\n", __func__);
+ }
+}
+
+void mdss_edp_enable_aux(unsigned char *edp_base, int enable)
+{
+ if (!enable) {
+ edp_write(edp_base + 0x300, 0); /* EDP_AUX_CTRL */
+ return;
+ }
+
+ /*reset AUX */
+ edp_write(edp_base + 0x300, BIT(1)); /* EDP_AUX_CTRL */
+ edp_write(edp_base + 0x300, 0); /* EDP_AUX_CTRL */
+
+ /* Enable AUX */
+ edp_write(edp_base + 0x300, BIT(0)); /* EDP_AUX_CTRL */
+
+ edp_write(edp_base + 0x550, 0x2c); /* AUX_CFG0 */
+ edp_write(edp_base + 0x308, 0xffffffff); /* INTR_STATUS */
+ edp_write(edp_base + 0x568, 0xff); /* INTR_MASK */
+}
+
+void mdss_edp_enable_mainlink(unsigned char *edp_base, int enable)
+{
+ u32 data;
+
+ data = edp_read(edp_base + 0x004);
+ data &= ~BIT(0);
+
+ if (enable) {
+ data |= 0x1;
+ edp_write(edp_base + 0x004, data);
+ edp_write(edp_base + 0x004, 0x1);
+ } else {
+ data |= 0x0;
+ edp_write(edp_base + 0x004, data);
+ }
+}
+
+void mdss_edp_enable_lane_bist(unsigned char *edp_base, int lane, int enable)
+{
+ unsigned char *addr_ln_bist_cfg, *addr_ln_pd_ctrl;
+
+ /* EDP_PHY_EDPPHY_LNn_PD_CTL */
+ addr_ln_pd_ctrl = edp_base + 0x404 + (0x40 * lane);
+ /* EDP_PHY_EDPPHY_LNn_BIST_CFG0 */
+ addr_ln_bist_cfg = edp_base + 0x408 + (0x40 * lane);
+
+ if (enable) {
+ edp_write(addr_ln_pd_ctrl, 0x0);
+ edp_write(addr_ln_bist_cfg, 0x10);
+
+ } else {
+ edp_write(addr_ln_pd_ctrl, 0xf);
+ edp_write(addr_ln_bist_cfg, 0x10);
+ }
+}
+
+void mdss_edp_clk_deinit(struct mdss_edp_drv_pdata *edp_drv)
+{
+ if (edp_drv->aux_clk)
+ clk_put(edp_drv->aux_clk);
+ if (edp_drv->pixel_clk)
+ clk_put(edp_drv->pixel_clk);
+ if (edp_drv->ahb_clk)
+ clk_put(edp_drv->ahb_clk);
+ if (edp_drv->link_clk)
+ clk_put(edp_drv->link_clk);
+}
+
+int mdss_edp_clk_init(struct mdss_edp_drv_pdata *edp_drv)
+{
+ struct device *dev = &(edp_drv->pdev->dev);
+
+ edp_drv->aux_clk = clk_get(dev, "core_clk");
+ if (IS_ERR(edp_drv->aux_clk)) {
+ pr_err("%s: Can't find aux_clk", __func__);
+ edp_drv->aux_clk = NULL;
+ goto mdss_edp_clk_err;
+ }
+
+ edp_drv->pixel_clk = clk_get(dev, "pixel_clk");
+ if (IS_ERR(edp_drv->pixel_clk)) {
+ pr_err("%s: Can't find pixel_clk", __func__);
+ edp_drv->pixel_clk = NULL;
+ goto mdss_edp_clk_err;
+ }
+
+ edp_drv->ahb_clk = clk_get(dev, "iface_clk");
+ if (IS_ERR(edp_drv->ahb_clk)) {
+ pr_err("%s: Can't find ahb_clk", __func__);
+ edp_drv->ahb_clk = NULL;
+ goto mdss_edp_clk_err;
+ }
+
+ edp_drv->link_clk = clk_get(dev, "link_clk");
+ if (IS_ERR(edp_drv->link_clk)) {
+ pr_err("%s: Can't find link_clk", __func__);
+ edp_drv->link_clk = NULL;
+ goto mdss_edp_clk_err;
+ }
+
+ return 0;
+
+mdss_edp_clk_err:
+ mdss_edp_clk_deinit(edp_drv);
+ return -EPERM;
+}
+
+
+void mdss_edp_clk_enable(struct mdss_edp_drv_pdata *edp_drv)
+{
+ if (edp_drv->clk_on) {
+ pr_info("%s: edp clks are already ON\n", __func__);
+ return;
+ }
+
+ if (clk_set_rate(edp_drv->aux_clk, 19200000) < 0)
+ pr_err("%s: aux_clk - clk_set_rate failed\n",
+ __func__);
+
+ if (clk_set_rate(edp_drv->pixel_clk, 138500000) < 0)
+ pr_err("%s: pixel_clk - clk_set_rate failed\n",
+ __func__);
+
+ if (clk_set_rate(edp_drv->link_clk, 270000000) < 0)
+ pr_err("%s: link_clk - clk_set_rate failed\n",
+ __func__);
+
+ clk_enable(edp_drv->aux_clk);
+ clk_enable(edp_drv->pixel_clk);
+ clk_enable(edp_drv->ahb_clk);
+ clk_enable(edp_drv->link_clk);
+
+ edp_drv->clk_on = 1;
+}
+
+void mdss_edp_clk_disable(struct mdss_edp_drv_pdata *edp_drv)
+{
+ if (edp_drv->clk_on == 0) {
+ pr_info("%s: edp clks are already OFF\n", __func__);
+ return;
+ }
+
+ clk_disable(edp_drv->aux_clk);
+ clk_disable(edp_drv->pixel_clk);
+ clk_disable(edp_drv->ahb_clk);
+ clk_disable(edp_drv->link_clk);
+
+ edp_drv->clk_on = 0;
+}
+
+void mdss_edp_prepare_clocks(struct mdss_edp_drv_pdata *edp_drv)
+{
+ clk_prepare(edp_drv->aux_clk);
+ clk_prepare(edp_drv->pixel_clk);
+ clk_prepare(edp_drv->ahb_clk);
+ clk_prepare(edp_drv->link_clk);
+}
+
+void mdss_edp_unprepare_clocks(struct mdss_edp_drv_pdata *edp_drv)
+{
+ clk_unprepare(edp_drv->aux_clk);
+ clk_unprepare(edp_drv->pixel_clk);
+ clk_unprepare(edp_drv->ahb_clk);
+ clk_unprepare(edp_drv->link_clk);
+}
+
+void mdss_edp_enable_pixel_clk(unsigned char *edp_base,
+ unsigned char *mmss_cc_base, int enable)
+{
+ if (!enable) {
+ edp_write(mmss_cc_base + 0x032c, 0); /* CBCR */
+ return;
+ }
+
+ edp_write(edp_base + 0x624, 0x1); /* PostDiv2 */
+
+ /* Configuring MND for Pixel */
+ edp_write(mmss_cc_base + 0x00a8, 0x3f); /* M value */
+ edp_write(mmss_cc_base + 0x00ac, 0xb); /* N value */
+ edp_write(mmss_cc_base + 0x00b0, 0x0); /* D value */
+
+ /* CFG RCGR */
+ edp_write(mmss_cc_base + 0x00a4, (5 << 8) | (2 << 12));
+ edp_write(mmss_cc_base + 0x00a0, 3); /* CMD RCGR */
+
+ edp_write(mmss_cc_base + 0x032c, 1); /* CBCR */
+}
+
+void mdss_edp_enable_link_clk(unsigned char *mmss_cc_base, int enable)
+{
+ if (!enable) {
+ edp_write(mmss_cc_base + 0x0330, 0); /* CBCR */
+ return;
+ }
+
+ edp_write(mmss_cc_base + 0x00c4, (4 << 8)); /* CFG RCGR */
+ edp_write(mmss_cc_base + 0x00c0, 3); /* CMD RCGR */
+
+ edp_write(mmss_cc_base + 0x0330, 1); /* CBCR */
+}
+
+void mdss_edp_config_clk(unsigned char *edp_base, unsigned char *mmss_cc_base)
+{
+ mdss_edp_enable_link_clk(mmss_cc_base, 1);
+ mdss_edp_enable_pixel_clk(edp_base, mmss_cc_base, 1);
+}
+
+void mdss_edp_unconfig_clk(unsigned char *edp_base,
+ unsigned char *mmss_cc_base)
+{
+ mdss_edp_enable_link_clk(mmss_cc_base, 0);
+ mdss_edp_enable_pixel_clk(edp_base, mmss_cc_base, 0);
+}
+
+void mdss_edp_phy_misc_cfg(unsigned char *edp_base)
+{
+ /* EDP_PHY_EDPPHY_GLB_VM_CFG0 */
+ edp_write(edp_base + 0x510, 0x3);
+ /* EDP_PHY_EDPPHY_GLB_VM_CFG1 */
+ edp_write(edp_base + 0x514, 0x64);
+ /* EDP_PHY_EDPPHY_GLB_MISC9 */
+ edp_write(edp_base + 0x518, 0x6c);
+ /* EDP_MISC1_MISC0 */
+ edp_write(edp_base + 0x2c, 0x1);
+}
diff --git a/drivers/video/msm/msm_fb.h b/drivers/video/msm/msm_fb.h
index 7dc89ef..1594825 100644
--- a/drivers/video/msm/msm_fb.h
+++ b/drivers/video/msm/msm_fb.h
@@ -189,6 +189,7 @@
void *copy_splash_buf;
unsigned char *copy_splash_phys;
void *cpu_pm_hdl;
+ u32 avtimer_phy;
};
struct dentry *msm_fb_get_debugfs_root(void);
diff --git a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
index 3670dc81..0bc2228 100644
--- a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
+++ b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
@@ -537,7 +537,6 @@
u32 enc_perf_level = 0, dec_perf_level = 0;
u32 bus_clk_index, client_type = 0;
int rc = 0;
- bool turbo_enabled = false;
bool turbo_supported =
!resource_context.vidc_platform_data->disable_turbo;
@@ -547,9 +546,6 @@
dec_perf_level += cctxt_itr->reqd_perf_lvl;
else
enc_perf_level += cctxt_itr->reqd_perf_lvl;
-
- if (cctxt_itr->is_turbo_enabled)
- turbo_enabled = true;
cctxt_itr = cctxt_itr->next;
}
@@ -566,18 +562,8 @@
if (dev_ctxt->reqd_perf_lvl + dev_ctxt->curr_perf_lvl == 0)
bus_clk_index = 2;
- else if ((!turbo_supported || !turbo_enabled) && bus_clk_index == 3) {
- if (!turbo_supported)
- VCDRES_MSG_MED("Warning: Turbo mode not supported "\
- " falling back to 1080p bus\n");
+ else if (!turbo_supported && bus_clk_index == 3)
bus_clk_index = 2;
- }
-
- if (bus_clk_index == 3)
- dev_ctxt->turbo_mode_set = true;
- else
- dev_ctxt->turbo_mode_set = false;
-
bus_clk_index = (bus_clk_index << 1) + (client_type + 1);
VCDRES_MSG_LOW("%s(), bus_clk_index = %d", __func__, bus_clk_index);
VCDRES_MSG_LOW("%s(),context.pcl = %x", __func__, resource_context.pcl);
@@ -633,11 +619,8 @@
*pn_set_perf_lvl = RESTRK_1080P_TURBO_PERF_LEVEL;
}
- if ((!turbo_supported || !dev_ctxt->turbo_mode_set) &&
+ if (!turbo_supported &&
*pn_set_perf_lvl == RESTRK_1080P_TURBO_PERF_LEVEL) {
- if (!turbo_supported)
- VCDRES_MSG_ERROR("Warning: Turbo mode not supported "\
- " falling back to 1080p clocks\n");
vidc_freq = vidc_clk_table[2];
*pn_set_perf_lvl = RESTRK_1080P_MAX_PERF_LEVEL;
}
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_core.h b/drivers/video/msm/vidc/common/vcd/vcd_core.h
index ae97561..aba8119 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_core.h
+++ b/drivers/video/msm/vidc/common/vcd/vcd_core.h
@@ -147,7 +147,6 @@
u32 reqd_perf_lvl;
u32 curr_perf_lvl;
u32 set_perf_lvl_pending;
- bool turbo_mode_set;
};
struct vcd_clnt_status {
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c b/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c
index 0d13028..f670a4a 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c
+++ b/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c
@@ -219,8 +219,6 @@
VCD_DEVICE_STATE_INITING,
ev_code);
}
- dev_ctxt->turbo_mode_set = 0;
-
return rc;
}
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_sub.c b/drivers/video/msm/vidc/common/vcd/vcd_sub.c
index 6e332ef..5fdee02 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_sub.c
+++ b/drivers/video/msm/vidc/common/vcd/vcd_sub.c
@@ -920,7 +920,7 @@
u32 i;
u32 found = false;
- for (i = 0; i <= pool->count && !found; i++) {
+ for (i = 1; i <= pool->count && !found; i++) {
if (pool->entries[i].virtual == addr)
found = true;
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index abcdeab..9a66ff9 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -2088,7 +2088,7 @@
ext4_ext_in_cache(struct inode *inode, ext4_lblk_t block,
struct ext4_extent *ex)
{
- struct ext4_ext_cache cex;
+ struct ext4_ext_cache cex = {0, 0, 0};
int ret = 0;
if (ext4_ext_check_cache(inode, block, &cex)) {
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 31a152d..5b07403 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -444,3 +444,4 @@
header-y += idle_stats_device.h
header-y += genlock.h
header-y += msm_audio_amrwb.h
+header-y += coresight-stm.h
diff --git a/include/linux/coresight-stm.h b/include/linux/coresight-stm.h
index 754f2f3..bb3ebca 100644
--- a/include/linux/coresight-stm.h
+++ b/include/linux/coresight-stm.h
@@ -1,15 +1,3 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
#ifndef __MACH_STM_H
#define __MACH_STM_H
@@ -28,6 +16,7 @@
STM_OPTION_GUARANTEED = 0x80,
};
+#ifdef __KERNEL__
#define stm_log_inv(entity_id, proto_id, data, size) \
stm_trace(STM_OPTION_NONE, entity_id, proto_id, data, size)
@@ -56,5 +45,6 @@
return 0;
}
#endif
+#endif /* __KERNEL__ */
#endif
diff --git a/include/linux/cyttsp-qc.h b/include/linux/cyttsp-qc.h
index e1ab6fe..0e5cac7 100644
--- a/include/linux/cyttsp-qc.h
+++ b/include/linux/cyttsp-qc.h
@@ -356,7 +356,6 @@
#define CY_DLY_BL 300
#define CY_DLY_DNLOAD 100 /* ms */
#define CY_NUM_RETRY 4 /* max num touch data read */
-#define CY_HALF_SEC_TMO_MS 500
/* handshake bit in the hst_mode reg */
#define CY_HNDSHK_BIT 0x80
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index 288ed43..f0e42c2 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -41,6 +41,7 @@
#define DIAG_IOCTL_DCI_DEINIT 21
#define DIAG_IOCTL_DCI_SUPPORT 22
#define DIAG_IOCTL_DCI_REG 23
+#define DIAG_IOCTL_DCI_STREAM_INIT 24
/* PC Tools IDs */
#define APQ8060_TOOLS_ID 4062
@@ -706,5 +707,6 @@
#define LOG_15 0x0
#define LOG_GET_ITEM_NUM(xx_code) (xx_code & 0x0FFF)
+#define LOG_GET_EQUIP_ID(xx_code) ((xx_code & 0xF000) >> 12)
#endif
diff --git a/include/linux/mfd/pm8xxx/pm8038.h b/include/linux/mfd/pm8xxx/pm8038.h
index 682abc8..574dab6 100644
--- a/include/linux/mfd/pm8xxx/pm8038.h
+++ b/include/linux/mfd/pm8xxx/pm8038.h
@@ -54,6 +54,7 @@
/* PMIC Interrupts */
#define PM8038_RTC_ALARM_IRQ PM8038_IRQ_BLOCK_BIT(4, 7)
+#define PM8038_BATT_ALARM_IRQ PM8921_IRQ_BLOCK_BIT(5, 6)
#define PM8038_PWRKEY_REL_IRQ PM8038_IRQ_BLOCK_BIT(6, 2)
#define PM8038_PWRKEY_PRESS_IRQ PM8038_IRQ_BLOCK_BIT(6, 3)
#define PM8038_KEYPAD_IRQ PM8038_IRQ_BLOCK_BIT(9, 2)
diff --git a/include/linux/mfd/pm8xxx/pm8921-charger.h b/include/linux/mfd/pm8xxx/pm8921-charger.h
index 7b389c5..16d4a4b 100644
--- a/include/linux/mfd/pm8xxx/pm8921-charger.h
+++ b/include/linux/mfd/pm8xxx/pm8921-charger.h
@@ -63,6 +63,8 @@
* @ttrkl_time: max trckl charging time in minutes
* valid range 1 to 64 mins. PON default 15 min
* @update_time: how often the userland be updated of the charging (msec)
+ * @alarm_low_mv: the voltage (mV) when low battery alarm is triggered
+ * @alarm_high_mv: the voltage (mV) when high battery alarm is triggered
* @max_voltage: the max voltage (mV) the battery should be charged up to
* @min_voltage: the voltage (mV) where charging method switches from
* trickle to fast. This is also the minimum voltage the
@@ -70,6 +72,9 @@
* @uvd_thresh_voltage: the USB falling UVD threshold (mV) (PM8917 only)
* @resume_voltage_delta: the (mV) drop to wait for before resume charging
* after the battery has been fully charged
+ * @resume_charge_percent: the % SOC the charger will drop to after the
+ * battery is fully charged before resuming
+ * charging.
* @term_current: the charger current (mA) at which EOC happens
* @cool_temp: the temperature (degC) at which the battery is
* considered cool charging current and voltage is reduced.
@@ -128,7 +133,10 @@
unsigned int max_voltage;
unsigned int min_voltage;
unsigned int uvd_thresh_voltage;
+ unsigned int alarm_low_mv;
+ unsigned int alarm_high_mv;
unsigned int resume_voltage_delta;
+ int resume_charge_percent;
unsigned int term_current;
int cool_temp;
int warm_temp;
diff --git a/include/linux/mfd/wcd9xxx/core.h b/include/linux/mfd/wcd9xxx/core.h
index c306c75..2dea611 100644
--- a/include/linux/mfd/wcd9xxx/core.h
+++ b/include/linux/mfd/wcd9xxx/core.h
@@ -13,10 +13,13 @@
#ifndef __MFD_TABLA_CORE_H__
#define __MFD_TABLA_CORE_H__
+#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/pm_qos.h>
+#include <linux/platform_device.h>
+#include <linux/of_irq.h>
-#define WCD9XXX_NUM_IRQ_REGS 3
+#define WCD9XXX_NUM_IRQ_REGS 4
#define WCD9XXX_SLIM_NUM_PORT_REG 3
@@ -44,83 +47,52 @@
enum {
- TABLA_IRQ_SLIMBUS = 0,
- TABLA_IRQ_MBHC_REMOVAL,
- TABLA_IRQ_MBHC_SHORT_TERM,
- TABLA_IRQ_MBHC_PRESS,
- TABLA_IRQ_MBHC_RELEASE,
- TABLA_IRQ_MBHC_POTENTIAL,
- TABLA_IRQ_MBHC_INSERTION,
- TABLA_IRQ_BG_PRECHARGE,
- TABLA_IRQ_PA1_STARTUP,
- TABLA_IRQ_PA2_STARTUP,
- TABLA_IRQ_PA3_STARTUP,
- TABLA_IRQ_PA4_STARTUP,
- TABLA_IRQ_PA5_STARTUP,
- TABLA_IRQ_MICBIAS1_PRECHARGE,
- TABLA_IRQ_MICBIAS2_PRECHARGE,
- TABLA_IRQ_MICBIAS3_PRECHARGE,
- TABLA_IRQ_HPH_PA_OCPL_FAULT,
- TABLA_IRQ_HPH_PA_OCPR_FAULT,
- TABLA_IRQ_EAR_PA_OCPL_FAULT,
- TABLA_IRQ_HPH_L_PA_STARTUP,
- TABLA_IRQ_HPH_R_PA_STARTUP,
- TABLA_IRQ_EAR_PA_STARTUP,
- TABLA_NUM_IRQS,
+ /* INTR_REG 0 */
+ WCD9XXX_IRQ_SLIMBUS = 0,
+ WCD9XXX_IRQ_MBHC_REMOVAL,
+ WCD9XXX_IRQ_MBHC_SHORT_TERM,
+ WCD9XXX_IRQ_MBHC_PRESS,
+ WCD9XXX_IRQ_MBHC_RELEASE,
+ WCD9XXX_IRQ_MBHC_POTENTIAL,
+ WCD9XXX_IRQ_MBHC_INSERTION,
+ WCD9XXX_IRQ_BG_PRECHARGE,
+ /* INTR_REG 1 */
+ WCD9XXX_IRQ_PA1_STARTUP,
+ WCD9XXX_IRQ_PA2_STARTUP,
+ WCD9XXX_IRQ_PA3_STARTUP,
+ WCD9XXX_IRQ_PA4_STARTUP,
+ WCD9XXX_IRQ_PA5_STARTUP,
+ WCD9XXX_IRQ_MICBIAS1_PRECHARGE,
+ WCD9XXX_IRQ_MICBIAS2_PRECHARGE,
+ WCD9XXX_IRQ_MICBIAS3_PRECHARGE,
+ /* INTR_REG 2 */
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
+ WCD9XXX_IRQ_EAR_PA_OCPL_FAULT,
+ WCD9XXX_IRQ_HPH_L_PA_STARTUP,
+ WCD9XXX_IRQ_HPH_R_PA_STARTUP,
+ WCD9XXX_IRQ_EAR_PA_STARTUP,
+ WCD9XXX_IRQ_RESERVED_0,
+ WCD9XXX_IRQ_RESERVED_1,
+ /* INTR_REG 3 */
+ WCD9XXX_IRQ_MAD_AUDIO,
+ WCD9XXX_IRQ_MAD_BEACON,
+ WCD9XXX_IRQ_MAD_ULTRASOUND,
+ WCD9XXX_IRQ_SPEAKER_CLIPPING,
+ WCD9XXX_IRQ_MBHC_JACK_SWITCH,
+ WCD9XXX_NUM_IRQS,
};
enum {
- SITAR_IRQ_SLIMBUS = 0,
- SITAR_IRQ_MBHC_REMOVAL,
- SITAR_IRQ_MBHC_SHORT_TERM,
- SITAR_IRQ_MBHC_PRESS,
- SITAR_IRQ_MBHC_RELEASE,
- SITAR_IRQ_MBHC_POTENTIAL,
- SITAR_IRQ_MBHC_INSERTION,
- SITAR_IRQ_BG_PRECHARGE,
- SITAR_IRQ_PA1_STARTUP,
- SITAR_IRQ_PA2_STARTUP,
- SITAR_IRQ_PA3_STARTUP,
- SITAR_IRQ_PA4_STARTUP,
- SITAR_IRQ_PA5_STARTUP,
- SITAR_IRQ_MICBIAS1_PRECHARGE,
- SITAR_IRQ_MICBIAS2_PRECHARGE,
- SITAR_IRQ_MICBIAS3_PRECHARGE,
- SITAR_IRQ_HPH_PA_OCPL_FAULT,
- SITAR_IRQ_HPH_PA_OCPR_FAULT,
- SITAR_IRQ_EAR_PA_OCPL_FAULT,
- SITAR_IRQ_HPH_L_PA_STARTUP,
- SITAR_IRQ_HPH_R_PA_STARTUP,
- SITAR_IRQ_EAR_PA_STARTUP,
- SITAR_NUM_IRQS,
+ TABLA_NUM_IRQS = WCD9XXX_NUM_IRQS,
+ SITAR_NUM_IRQS = WCD9XXX_NUM_IRQS,
+ TAIKO_NUM_IRQS = WCD9XXX_NUM_IRQS,
};
-enum {
- TAIKO_IRQ_SLIMBUS = 0,
- TAIKO_IRQ_MBHC_REMOVAL,
- TAIKO_IRQ_MBHC_SHORT_TERM,
- TAIKO_IRQ_MBHC_PRESS,
- TAIKO_IRQ_MBHC_RELEASE,
- TAIKO_IRQ_MBHC_POTENTIAL,
- TAIKO_IRQ_MBHC_INSERTION,
- TAIKO_IRQ_BG_PRECHARGE,
- TAIKO_IRQ_PA1_STARTUP,
- TAIKO_IRQ_PA2_STARTUP,
- TAIKO_IRQ_PA3_STARTUP,
- TAIKO_IRQ_PA4_STARTUP,
- TAIKO_IRQ_PA5_STARTUP,
- TAIKO_IRQ_MICBIAS1_PRECHARGE,
- TAIKO_IRQ_MICBIAS2_PRECHARGE,
- TAIKO_IRQ_MICBIAS3_PRECHARGE,
- TAIKO_IRQ_HPH_PA_OCPL_FAULT,
- TAIKO_IRQ_HPH_PA_OCPR_FAULT,
- TAIKO_IRQ_EAR_PA_OCPL_FAULT,
- TAIKO_IRQ_HPH_L_PA_STARTUP,
- TAIKO_IRQ_HPH_R_PA_STARTUP,
- TAIKO_IRQ_EAR_PA_STARTUP,
- TAIKO_NUM_IRQS,
-};
+#define MAX(X, Y) (((int)X) >= ((int)Y) ? (X) : (Y))
+#define WCD9XXX_MAX_NUM_IRQS (MAX(MAX(TABLA_NUM_IRQS, SITAR_NUM_IRQS), \
+ TAIKO_NUM_IRQS))
enum wcd9xxx_pm_state {
WCD9XXX_PM_SLEEPABLE,
@@ -128,6 +100,44 @@
WCD9XXX_PM_ASLEEP,
};
+/*
+ * data structure for Slimbus and I2S channel.
+ * Some of fields are only used in smilbus mode
+ */
+struct wcd9xxx_ch {
+ u32 sph; /* share channel handle - slimbus only */
+ u32 ch_num; /*
+ * vitrual channel number, such as 128 -144.
+ * apply for slimbus only
+ */
+ u16 ch_h; /* chanel handle - slimbus only */
+ u16 port; /*
+ * tabla port for RX and TX
+ * such as 0-9 for TX and 10 -16 for RX
+ * apply for both i2s and slimbus
+ */
+ u16 shift; /*
+ * shift bit for RX and TX
+ * apply for both i2s and slimbus
+ */
+ struct list_head list; /*
+ * channel link list
+ * apply for both i2s and slimbus
+ */
+};
+
+struct wcd9xxx_codec_dai_data {
+ u32 rate; /* sample rate */
+ u32 bit_width; /* sit width 16,24,32 */
+ struct list_head wcd9xxx_ch_list; /* channel list */
+ u16 grph; /* slimbus group handle */
+ u32 ch_mask;
+ wait_queue_head_t dai_wait;
+};
+
+#define WCD9XXX_CH(xport, xshift) \
+ {.port = xport, .shift = xshift}
+
struct wcd9xxx {
struct device *dev;
struct slim_device *slim;
@@ -137,18 +147,12 @@
struct mutex irq_lock;
u8 version;
- unsigned int irq_base;
- unsigned int irq;
- u8 irq_masks_cur[WCD9XXX_NUM_IRQ_REGS];
- u8 irq_masks_cache[WCD9XXX_NUM_IRQ_REGS];
- u8 irq_level[WCD9XXX_NUM_IRQ_REGS];
-
int reset_gpio;
int (*read_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg,
int bytes, void *dest, bool interface_reg);
int (*write_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg,
- int bytes, void *src, bool interface_reg);
+ int bytes, void *src, bool interface_reg);
u32 num_of_supplies;
struct regulator_bulk_data *supplies;
@@ -160,10 +164,19 @@
struct pm_qos_request pm_qos_req;
int wlock_holders;
- int num_rx_port;
- int num_tx_port;
-
u8 idbyte[4];
+
+ unsigned int irq_base;
+ unsigned int irq;
+ u8 irq_masks_cur[WCD9XXX_NUM_IRQ_REGS];
+ u8 irq_masks_cache[WCD9XXX_NUM_IRQ_REGS];
+ bool irq_level_high[WCD9XXX_MAX_NUM_IRQS];
+ int num_irqs;
+ /* Slimbus or I2S port */
+ u32 num_rx_port;
+ u32 num_tx_port;
+ struct wcd9xxx_ch *rx_chs;
+ struct wcd9xxx_ch *tx_chs;
};
int wcd9xxx_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg);
@@ -187,40 +200,23 @@
enum wcd9xxx_pm_state o,
enum wcd9xxx_pm_state n);
-static inline int wcd9xxx_request_irq(struct wcd9xxx *wcd9xxx, int irq,
- irq_handler_t handler, const char *name,
- void *data)
-{
- if (!wcd9xxx->irq_base)
- return -EINVAL;
- return request_threaded_irq(wcd9xxx->irq_base + irq, NULL, handler,
- IRQF_TRIGGER_RISING, name,
- data);
-}
-static inline void wcd9xxx_free_irq(struct wcd9xxx *wcd9xxx,
- int irq, void *data)
-{
- if (!wcd9xxx->irq_base)
- return;
- free_irq(wcd9xxx->irq_base + irq, data);
-}
-static inline void wcd9xxx_enable_irq(struct wcd9xxx *wcd9xxx, int irq)
-{
- if (!wcd9xxx->irq_base)
- return;
- enable_irq(wcd9xxx->irq_base + irq);
-}
-static inline void wcd9xxx_disable_irq(struct wcd9xxx *wcd9xxx, int irq)
-{
- if (!wcd9xxx->irq_base)
- return;
- disable_irq_nosync(wcd9xxx->irq_base + irq);
-}
-static inline void wcd9xxx_disable_irq_sync(struct wcd9xxx *wcd9xxx, int irq)
-{
- if (!wcd9xxx->irq_base)
- return;
- disable_irq(wcd9xxx->irq_base + irq);
-}
+int wcd9xxx_request_irq(struct wcd9xxx *wcd9xxx, int irq,
+ irq_handler_t handler, const char *name, void *data);
+void wcd9xxx_free_irq(struct wcd9xxx *wcd9xxx, int irq, void *data);
+void wcd9xxx_enable_irq(struct wcd9xxx *wcd9xxx, int irq);
+void wcd9xxx_disable_irq(struct wcd9xxx *wcd9xxx, int irq);
+void wcd9xxx_disable_irq_sync(struct wcd9xxx *wcd9xxx, int irq);
+#if defined(CONFIG_WCD9310_CODEC) || \
+ defined(CONFIG_WCD9304_CODEC) || \
+ defined(CONFIG_WCD9320_CODEC)
+int __init wcd9xxx_irq_of_init(struct device_node *node,
+ struct device_node *parent);
+#else
+static inline int __init wcd9xxx_irq_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ return 0;
+}
+#endif
#endif
diff --git a/include/linux/mfd/wcd9xxx/pdata.h b/include/linux/mfd/wcd9xxx/pdata.h
index e831f0b..a7ca417 100644
--- a/include/linux/mfd/wcd9xxx/pdata.h
+++ b/include/linux/mfd/wcd9xxx/pdata.h
@@ -28,6 +28,12 @@
#define SITAR_CFILT2_SEL 0x1
#define SITAR_CFILT3_SEL 0x2
+#define WCD9XXX_LDOH_1P95_V 0x0
+#define WCD9XXX_LDOH_2P35_V 0x1
+#define WCD9XXX_LDOH_2P75_V 0x2
+#define WCD9XXX_LDOH_2P85_V 0x3
+#define WCD9XXX_LDOH_3P0_V 0x3
+
#define TABLA_LDOH_1P95_V 0x0
#define TABLA_LDOH_2P35_V 0x1
#define TABLA_LDOH_2P75_V 0x2
@@ -37,16 +43,6 @@
#define TABLA_CFILT2_SEL 0x1
#define TABLA_CFILT3_SEL 0x2
-#define TAIKO_CFILT1_SEL 0x0
-#define TAIKO_CFILT2_SEL 0x1
-#define TAIKO_CFILT3_SEL 0x2
-
-#define TAIKO_LDOH_1P95_V 0x0
-#define TAIKO_LDOH_2P35_V 0x1
-#define TAIKO_LDOH_2P75_V 0x2
-#define TAIKO_LDOH_2P85_V 0x3
-
-
#define MAX_AMIC_CHANNEL 7
#define TABLA_OCP_300_MA 0x0
diff --git a/include/linux/mfd/wcd9xxx/wcd9304_registers.h b/include/linux/mfd/wcd9xxx/wcd9304_registers.h
index f7c483c..73919e0 100644
--- a/include/linux/mfd/wcd9xxx/wcd9304_registers.h
+++ b/include/linux/mfd/wcd9xxx/wcd9304_registers.h
@@ -409,114 +409,163 @@
#define SITAR_A_CDC_ANC1_SMLPF_CTL__POR (0x00000000)
#define SITAR_A_CDC_ANC1_DCFLT_CTL (0x20B)
#define SITAR_A_CDC_ANC1_DCFLT_CTL__POR (0x00000000)
-#define SITAR_A_CDC_TX1_VOL_CTL_TIMER (0x220)
-#define SITAR_A_CDC_TX1_VOL_CTL_TIMER__POR (0x00000000)
-#define SITAR_A_CDC_TX1_VOL_CTL_GAIN (0x221)
-#define SITAR_A_CDC_TX1_VOL_CTL_GAIN__POR (0x00000000)
-#define SITAR_A_CDC_TX2_VOL_CTL_GAIN (0x229)
-#define SITAR_A_CDC_TX2_VOL_CTL_GAIN__POR (0x00000000)
-#define SITAR_A_CDC_TX3_VOL_CTL_GAIN (0x231)
-#define SITAR_A_CDC_TX3_VOL_CTL_GAIN__POR (0x00000000)
-#define SITAR_A_CDC_TX4_VOL_CTL_GAIN (0x239)
-#define SITAR_A_CDC_TX4_VOL_CTL_GAIN__POR (0x00000000)
-#define SITAR_A_CDC_TX5_VOL_CTL_GAIN (0x241)
-#define SITAR_A_CDC_TX5_VOL_CTL_GAIN__POR (0x00000000)
-#define SITAR_A_CDC_TX1_VOL_CTL_CFG (0x222)
-#define SITAR_A_CDC_TX1_VOL_CTL_CFG__POR (0x00000000)
-#define SITAR_A_CDC_TX2_VOL_CTL_CFG (0x22A)
-#define SITAR_A_CDC_TX2_VOL_CTL_CFG__POR (0x00000000)
-#define SITAR_A_CDC_TX3_VOL_CTL_CFG (0x232)
-#define SITAR_A_CDC_TX3_VOL_CTL_CFG__POR (0x00000000)
-#define SITAR_A_CDC_TX4_VOL_CTL_CFG (0x23A)
-#define SITAR_A_CDC_TX4_VOL_CTL_CFG__POR (0x00000000)
+#define SITAR_A_CDC_ANC2_CTL (0x280)
+#define SITAR_A_CDC_ANC2_CTL__POR (0x00000000)
+#define SITAR_A_CDC_ANC2_SHIFT (0x281)
+#define SITAR_A_CDC_ANC2_SHIFT__POR (0x00000000)
+#define SITAR_A_CDC_ANC2_IIR_B1_CTL (0x282)
+#define SITAR_A_CDC_ANC2_IIR_B1_CTL__POR (0x00000000)
+#define SITAR_A_CDC_ANC2_IIR_B2_CTL (0x283)
+#define SITAR_A_CDC_ANC2_IIR_B2_CTL__POR (0x00000000)
+#define SITAR_A_CDC_ANC2_IIR_B3_CTL (0x284)
+#define SITAR_A_CDC_ANC2_IIR_B3_CTL__POR (0x00000000)
+#define SITAR_A_CDC_ANC2_IIR_B4_CTL (0x285)
+#define SITAR_A_CDC_ANC2_IIR_B4_CTL__POR (0x00000000)
+#define SITAR_A_CDC_ANC2_LPF_B1_CTL (0x286)
+#define SITAR_A_CDC_ANC2_LPF_B1_CTL__POR (0x00000000)
+#define SITAR_A_CDC_ANC2_LPF_B2_CTL (0x287)
+#define SITAR_A_CDC_ANC2_LPF_B2_CTL__POR (0x00000000)
+#define SITAR_A_CDC_ANC2_LPF_B3_CTL (0x288)
+#define SITAR_A_CDC_ANC2_LPF_B3_CTL__POR (0x00000000)
+#define SITAR_A_CDC_ANC2_SPARE (0x289)
+#define SITAR_A_CDC_ANC2_SPARE__POR (0x00000000)
+#define SITAR_A_CDC_ANC2_SMLPF_CTL (0x28A)
+#define SITAR_A_CDC_ANC2_SMLPF_CTL__POR (0x00000000)
+#define SITAR_A_CDC_ANC2_DCFLT_CTL (0x28B)
+#define SITAR_A_CDC_ANC2_DCFLT_CTL__POR (0x00000000)
+#define SITAR_A_CDC_TX1_VOL_CTL_TIMER (0x220)
+#define SITAR_A_CDC_TX1_VOL_CTL_TIMER__POR (0x00000000)
+#define SITAR_A_CDC_TX1_VOL_CTL_GAIN (0x221)
+#define SITAR_A_CDC_TX1_VOL_CTL_GAIN__POR (0x00000000)
+#define SITAR_A_CDC_TX1_VOL_CTL_CFG (0x222)
+#define SITAR_A_CDC_TX1_VOL_CTL_CFG__POR (0x00000000)
#define SITAR_A_CDC_TX1_MUX_CTL (0x223)
#define SITAR_A_CDC_TX1_MUX_CTL__POR (0x00000008)
-#define SITAR_A_CDC_TX1_CLK_FS_CTL (0x00000224)
-#define SITAR_A_CDC_TX1_CLK_FS_CTL__POR (0x00000003)
-#define SITAR_A_CDC_TX2_CLK_FS_CTL (0x0000022C)
-#define SITAR_A_CDC_TX2_CLK_FS_CTL__POR (0x00000003)
-#define SITAR_A_CDC_TX3_CLK_FS_CTL (0x00000234)
-#define SITAR_A_CDC_TX3_CLK_FS_CTL__POR (0x00000003)
-#define SITAR_A_CDC_TX4_CLK_FS_CTL (0x0000023C)
-#define SITAR_A_CDC_TX4_CLK_FS_CTL__POR (0x00000003)
-#define SITAR_A_CDC_TX1_DMIC_CTL (0x225)
+#define SITAR_A_CDC_TX1_CLK_FS_CTL (0x224)
+#define SITAR_A_CDC_TX1_CLK_FS_CTL__POR (0x00000003)
+#define SITAR_A_CDC_TX1_DMIC_CTL (0x225)
#define SITAR_A_CDC_TX1_DMIC_CTL__POR (0x00000000)
-#define SITAR_A_CDC_TX2_MUX_CTL (0x22B)
+
+#define SITAR_A_CDC_TX2_VOL_CTL_TIMER (0x228)
+#define SITAR_A_CDC_TX2_VOL_CTL_TIMER__POR (0x00000000)
+#define SITAR_A_CDC_TX2_VOL_CTL_GAIN (0x229)
+#define SITAR_A_CDC_TX2_VOL_CTL_GAIN__POR (0x00000000)
+#define SITAR_A_CDC_TX2_VOL_CTL_CFG (0x22A)
+#define SITAR_A_CDC_TX2_VOL_CTL_CFG__POR (0x00000000)
+#define SITAR_A_CDC_TX2_MUX_CTL (0x22B)
#define SITAR_A_CDC_TX2_MUX_CTL__POR (0x00000008)
-#define SITAR_A_CDC_TX3_MUX_CTL (0x233)
+#define SITAR_A_CDC_TX2_CLK_FS_CTL (0x22C)
+#define SITAR_A_CDC_TX2_CLK_FS_CTL__POR (0x00000003)
+#define SITAR_A_CDC_TX2_DMIC_CTL (0x22D)
+#define SITAR_A_CDC_TX2_DMIC_CTL__POR (0x00000000)
+
+#define SITAR_A_CDC_TX3_VOL_CTL_TIMER (0x230)
+#define SITAR_A_CDC_TX3_VOL_CTL_TIMER__POR (0x00000000)
+#define SITAR_A_CDC_TX3_VOL_CTL_GAIN (0x231)
+#define SITAR_A_CDC_TX3_VOL_CTL_GAIN__POR (0x00000000)
+#define SITAR_A_CDC_TX3_VOL_CTL_CFG (0x232)
+#define SITAR_A_CDC_TX3_VOL_CTL_CFG__POR (0x00000000)
+#define SITAR_A_CDC_TX3_MUX_CTL (0x233)
#define SITAR_A_CDC_TX3_MUX_CTL__POR (0x00000008)
-#define SITAR_A_CDC_TX4_MUX_CTL (0x23B)
+#define SITAR_A_CDC_TX3_CLK_FS_CTL (0x234)
+#define SITAR_A_CDC_TX3_CLK_FS_CTL__POR (0x00000003)
+#define SITAR_A_CDC_TX3_DMIC_CTL (0x235)
+#define SITAR_A_CDC_TX3_DMIC_CTL__POR (0x00000000)
+
+#define SITAR_A_CDC_TX4_VOL_CTL_TIMER (0x239)
+#define SITAR_A_CDC_TX4_VOL_CTL_TIMER__POR (0x00000000)
+#define SITAR_A_CDC_TX4_VOL_CTL_GAIN (0x23A)
+#define SITAR_A_CDC_TX4_VOL_CTL_GAIN__POR (0x00000000)
+#define SITAR_A_CDC_TX4_VOL_CTL_CFG (0x23B)
+#define SITAR_A_CDC_TX4_VOL_CTL_CFG__POR (0x00000000)
+#define SITAR_A_CDC_TX4_MUX_CTL (0x23C)
#define SITAR_A_CDC_TX4_MUX_CTL__POR (0x00000008)
-#define SITAR_A_CDC_TX5_MUX_CTL (0x243)
+#define SITAR_A_CDC_TX4_CLK_FS_CTL (0x23D)
+#define SITAR_A_CDC_TX4_CLK_FS_CTL__POR (0x00000003)
+#define SITAR_A_CDC_TX4_DMIC_CTL (0x23E)
+#define SITAR_A_CDC_TX4_DMIC_CTL__POR (0x00000000)
+
+#define SITAR_A_CDC_TX5_VOL_CTL_TIMER (0x240)
+#define SITAR_A_CDC_TX5_VOL_CTL_TIMER__POR (0x00000000)
+#define SITAR_A_CDC_TX5_VOL_CTL_GAIN (0x241)
+#define SITAR_A_CDC_TX5_VOL_CTL_GAIN__POR (0x00000000)
+#define SITAR_A_CDC_TX5_VOL_CTL_CFG (0x242)
+#define SITAR_A_CDC_TX5_VOL_CTL_CFG__POR (0x00000000)
+#define SITAR_A_CDC_TX5_MUX_CTL (0x243)
#define SITAR_A_CDC_TX5_MUX_CTL__POR (0x00000008)
+#define SITAR_A_CDC_TX5_CLK_FS_CTL (0x244)
+#define SITAR_A_CDC_TX5_CLK_FS_CTL__POR (0x00000003)
+#define SITAR_A_CDC_TX5_DMIC_CTL (0x245)
+#define SITAR_A_CDC_TX5_DMIC_CTL__POR (0x00000000)
#define SITAR_A_CDC_SRC1_PDA_CFG (0x2A0)
#define SITAR_A_CDC_SRC1_PDA_CFG__POR (0x00000000)
#define SITAR_A_CDC_SRC1_FS_CTL (0x2A1)
#define SITAR_A_CDC_SRC1_FS_CTL__POR (0x0000001b)
+#define SITAR_A_CDC_SRC2_PDA_CFG (0x2A8)
+#define SITAR_A_CDC_SRC2_PDA_CFG__POR (0x00000000)
+#define SITAR_A_CDC_SRC2_FS_CTL (0x2A9)
+#define SITAR_A_CDC_SRC2_FS_CTL__POR (0x0000001b)
-#define SITAR_A_CDC_RX1_B1_CTL (0x000002B0)
+#define SITAR_A_CDC_RX1_B1_CTL (0x2B0)
#define SITAR_A_CDC_RX1_B1_CTL__POR (0x00000000)
-#define SITAR_A_CDC_RX2_B1_CTL (0x000002B8)
-#define SITAR_A_CDC_RX2_B1_CTL__POR (0x00000000)
-#define SITAR_A_CDC_RX3_B1_CTL (0x000002C0)
-#define SITAR_A_CDC_RX3_B1_CTL__POR (0x00000000)
-
-#define SITAR_A_CDC_RX1_B2_CTL (0x000002B1)
+#define SITAR_A_CDC_RX1_B2_CTL (0x2B1)
#define SITAR_A_CDC_RX1_B2_CTL__POR (0x00000000)
-#define SITAR_A_CDC_RX2_B2_CTL (0x000002B9)
-#define SITAR_A_CDC_RX2_B2_CTL__POR (0x00000000)
-#define SITAR_A_CDC_RX3_B2_CTL (0x000002C1)
-#define SITAR_A_CDC_RX3_B2_CTL__POR (0x00000000)
-
-#define SITAR_A_CDC_RX1_B3_CTL (0x000002B2)
+#define SITAR_A_CDC_RX1_B3_CTL (0x2B2)
#define SITAR_A_CDC_RX1_B3_CTL__POR (0x00000000)
-#define SITAR_A_CDC_RX2_B3_CTL (0x000002BA)
-#define SITAR_A_CDC_RX2_B3_CTL__POR (0x00000000)
-#define SITAR_A_CDC_RX3_B3_CTL (0x000002C2)
-#define SITAR_A_CDC_RX3_B3_CTL__POR (0x00000000)
-
-#define SITAR_A_CDC_RX1_B4_CTL (0x000002B3)
+#define SITAR_A_CDC_RX1_B4_CTL (0x2B3)
#define SITAR_A_CDC_RX1_B4_CTL__POR (0x00000000)
-#define SITAR_A_CDC_RX2_B4_CTL (0x000002BB)
-#define SITAR_A_CDC_RX2_B4_CTL__POR (0x00000000)
-#define SITAR_A_CDC_RX3_B4_CTL (0x000002C3)
-#define SITAR_A_CDC_RX3_B4_CTL__POR (0x00000000)
-
-#define SITAR_A_CDC_RX1_B5_CTL (0x000002B4)
+#define SITAR_A_CDC_RX1_B5_CTL (0x2B4)
#define SITAR_A_CDC_RX1_B5_CTL__POR (0x00000078)
-#define SITAR_A_CDC_RX2_B5_CTL (0x000002BC)
-#define SITAR_A_CDC_RX2_B5_CTL__POR (0x00000078)
-#define SITAR_A_CDC_RX3_B5_CTL (0x000002C4)
-#define SITAR_A_CDC_RX3_B5_CTL__POR (0x00000078)
-
-#define SITAR_A_CDC_RX1_B6_CTL (0x000002B5)
+#define SITAR_A_CDC_RX1_B6_CTL (0x2B5)
#define SITAR_A_CDC_RX1_B6_CTL__POR (0x00000080)
-#define SITAR_A_CDC_RX2_B6_CTL (0x000002BD)
+#define SITAR_A_CDC_RX1_VOL_CTL_B1_CTL (0x2B6)
+#define SITAR_A_CDC_RX1_VOL_CTL_B1_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX1_VOL_CTL_B2_CTL (0x2B7)
+#define SITAR_A_CDC_RX1_VOL_CTL_B2_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX2_B1_CTL (0x2B8)
+#define SITAR_A_CDC_RX2_B1_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX2_B2_CTL (0x2B9)
+#define SITAR_A_CDC_RX2_B2_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX2_B3_CTL (0x2BA)
+#define SITAR_A_CDC_RX2_B3_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX2_B4_CTL (0x2BB)
+#define SITAR_A_CDC_RX2_B4_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX2_B5_CTL (0x2BC)
+#define SITAR_A_CDC_RX2_B5_CTL__POR (0x00000078)
+#define SITAR_A_CDC_RX2_B6_CTL (0x2BD)
#define SITAR_A_CDC_RX2_B6_CTL__POR (0x00000080)
-#define SITAR_A_CDC_RX3_B6_CTL (0x000002C5)
+#define SITAR_A_CDC_RX2_VOL_CTL_B1_CTL (0x2BE)
+#define SITAR_A_CDC_RX2_VOL_CTL_B1_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX2_VOL_CTL_B2_CTL (0x2BF)
+#define SITAR_A_CDC_RX2_VOL_CTL_B2_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX3_B1_CTL (0x2C0)
+#define SITAR_A_CDC_RX3_B1_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX3_B2_CTL (0x2C1)
+#define SITAR_A_CDC_RX3_B2_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX3_B3_CTL (0x2C2)
+#define SITAR_A_CDC_RX3_B3_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX3_B4_CTL (0x2C3)
+#define SITAR_A_CDC_RX3_B4_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX3_B5_CTL (0x2C4)
+#define SITAR_A_CDC_RX3_B5_CTL__POR (0x00000078)
+#define SITAR_A_CDC_RX3_B6_CTL (0x2C5)
#define SITAR_A_CDC_RX3_B6_CTL__POR (0x00000080)
+#define SITAR_A_CDC_RX3_VOL_CTL_B1_CTL (0x2C6)
+#define SITAR_A_CDC_RX3_VOL_CTL_B1_CTL__POR (0x00000000)
+#define SITAR_A_CDC_RX3_VOL_CTL_B2_CTL (0x2C7)
+#define SITAR_A_CDC_RX3_VOL_CTL_B2_CTL__POR (0x00000000)
-
-#define SITAR_A_CDC_RX1_VOL_CTL_B1_CTL (0x2B6)
-#define SITAR_A_CDC_RX1_VOL_CTL_B1_CTL__POR (0x00000000)
-#define SITAR_A_CDC_RX1_VOL_CTL_B2_CTL (0x2B7)
-#define SITAR_A_CDC_RX1_VOL_CTL_B2_CTL__POR (0x00000000)
-#define SITAR_A_CDC_RX2_VOL_CTL_B2_CTL (0x2BF)
-#define SITAR_A_CDC_RX2_VOL_CTL_B2_CTL__POR (0x00000000)
-#define SITAR_A_CDC_RX3_VOL_CTL_B2_CTL (0x2C7)
-#define SITAR_A_CDC_RX3_VOL_CTL_B2_CTL__POR (0x00000000)
-
-#define SITAR_A_CDC_CLK_ANC_RESET_CTL (0x300)
-#define SITAR_A_CDC_CLK_ANC_RESET_CTL__POR (0x00000000)
-#define SITAR_A_CDC_CLK_RX_RESET_CTL (0x301)
-#define SITAR_A_CDC_CLK_RX_RESET_CTL__POR (0x00000000)
-#define SITAR_A_CDC_CLK_TX_RESET_B1_CTL (0x302)
-#define SITAR_A_CDC_CLK_TX_RESET_B1_CTL__POR (0x00000000)
-#define SITAR_A_CDC_CLK_TX_RESET_B2_CTL (0x303)
-#define SITAR_A_CDC_CLK_TX_RESET_B2_CTL__POR (0x00000000)
-#define SITAR_A_CDC_CLK_DMIC_CTL (0x304)
+#define SITAR_A_CDC_CLK_ANC_RESET_CTL (0x300)
+#define SITAR_A_CDC_CLK_ANC_RESET_CTL__POR (0x00000000)
+#define SITAR_A_CDC_CLK_RX_RESET_CTL (0x301)
+#define SITAR_A_CDC_CLK_RX_RESET_CTL__POR (0x00000000)
+#define SITAR_A_CDC_CLK_TX_RESET_B1_CTL (0x302)
+#define SITAR_A_CDC_CLK_TX_RESET_B1_CTL__POR (0x00000000)
+#define SITAR_A_CDC_CLK_TX_RESET_B2_CTL (0x303)
+#define SITAR_A_CDC_CLK_TX_RESET_B2_CTL__POR (0x00000000)
+#define SITAR_A_CDC_CLK_DMIC_CTL (0x304)
#define SITAR_A_CDC_CLK_DMIC_CTL__POR (0x00000000)
#define SITAR_A_CDC_CLK_RX_I2S_CTL (0x305)
#define SITAR_A_CDC_CLK_RX_I2S_CTL__POR (0x00000003)
@@ -654,7 +703,23 @@
#define SITAR_A_CDC_COMP1_SHUT_DOWN_STATUS__POR (0x00000003)
#define SITAR_A_CDC_COMP1_FS_CFG (0x377)
#define SITAR_A_CDC_COMP1_FS_CFG__POR (0x0000001b)
-#define SITAR_A_CDC_CONN_RX1_B1_CTL (0x380)
+#define SITAR_A_CDC_COMP2_B1_CTL (0x378)
+#define SITAR_A_CDC_COMP2_B1_CTL__POR (0x00000030)
+#define SITAR_A_CDC_COMP2_B2_CTL (0x379)
+#define SITAR_A_CDC_COMP2_B2_CTL__POR (0x000000b5)
+#define SITAR_A_CDC_COMP2_B3_CTL (0x37A)
+#define SITAR_A_CDC_COMP2_B3_CTL__POR (0x00000028)
+#define SITAR_A_CDC_COMP2_B4_CTL (0x37B)
+#define SITAR_A_CDC_COMP2_B4_CTL__POR (0x0000003c)
+#define SITAR_A_CDC_COMP2_B5_CTL (0x37C)
+#define SITAR_A_CDC_COMP2_B5_CTL__POR (0x0000001f)
+#define SITAR_A_CDC_COMP2_B6_CTL (0x37D)
+#define SITAR_A_CDC_COMP2_B6_CTL__POR (0x00000000)
+#define SITAR_A_CDC_COMP2_SHUT_DOWN_STATUS (0x37E)
+#define SITAR_A_CDC_COMP2_SHUT_DOWN_STATUS__POR (0x00000003)
+#define SITAR_A_CDC_COMP2_FS_CFG (0x37F)
+#define SITAR_A_CDC_COMP2_FS_CFG__POR (0x0000001b)
+#define SITAR_A_CDC_CONN_RX1_B1_CTL (0x380)
#define SITAR_A_CDC_CONN_RX1_B1_CTL__POR (0x00000000)
#define SITAR_A_CDC_CONN_RX1_B2_CTL (0x381)
#define SITAR_A_CDC_CONN_RX1_B2_CTL__POR (0x00000000)
diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h b/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
index 0d5d058..45abd92 100644
--- a/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
+++ b/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
@@ -16,27 +16,6 @@
#include <linux/slimbus/slimbus.h>
#include <linux/mfd/wcd9xxx/core.h>
-/* Channel numbers to be used for each port */
-enum {
- SLIM_TX_1 = 128,
- SLIM_TX_2 = 129,
- SLIM_TX_3 = 130,
- SLIM_TX_4 = 131,
- SLIM_TX_5 = 132,
- SLIM_TX_6 = 133,
- SLIM_TX_7 = 134,
- SLIM_TX_8 = 135,
- SLIM_TX_9 = 136,
- SLIM_TX_10 = 137,
- SLIM_RX_1 = 138,
- SLIM_RX_2 = 139,
- SLIM_RX_3 = 140,
- SLIM_RX_4 = 141,
- SLIM_RX_5 = 142,
- SLIM_RX_6 = 143,
- SLIM_RX_7 = 144,
- SLIM_MAX = 145
-};
/*
* client is expected to give port ids in the range of
@@ -92,30 +71,42 @@
/* slave port water mark level
* (0: 6bytes, 1: 9bytes, 2: 12 bytes, 3: 15 bytes)
*/
-#define SLAVE_PORT_WATER_MARK_VALUE 2
+#define SLAVE_PORT_WATER_MARK_6BYTES 0
+#define SLAVE_PORT_WATER_MARK_9BYTES 1
+#define SLAVE_PORT_WATER_MARK_12BYTES 2
+#define SLAVE_PORT_WATER_MARK_15BYTES 3
#define SLAVE_PORT_WATER_MARK_SHIFT 1
#define SLAVE_PORT_ENABLE 1
#define SLAVE_PORT_DISABLE 0
-
+#define WATER_MARK_VAL \
+ ((SLAVE_PORT_WATER_MARK_12BYTES << SLAVE_PORT_WATER_MARK_SHIFT) | \
+ (SLAVE_PORT_ENABLE))
#define BASE_CH_NUM 128
-int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la);
+int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx,
+ u8 wcd9xxx_pgd_la,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot);
int wcd9xxx_deinit_slimslave(struct wcd9xxx *wcd9xxx);
-int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
- unsigned int tot_ch, unsigned int rate);
-int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
- unsigned int tot_ch, unsigned int rate);
-int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
- unsigned int tot_ch);
-int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
- unsigned int tot_ch);
+int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx,
+ struct list_head *wcd9xxx_ch_list,
+ unsigned int rate, unsigned int bit_width,
+ u16 *grph);
+int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx,
+ struct list_head *wcd9xxx_ch_list,
+ unsigned int rate, unsigned int bit_width,
+ u16 *grph);
+int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx,
+ struct list_head *wcd9xxx_ch_list, u16 grph);
+int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx,
+ struct list_head *wcd9xxx_ch_list, u16 grph);
int wcd9xxx_get_channel(struct wcd9xxx *wcd9xxx,
unsigned int *rx_ch,
unsigned int *tx_ch);
int wcd9xxx_get_slave_port(unsigned int ch_num);
-int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
- unsigned int tot_ch, unsigned int rx_tx);
+int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx,
+ struct list_head *wcd9xxx_ch_list, u16 grph);
#endif /* __WCD9310_SLIMSLAVE_H_ */
diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h b/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
index c66e953..4b7a32c 100644
--- a/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
+++ b/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
@@ -16,27 +16,191 @@
#define WCD9XXX_A_CHIP_CTL (0x00)
#define WCD9XXX_A_CHIP_CTL__POR (0x00000000)
#define WCD9XXX_A_CHIP_STATUS (0x01)
-#define WCD9XXX_A_CHIP_STATUS__POR (0x00000000)
-#define WCD9XXX_A_CHIP_ID_BYTE_0 (0x04)
-#define WCD9XXX_A_CHIP_ID_BYTE_0__POR (0x00000000)
-#define WCD9XXX_A_CHIP_ID_BYTE_1 (0x05)
-#define WCD9XXX_A_CHIP_ID_BYTE_1__POR (0x00000000)
-#define WCD9XXX_A_CHIP_ID_BYTE_2 (0x06)
-#define WCD9XXX_A_CHIP_ID_BYTE_2__POR (0x00000000)
-#define WCD9XXX_A_CHIP_ID_BYTE_3 (0x07)
-#define WCD9XXX_A_CHIP_ID_BYTE_3__POR (0x00000001)
+#define WCD9XXX_A_CHIP_STATUS__POR (0x00000000)
+#define WCD9XXX_A_CHIP_ID_BYTE_0 (0x04)
+#define WCD9XXX_A_CHIP_ID_BYTE_0__POR (0x00000000)
+#define WCD9XXX_A_CHIP_ID_BYTE_1 (0x05)
+#define WCD9XXX_A_CHIP_ID_BYTE_1__POR (0x00000000)
+#define WCD9XXX_A_CHIP_ID_BYTE_2 (0x06)
+#define WCD9XXX_A_CHIP_ID_BYTE_2__POR (0x00000000)
+#define WCD9XXX_A_CHIP_ID_BYTE_3 (0x07)
+#define WCD9XXX_A_CHIP_ID_BYTE_3__POR (0x00000001)
#define WCD9XXX_A_CHIP_VERSION (0x08)
-#define WCD9XXX_A_CHIP_VERSION__POR (0x00000020)
+#define WCD9XXX_A_CHIP_VERSION__POR (0x00000020)
#define WCD9XXX_A_SB_VERSION (0x09)
-#define WCD9XXX_A_SB_VERSION__POR (0x00000010)
+#define WCD9XXX_A_SB_VERSION__POR (0x00000010)
#define WCD9XXX_A_SLAVE_ID_1 (0x0C)
-#define WCD9XXX_A_SLAVE_ID_1__POR (0x00000077)
+#define WCD9XXX_A_SLAVE_ID_1__POR (0x00000077)
#define WCD9XXX_A_SLAVE_ID_2 (0x0D)
-#define WCD9XXX_A_SLAVE_ID_2__POR (0x00000066)
+#define WCD9XXX_A_SLAVE_ID_2__POR (0x00000066)
#define WCD9XXX_A_SLAVE_ID_3 (0x0E)
-#define WCD9XXX_A_SLAVE_ID_3__POR (0x00000055)
+#define WCD9XXX_A_SLAVE_ID_3__POR (0x00000055)
#define WCD9XXX_A_CDC_CTL (0x80)
#define WCD9XXX_A_CDC_CTL__POR (0x00000000)
#define WCD9XXX_A_LEAKAGE_CTL (0x88)
-#define WCD9XXX_A_LEAKAGE_CTL__POR (0x00000004)
+#define WCD9XXX_A_LEAKAGE_CTL__POR (0x00000004)
+#define WCD9XXX_A_INTR_MODE (0x90)
+#define WCD9XXX_A_INTR_MASK0 (0x94)
+#define WCD9XXX_A_INTR_STATUS0 (0x98)
+#define WCD9XXX_A_INTR_CLEAR0 (0x9C)
+#define WCD9XXX_A_INTR_LEVEL0 (0xA0)
+#define WCD9XXX_A_INTR_LEVEL1 (0xA1)
+#define WCD9XXX_A_INTR_LEVEL2 (0xA2)
+#define WCD9XXX_A_RX_HPH_CNP_EN (0x1AB)
+#define WCD9XXX_A_RX_HPH_CNP_EN__POR (0x80)
+#define WCD9XXX_A_RX_HPH_CNP_EN (0x1AB)
+#define WCD9XXX_A_RX_HPH_CNP_EN__POR (0x80)
+#define WCD9XXX_A_BIAS_CENTRAL_BG_CTL (0x101)
+#define WCD9XXX_A_BIAS_CENTRAL_BG_CTL__POR (0x50)
+#define WCD9XXX_A_CLK_BUFF_EN1 (0x108)
+#define WCD9XXX_A_CLK_BUFF_EN1__POR (0x04)
+#define WCD9XXX_A_CLK_BUFF_EN2 (0x109)
+#define WCD9XXX_A_CLK_BUFF_EN2__POR (0x02)
+#define WCD9XXX_A_RX_COM_BIAS (0x1A2)
+#define WCD9XXX_A_RX_COM_BIAS__POR (0x00)
+#define WCD9XXX_A_RC_OSC_FREQ (0x1FA)
+#define WCD9XXX_A_RC_OSC_FREQ__POR (0x46)
+#define WCD9XXX_A_BIAS_OSC_BG_CTL (0x105)
+#define WCD9XXX_A_BIAS_OSC_BG_CTL__POR (0x16)
+#define WCD9XXX_A_RC_OSC_TEST (0x1FB)
+#define WCD9XXX_A_RC_OSC_TEST__POR (0x0A)
+#define WCD9XXX_A_CDC_CLK_MCLK_CTL (0x311)
+#define WCD9XXX_A_CDC_CLK_MCLK_CTL__POR (0x00)
+
+#define WCD9XXX_A_CDC_MBHC_EN_CTL (0x3C0)
+#define WCD9XXX_A_CDC_MBHC_EN_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_FIR_B1_CFG (0x3C1)
+#define WCD9XXX_A_CDC_MBHC_FIR_B1_CFG__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_FIR_B2_CFG (0x3C2)
+#define WCD9XXX_A_CDC_MBHC_FIR_B2_CFG__POR (0x06)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL (0x3C3)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL__POR (0x03)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL (0x3C4)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL__POR (0x09)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL (0x3C5)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL__POR (0x1E)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL (0x3C6)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL__POR (0x45)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL (0x3C7)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL__POR (0x04)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL (0x3C8)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL__POR (0x78)
+#define WCD9XXX_A_CDC_MBHC_B1_STATUS (0x3C9)
+#define WCD9XXX_A_CDC_MBHC_B1_STATUS__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_B2_STATUS (0x3CA)
+#define WCD9XXX_A_CDC_MBHC_B2_STATUS__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_B3_STATUS (0x3CB)
+#define WCD9XXX_A_CDC_MBHC_B3_STATUS__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_B4_STATUS (0x3CC)
+#define WCD9XXX_A_CDC_MBHC_B4_STATUS__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_B5_STATUS (0x3CD)
+#define WCD9XXX_A_CDC_MBHC_B5_STATUS__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_B1_CTL (0x3CE)
+#define WCD9XXX_A_CDC_MBHC_B1_CTL__POR (0xC0)
+#define WCD9XXX_A_CDC_MBHC_B2_CTL (0x3CF)
+#define WCD9XXX_A_CDC_MBHC_B2_CTL__POR (0x5D)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL (0x3D0)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL (0x3D1)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL (0x3D2)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL (0x3D3)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL (0x3D4)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL (0x3D5)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B7_CTL (0x3D6)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B7_CTL__POR (0xFF)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B8_CTL (0x3D7)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B8_CTL__POR (0x07)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL (0x3D8)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL__POR (0xFF)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL (0x3D9)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL__POR (0x7F)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL (0x3DA)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL (0x3DB)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL__POR (0x80)
+#define WCD9XXX_A_CDC_MBHC_CLK_CTL (0x3DC)
+#define WCD9XXX_A_CDC_MBHC_CLK_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_INT_CTL (0x3DD)
+#define WCD9XXX_A_CDC_MBHC_INT_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_DEBUG_CTL (0x3DE)
+#define WCD9XXX_A_CDC_MBHC_DEBUG_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_SPARE (0x3DF)
+#define WCD9XXX_A_CDC_MBHC_SPARE__POR (0x00)
+#define WCD9XXX_A_MBHC_SCALING_MUX_1 (0x14E)
+#define WCD9XXX_A_MBHC_SCALING_MUX_1__POR (0x00)
+#define WCD9XXX_A_RX_HPH_OCP_CTL (0x1AA)
+#define WCD9XXX_A_RX_HPH_OCP_CTL__POR (0x68)
+#define WCD9XXX_A_MICB_1_CTL (0x12B)
+#define WCD9XXX_A_MICB_1_CTL__POR (0x16)
+#define WCD9XXX_A_MICB_1_INT_RBIAS (0x12C)
+#define WCD9XXX_A_MICB_1_INT_RBIAS__POR (0x24)
+#define WCD9XXX_A_MICB_1_MBHC (0x12D)
+#define WCD9XXX_A_MICB_1_MBHC__POR (0x01)
+#define WCD9XXX_A_MICB_CFILT_2_CTL (0x12E)
+#define WCD9XXX_A_MICB_CFILT_2_CTL__POR (0x40)
+#define WCD9XXX_A_MICB_CFILT_2_VAL (0x12F)
+#define WCD9XXX_A_MICB_CFILT_2_VAL__POR (0x80)
+#define WCD9XXX_A_MICB_CFILT_2_PRECHRG (0x130)
+#define WCD9XXX_A_MICB_CFILT_2_PRECHRG__POR (0x38)
+#define WCD9XXX_A_MICB_2_CTL (0x131)
+#define WCD9XXX_A_MICB_2_CTL__POR (0x16)
+#define WCD9XXX_A_MICB_2_INT_RBIAS (0x132)
+#define WCD9XXX_A_MICB_2_INT_RBIAS__POR (0x24)
+#define WCD9XXX_A_MICB_2_MBHC (0x133)
+#define WCD9XXX_A_MICB_2_MBHC__POR (0x02)
+#define WCD9XXX_A_MICB_CFILT_3_CTL (0x134)
+#define WCD9XXX_A_MICB_CFILT_3_CTL__POR (0x40)
+#define WCD9XXX_A_MICB_CFILT_3_VAL (0x135)
+#define WCD9XXX_A_MICB_CFILT_3_VAL__POR (0x80)
+#define WCD9XXX_A_MICB_CFILT_3_PRECHRG (0x136)
+#define WCD9XXX_A_MICB_CFILT_3_PRECHRG__POR (0x38)
+#define WCD9XXX_A_MICB_3_CTL (0x137)
+#define WCD9XXX_A_MICB_3_CTL__POR (0x16)
+#define WCD9XXX_A_MICB_3_INT_RBIAS (0x138)
+#define WCD9XXX_A_MICB_3_INT_RBIAS__POR (0x24)
+#define WCD9XXX_A_MICB_3_MBHC (0x139)
+#define WCD9XXX_A_MICB_3_MBHC__POR (0x00)
+#define WCD9XXX_A_MICB_4_CTL (0x13D)
+#define WCD9XXX_A_MICB_4_CTL__POR (0x16)
+#define WCD9XXX_A_MICB_4_INT_RBIAS (0x13E)
+#define WCD9XXX_A_MICB_4_INT_RBIAS__POR (0x24)
+#define WCD9XXX_A_MICB_4_MBHC (0x13F)
+#define WCD9XXX_A_MICB_4_MBHC__POR (0x01)
+#define WCD9XXX_A_MICB_CFILT_1_VAL (0x129)
+#define WCD9XXX_A_MICB_CFILT_1_VAL__POR (0x80)
+#define WCD9XXX_A_MBHC_HPH (0x1FE)
+#define WCD9XXX_A_MBHC_HPH__POR (0x44)
+#define WCD9XXX_A_RX_HPH_CNP_WG_TIME (0x1AD)
+#define WCD9XXX_A_RX_HPH_CNP_WG_TIME__POR (0x2A)
+#define WCD9XXX_A_RX_HPH_R_DAC_CTL (0x1B7)
+#define WCD9XXX_A_RX_HPH_R_DAC_CTL__POR (0x00)
+#define WCD9XXX_A_RX_HPH_L_DAC_CTL (0x1B1)
+#define WCD9XXX_A_RX_HPH_L_DAC_CTL__POR (0x00)
+#define WCD9XXX_A_TX_7_MBHC_EN (0x171)
+#define WCD9XXX_A_TX_7_MBHC_EN__POR (0x0C)
+#define WCD9XXX_A_PIN_CTL_OE0 (0x010)
+#define WCD9XXX_A_PIN_CTL_OE0__POR (0x00)
+#define WCD9XXX_A_PIN_CTL_OE1 (0x011)
+#define WCD9XXX_A_PIN_CTL_OE1__POR (0x00)
+#define WCD9XXX_A_MICB_CFILT_1_CTL (0x128)
+#define WCD9XXX_A_LDO_H_MODE_1 (0x110)
+#define WCD9XXX_A_LDO_H_MODE_1__POR (0x65)
+#define WCD9XXX_A_MICB_CFILT_1_CTL__POR (0x40)
+#define WCD9XXX_A_TX_7_MBHC_TEST_CTL (0x174)
+#define WCD9XXX_A_TX_7_MBHC_TEST_CTL__POR (0x38)
+#define WCD9XXX_A_MBHC_SCALING_MUX_2 (0x14F)
+#define WCD9XXX_A_MBHC_SCALING_MUX_2__POR (0x80)
+#define WCD9XXX_A_TX_COM_BIAS (0x14C)
+#define WCD9XXX_A_TX_COM_BIAS__POR (0xF0)
+
+#define WCD9XXX_A_MBHC_INSERT_DETECT (0x14A) /* TAIKO and later */
+#define WCD9XXX_A_MBHC_INSERT_DETECT__POR (0x00)
+#define WCD9XXX_A_MBHC_INSERT_DET_STATUS (0x14B) /* TAIKO and later */
+#define WCD9XXX_A_MBHC_INSERT_DET_STATUS__POR (0x00)
+
#endif
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 0330dfb..30ca87c 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -187,6 +187,26 @@
#define SDIO_MAX_FUNCS 7
+enum mmc_packed_stop_reasons {
+ EXCEEDS_SEGMENTS = 0,
+ EXCEEDS_SECTORS,
+ WRONG_DATA_DIR,
+ FLUSH_OR_DISCARD,
+ EMPTY_QUEUE,
+ REL_WRITE,
+ THRESHOLD,
+ LARGE_SEC_ALIGN,
+ MAX_REASONS,
+};
+
+struct mmc_wr_pack_stats {
+ u32 *packing_events;
+ u32 pack_stop_reason[MAX_REASONS];
+ spinlock_t lock;
+ bool enabled;
+ bool print_in_read;
+};
+
/* The number of MMC physical partitions. These consist of:
* boot partitions (2), general purpose partitions (4) in MMC v4.4.
*/
@@ -209,6 +229,64 @@
#define MMC_BLK_DATA_AREA_GP (1<<2)
};
+#define BKOPS_NUM_OF_SEVERITY_LEVELS 3
+#define BKOPS_SEVERITY_1_INDEX 0
+#define BKOPS_SEVERITY_2_INDEX 1
+#define BKOPS_SEVERITY_3_INDEX 2
+struct mmc_bkops_stats {
+ spinlock_t lock;
+ bool enabled;
+ unsigned int hpi; /* hpi issued */
+ unsigned int suspend;/* card sleed issued */
+ bool print_stats;
+ unsigned int bkops_level[BKOPS_NUM_OF_SEVERITY_LEVELS];
+ bool ignore_card_bkops_status;
+};
+
+/**
+ * struct mmc_bkops_info - BKOPS data
+ * @dw: Idle time bkops delayed work
+ * @host_suspend_tout_ms: The host controller idle time,
+ * before getting into suspend
+ * @delay_ms: The time to start the BKOPS
+ * delayed work once MMC thread is idle
+ * @poll_for_completion: Poll on BKOPS completion
+ * @cancel_delayed_work: A flag to indicate if the delayed work
+ * should be cancelled
+ * @started_delayed_bkops: A flag to indicate if the delayed
+ * work was scheduled
+ * @sectors_changed: number of sectors written or
+ * discard since the last idle BKOPS were scheduled
+ */
+struct mmc_bkops_info {
+ struct delayed_work dw;
+ unsigned int host_suspend_tout_ms;
+ unsigned int delay_ms;
+ unsigned int min_sectors_to_queue_delayed_work;
+ struct mmc_bkops_stats bkops_stats; /* BKOPS statistics */
+/*
+ * A default time for checking the need for non urgent BKOPS once mmcqd
+ * is idle.
+ */
+#define MMC_IDLE_BKOPS_TIME_MS 2000
+ struct work_struct poll_for_completion;
+/* Polling timeout and interval for waiting on non-blocking BKOPs completion */
+#define BKOPS_COMPLETION_POLLING_TIMEOUT_MS 10000 /* in ms */
+#define BKOPS_COMPLETION_POLLING_INTERVAL_MS 1000 /* in ms */
+ bool cancel_delayed_work;
+ bool started_delayed_bkops;
+ unsigned int sectors_changed;
+/*
+ * Since canceling the delayed work might have significant effect on the
+ * performance of small requests we won't queue the delayed work every time
+ * mmcqd thread is idle.
+ * The delayed work for idle BKOPS will be scheduled only after a significant
+ * amount of write or discard data.
+ * 100MB is chosen based on benchmark tests.
+ */
+#define BKOPS_MIN_SECTORS_TO_QUEUE_DELAYED_WORK 204800 /* 100MB */
+};
+
/*
* MMC device
*/
@@ -248,10 +326,11 @@
#define MMC_QUIRK_LONG_READ_TIME (1<<9) /* Data read time > CSD says */
/* byte mode */
#define MMC_QUIRK_INAND_DATA_TIMEOUT (1<<8) /* For incorrect data timeout */
- unsigned int poweroff_notify_state; /* eMMC4.5 notify
- feature */
+ unsigned int poweroff_notify_state; /* eMMC4.5 notify feature */
#define MMC_NO_POWER_NOTIFICATION 0
#define MMC_POWERED_ON 1
+#define MMC_POWEROFF_SHORT 2
+#define MMC_POWEROFF_LONG 3
unsigned int erase_size; /* erase size in sectors */
unsigned int erase_shift; /* if erase unit is power 2 */
@@ -283,6 +362,9 @@
struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */
unsigned int nr_parts;
+ struct mmc_wr_pack_stats wr_pack_stats; /* packed commands stats*/
+
+ struct mmc_bkops_info bkops_info;
};
/*
@@ -511,5 +593,8 @@
extern void mmc_fixup_device(struct mmc_card *card,
const struct mmc_fixup *table);
+extern struct mmc_wr_pack_stats *mmc_blk_get_packed_statistics(
+ struct mmc_card *card);
+extern void mmc_blk_init_packed_statistics(struct mmc_card *card);
#endif /* LINUX_MMC_CARD_H */
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 3f26a80..7247696 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -149,6 +149,9 @@
extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
struct mmc_command *, int);
extern void mmc_start_bkops(struct mmc_card *card, bool from_exception);
+extern void mmc_start_delayed_bkops(struct mmc_card *card);
+extern void mmc_start_idle_time_bkops(struct work_struct *work);
+extern void mmc_bkops_completion_polling(struct work_struct *work);
extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, bool);
extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
extern int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
@@ -170,7 +173,6 @@
extern int mmc_can_discard(struct mmc_card *card);
extern int mmc_can_sanitize(struct mmc_card *card);
extern int mmc_can_secure_erase_trim(struct mmc_card *card);
-extern int mmc_can_poweroff_notify(const struct mmc_card *card);
extern int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
unsigned int nr);
extern unsigned int mmc_calc_max_discard(struct mmc_card *card);
@@ -194,6 +196,8 @@
extern int mmc_detect_card_removed(struct mmc_host *host);
+extern void mmc_blk_init_bkops_statistics(struct mmc_card *card);
+
/**
* mmc_claim_host - exclusively claim a host
* @host: mmc host to claim
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 714cc76..8f0a756 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -241,15 +241,20 @@
#define MMC_CAP2_BROKEN_VOLTAGE (1 << 7) /* Use the broken voltage */
#define MMC_CAP2_DETECT_ON_ERR (1 << 8) /* On I/O err check card removal */
#define MMC_CAP2_HC_ERASE_SZ (1 << 9) /* High-capacity erase size */
+
#define MMC_CAP2_PACKED_RD (1 << 10) /* Allow packed read */
#define MMC_CAP2_PACKED_WR (1 << 11) /* Allow packed write */
#define MMC_CAP2_PACKED_CMD (MMC_CAP2_PACKED_RD | \
MMC_CAP2_PACKED_WR) /* Allow packed commands */
+#define MMC_CAP2_PACKED_WR_CONTROL (1 << 12) /* Allow write packing control */
+
#define MMC_CAP2_SANITIZE (1 << 13) /* Support Sanitize */
#define MMC_CAP2_INIT_BKOPS (1 << 15) /* Need to set BKOPS_EN */
-#define MMC_CAP2_POWER_OFF_VCCQ_DURING_SUSPEND (1 << 16)
-
mmc_pm_flag_t pm_caps; /* supported pm features */
+ unsigned int power_notify_type;
+#define MMC_HOST_PW_NOTIFY_NONE 0
+#define MMC_HOST_PW_NOTIFY_SHORT 1
+#define MMC_HOST_PW_NOTIFY_LONG 2
int clk_requests; /* internal reference counter */
unsigned int clk_delay; /* number of MCI clk hold cycles */
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 92888c3..237a92e 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -427,11 +427,4 @@
#define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits which are 1 in value */
#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */
-/*
- * MMC Poweroff Notify types
- */
-#define MMC_PW_OFF_NOTIFY_NONE 0
-#define MMC_PW_OFF_NOTIFY_SHORT 1
-#define MMC_PW_OFF_NOTIFY_LONG 2
-
#endif /* LINUX_MMC_MMC_H */
diff --git a/include/linux/msm_audio_acdb.h b/include/linux/msm_audio_acdb.h
index e7f06b5..e907f4a 100644
--- a/include/linux/msm_audio_acdb.h
+++ b/include/linux/msm_audio_acdb.h
@@ -39,7 +39,14 @@
(AUDIO_MAX_COMMON_IOCTL_NUM+16), unsigned)
#define AUDIO_SET_AFE_RX_CAL _IOW(AUDIO_IOCTL_MAGIC, \
(AUDIO_MAX_COMMON_IOCTL_NUM+17), unsigned)
-
+#define AUDIO_SET_VOCPROC_COL_CAL _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+18), unsigned)
+#define AUDIO_SET_VOCSTRM_COL_CAL _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+19), unsigned)
+#define AUDIO_SET_VOCVOL_COL_CAL _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+20), unsigned)
+#define AUDIO_SET_VOCPROC_DEV_CFG_CAL _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+21), unsigned)
#define AUDIO_MAX_ACDB_IOCTL (AUDIO_MAX_COMMON_IOCTL_NUM+30)
diff --git a/include/linux/msm_ion.h b/include/linux/msm_ion.h
index 21000f9..c1ea490 100644
--- a/include/linux/msm_ion.h
+++ b/include/linux/msm_ion.h
@@ -102,6 +102,12 @@
*/
#define ION_IOMMU_UNMAP_DELAYED 1
+/*
+ * This flag allows clients to defer unsecuring a buffer until the buffer
+ * is actually freed.
+ */
+#define ION_UNSECURE_DELAYED 1
+
/**
* struct ion_cp_heap_pdata - defines a content protection heap in the given
* platform
@@ -216,6 +222,26 @@
* Returns 0 on success
*/
int msm_ion_unsecure_heap_2_0(int heap_id, enum cp_mem_usage usage);
+
+/**
+ * msm_ion_secure_buffer - secure an individual buffer
+ *
+ * @client - client who has access to the buffer
+ * @handle - buffer to secure
+ * @usage - usage hint to TZ
+ * @flags - flags for the securing
+ */
+int msm_ion_secure_buffer(struct ion_client *client, struct ion_handle *handle,
+ enum cp_mem_usage usage, int flags);
+
+/**
+ * msm_ion_unsecure_buffer - unsecure an individual buffer
+ *
+ * @client - client who has access to the buffer
+ * @handle - buffer to secure
+ */
+int msm_ion_unsecure_buffer(struct ion_client *client,
+ struct ion_handle *handle);
#else
static inline int msm_ion_secure_heap(int heap_id)
{
diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h
index 71ff639..6912087 100644
--- a/include/linux/msm_kgsl.h
+++ b/include/linux/msm_kgsl.h
@@ -2,7 +2,7 @@
#define _MSM_KGSL_H
#define KGSL_VERSION_MAJOR 3
-#define KGSL_VERSION_MINOR 12
+#define KGSL_VERSION_MINOR 14
/*context flags */
#define KGSL_CONTEXT_SAVE_GMEM 0x00000001
@@ -12,12 +12,47 @@
#define KGSL_CONTEXT_PREAMBLE 0x00000010
#define KGSL_CONTEXT_TRASH_STATE 0x00000020
#define KGSL_CONTEXT_PER_CONTEXT_TS 0x00000040
+#define KGSL_CONTEXT_USER_GENERATED_TS 0x00000080
#define KGSL_CONTEXT_INVALID 0xffffffff
/* Memory allocayion flags */
#define KGSL_MEMFLAGS_GPUREADONLY 0x01000000
+#define KGSL_MEMTYPE_MASK 0x0000FF00
+#define KGSL_MEMTYPE_SHIFT 8
+
+/* Memory types for which allocations are made */
+#define KGSL_MEMTYPE_OBJECTANY 0
+#define KGSL_MEMTYPE_FRAMEBUFFER 1
+#define KGSL_MEMTYPE_RENDERBUFFER 2
+#define KGSL_MEMTYPE_ARRAYBUFFER 3
+#define KGSL_MEMTYPE_ELEMENTARRAYBUFFER 4
+#define KGSL_MEMTYPE_VERTEXARRAYBUFFER 5
+#define KGSL_MEMTYPE_TEXTURE 6
+#define KGSL_MEMTYPE_SURFACE 7
+#define KGSL_MEMTYPE_EGL_SURFACE 8
+#define KGSL_MEMTYPE_GL 9
+#define KGSL_MEMTYPE_CL 10
+#define KGSL_MEMTYPE_CL_BUFFER_MAP 11
+#define KGSL_MEMTYPE_CL_BUFFER_NOMAP 12
+#define KGSL_MEMTYPE_CL_IMAGE_MAP 13
+#define KGSL_MEMTYPE_CL_IMAGE_NOMAP 14
+#define KGSL_MEMTYPE_CL_KERNEL_STACK 15
+#define KGSL_MEMTYPE_COMMAND 16
+#define KGSL_MEMTYPE_2D 17
+#define KGSL_MEMTYPE_EGL_IMAGE 18
+#define KGSL_MEMTYPE_EGL_SHADOW 19
+#define KGSL_MEMTYPE_MULTISAMPLE 20
+#define KGSL_MEMTYPE_KERNEL 255
+
+/*
+ * Alignment hint, passed as the power of 2 exponent.
+ * i.e 4k (2^12) would be 12, 64k (2^16)would be 16.
+ */
+#define KGSL_MEMALIGN_MASK 0x00FF0000
+#define KGSL_MEMALIGN_SHIFT 16
+
/* generic flag values */
#define KGSL_FLAGS_NORMALMODE 0x00000000
#define KGSL_FLAGS_SAFEMODE 0x00000001
@@ -39,6 +74,9 @@
#define KGSL_CLK_MEM_IFACE 0x00000010
#define KGSL_CLK_AXI 0x00000020
+/* Server Side Sync Timeout in milliseconds */
+#define KGSL_SYNCOBJ_SERVER_TIMEOUT 2000
+
/*
* Reset status values for context
*/
@@ -278,8 +316,7 @@
unsigned int offset;
unsigned int hostptr; /*input param */
enum kgsl_user_mem_type memtype;
- unsigned int reserved; /* May be required to add
- params for another mem type */
+ unsigned int flags;
};
#define IOCTL_KGSL_MAP_USER_MEM \
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 647a7ef..46724eb 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -89,6 +89,7 @@
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_CHARGING_ENABLED,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_CYCLE_COUNT,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
@@ -218,14 +219,20 @@
extern int power_supply_set_battery_charged(struct power_supply *psy);
extern int power_supply_set_current_limit(struct power_supply *psy, int limit);
extern int power_supply_set_online(struct power_supply *psy, bool enable);
+extern int power_supply_set_present(struct power_supply *psy, bool enable);
extern int power_supply_set_scope(struct power_supply *psy, int scope);
extern int power_supply_set_charge_type(struct power_supply *psy, int type);
extern int power_supply_set_supply_type(struct power_supply *psy,
enum power_supply_type supply_type);
extern int power_supply_is_system_supplied(void);
+extern int power_supply_register(struct device *parent,
+ struct power_supply *psy);
+extern void power_supply_unregister(struct power_supply *psy);
+extern int power_supply_powers(struct power_supply *psy, struct device *dev);
#else
static inline struct power_supply *power_supply_get_by_name(char *name)
- { return -ENOSYS; }
+ { return NULL; }
+static inline void power_supply_changed(struct power_supply *psy) { }
static inline int power_supply_am_i_supplied(struct power_supply *psy)
{ return -ENOSYS; }
static inline int power_supply_set_battery_charged(struct power_supply *psy)
@@ -236,6 +243,9 @@
static inline int power_supply_set_online(struct power_supply *psy,
bool enable)
{ return -ENOSYS; }
+static inline int power_supply_set_present(struct power_supply *psy,
+ bool enable)
+ { return -ENOSYS; }
static inline int power_supply_set_scope(struct power_supply *psy,
int scope)
{ return -ENOSYS; }
@@ -243,16 +253,18 @@
int type)
{ return -ENOSYS; }
static inline int power_supply_set_supply_type(struct power_supply *psy,
- enum power_supply_type supply_type);
+ enum power_supply_type supply_type)
{ return -ENOSYS; }
static inline int power_supply_is_system_supplied(void) { return -ENOSYS; }
+static inline int power_supply_register(struct device *parent,
+ struct power_supply *psy)
+ { return -ENOSYS; }
+static inline void power_supply_unregister(struct power_supply *psy) { }
+static inline int power_supply_powers(struct power_supply *psy,
+ struct device *dev)
+ { return -ENOSYS; }
#endif
-extern int power_supply_register(struct device *parent,
- struct power_supply *psy);
-extern void power_supply_unregister(struct power_supply *psy);
-extern int power_supply_powers(struct power_supply *psy, struct device *dev);
-
/* For APM emulation, think legacy userspace. */
extern struct class *power_supply_class;
diff --git a/include/linux/regulator/onsemi-ncp6335d.h b/include/linux/regulator/onsemi-ncp6335d.h
new file mode 100644
index 0000000..a57c3b7
--- /dev/null
+++ b/include/linux/regulator/onsemi-ncp6335d.h
@@ -0,0 +1,28 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __NCP6335D_H__
+#define __NCP6335D_H__
+
+enum {
+ NCP6335D_VSEL0,
+ NCP6335D_VSEL1,
+};
+
+struct ncp6335d_platform_data {
+ struct regulator_init_data *init_data;
+ int default_vsel;
+ int slew_rate_ns;
+ int discharge_enable;
+};
+
+#endif
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 2c9509d..1f13da3 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -142,6 +142,8 @@
extern unsigned long nr_iowait_cpu(int cpu);
extern unsigned long this_cpu_load(void);
+extern void sched_update_nr_prod(int cpu, unsigned long nr, bool inc);
+extern void sched_get_nr_running_avg(int *avg, int *iowait_avg);
extern void calc_global_load(unsigned long ticks);
diff --git a/include/linux/tsif_api.h b/include/linux/tsif_api.h
index fc4d20b..0c18228 100644
--- a/include/linux/tsif_api.h
+++ b/include/linux/tsif_api.h
@@ -3,8 +3,7 @@
*
* Kernel API
*
- * Copyright (c) 2009-2010, Code Aurora Forum. All rights
- * reserved.
+ * Copyright (c) 2009-2010, 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -124,11 +123,13 @@
* Should be called prior to any other tsif_XXX function.
*/
void *tsif_attach(int id, void (*notify)(void *client_data), void *client_data);
+
/**
* tsif_detach - detach from device
* @cookie: TSIF cookie previously obtained with tsif_attach()
*/
void tsif_detach(void *cookie);
+
/**
* tsif_get_info - get data buffer info
* @cookie: TSIF cookie previously obtained with tsif_attach()
@@ -140,6 +141,7 @@
* using data; since data buffer will be re-allocated on tsif_start()
*/
void tsif_get_info(void *cookie, void **pdata, int *psize);
+
/**
* tsif_set_mode - set TSIF mode
* @cookie: TSIF cookie previously obtained with tsif_attach()
@@ -150,6 +152,7 @@
* Mode may be changed only when TSIF device is stopped.
*/
int tsif_set_mode(void *cookie, int mode);
+
/**
* tsif_set_time_limit - set TSIF time limit
* @cookie: TSIF cookie previously obtained with tsif_attach()
@@ -160,6 +163,7 @@
* Time limit may be changed only when TSIF device is stopped.
*/
int tsif_set_time_limit(void *cookie, u32 value);
+
/**
* tsif_set_buf_config - configure data buffer
*
@@ -180,6 +184,7 @@
* stats
*/
int tsif_set_buf_config(void *cookie, u32 pkts_in_chunk, u32 chunks_in_buf);
+
/**
* tsif_get_state - query current data buffer information
* @cookie: TSIF cookie previously obtained with tsif_attach()
@@ -188,6 +193,51 @@
* @state: if not NULL, state will be stored here
*/
void tsif_get_state(void *cookie, int *ri, int *wi, enum tsif_state *state);
+
+/**
+ * tsif_set_clk_inverse - set whether to inverse the clock signal.
+ * @cookie: TSIF cookie previously obtained with tsif_attach()
+ * @inverse: 1 to inverse the clock, 0 otherwise. Default is 0.
+ *
+ * Return error code
+ *
+ * Setting may be changed only when TSIF device is stopped.
+ */
+int tsif_set_clk_inverse(void *cookie, int inverse);
+
+/**
+ * tsif_set_data_inverse - set whether to inverse the data signal.
+ * @cookie: TSIF cookie previously obtained with tsif_attach()
+ * @inverse: 1 to inverse the clock, 0 otherwise. Default is 0.
+ *
+ * Return error code
+ *
+ * Setting may be changed only when TSIF device is stopped.
+ */
+int tsif_set_data_inverse(void *cookie, int inverse);
+
+/**
+ * tsif_set_sync_inverse - set whether to inverse the sync signal.
+ * @cookie: TSIF cookie previously obtained with tsif_attach()
+ * @inverse: 1 to inverse the clock, 0 otherwise. Default is 0.
+ *
+ * Return error code
+ *
+ * Setting may be changed only when TSIF device is stopped.
+ */
+int tsif_set_sync_inverse(void *cookie, int inverse);
+
+/**
+ * tsif_set_enable_inverse - set whether to inverse the enable signal.
+ * @cookie: TSIF cookie previously obtained with tsif_attach()
+ * @inverse: 1 to inverse the clock, 0 otherwise. Default is 0.
+ *
+ * Return error code
+ *
+ * Setting may be changed only when TSIF device is stopped.
+ */
+int tsif_set_enable_inverse(void *cookie, int inverse);
+
/**
* tsif_start - start data acquisition
* @cookie: TSIF cookie previously obtained with tsif_attach()
@@ -195,6 +245,7 @@
* Return error code
*/
int tsif_start(void *cookie);
+
/**
* tsif_stop - stop data acquisition
* @cookie: TSIF cookie previously obtained with tsif_attach()
@@ -203,6 +254,7 @@
* query data buffer info using tsif_get_info() and reset its data pointers.
*/
void tsif_stop(void *cookie);
+
/**
* tsif_reclaim_packets - inform that buffer space may be reclaimed
* @cookie: TSIF cookie previously obtained with tsif_attach()
diff --git a/include/linux/usb/audio.h b/include/linux/usb/audio.h
index a54b825..c3ea237 100644
--- a/include/linux/usb/audio.h
+++ b/include/linux/usb/audio.h
@@ -175,6 +175,7 @@
__u8 baInterfaceNr[n]; \
} __attribute__ ((packed))
+DECLARE_UAC_AC_HEADER_DESCRIPTOR(2);
/* 4.3.2.1 Input Terminal Descriptor */
struct uac_input_terminal_descriptor {
__u8 bLength; /* in bytes: 12 */
@@ -454,6 +455,7 @@
__u8 tSamFreq[n][3]; \
} __attribute__ ((packed))
+DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1);
#define UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(n) (8 + (n * 3))
struct uac_format_type_i_ext_descriptor {
diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h
index 5b2dcf9..61ebe0a 100644
--- a/include/linux/usb/f_accessory.h
+++ b/include/linux/usb/f_accessory.h
@@ -36,13 +36,15 @@
#define ACCESSORY_STRING_URI 4
#define ACCESSORY_STRING_SERIAL 5
-/* Control request for retrieving device's protocol version (currently 1)
+/* Control request for retrieving device's protocol version
*
* requestType: USB_DIR_IN | USB_TYPE_VENDOR
* request: ACCESSORY_GET_PROTOCOL
* value: 0
* index: 0
* data version number (16 bits little endian)
+ * 1 for original accessory support
+ * 2 adds HID and device to host audio support
*/
#define ACCESSORY_GET_PROTOCOL 51
@@ -70,6 +72,65 @@
*/
#define ACCESSORY_START 53
+/* Control request for registering a HID device.
+ * Upon registering, a unique ID is sent by the accessory in the
+ * value parameter. This ID will be used for future commands for
+ * the device
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_REGISTER_HID_DEVICE
+ * value: Accessory assigned ID for the HID device
+ * index: total length of the HID report descriptor
+ * data none
+ */
+#define ACCESSORY_REGISTER_HID 54
+
+/* Control request for unregistering a HID device.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_REGISTER_HID
+ * value: Accessory assigned ID for the HID device
+ * index: 0
+ * data none
+ */
+#define ACCESSORY_UNREGISTER_HID 55
+
+/* Control request for sending the HID report descriptor.
+ * If the HID descriptor is longer than the endpoint zero max packet size,
+ * the descriptor will be sent in multiple ACCESSORY_SET_HID_REPORT_DESC
+ * commands. The data for the descriptor must be sent sequentially
+ * if multiple packets are needed.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_SET_HID_REPORT_DESC
+ * value: Accessory assigned ID for the HID device
+ * index: offset of data in descriptor
+ * (needed when HID descriptor is too big for one packet)
+ * data the HID report descriptor
+ */
+#define ACCESSORY_SET_HID_REPORT_DESC 56
+
+/* Control request for sending HID events.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_SEND_HID_EVENT
+ * value: Accessory assigned ID for the HID device
+ * index: 0
+ * data the HID report for the event
+ */
+#define ACCESSORY_SEND_HID_EVENT 57
+
+/* Control request for setting the audio mode.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_SET_AUDIO_MODE
+ * value: 0 - no audio
+ * 1 - device to host, 44100 16-bit stereo PCM
+ * index: 0
+ * data none
+ */
+#define ACCESSORY_SET_AUDIO_MODE 58
+
/* ioctls for retrieving strings set by the host */
#define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256])
#define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256])
@@ -79,5 +140,7 @@
#define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256])
/* returns 1 if there is a start request pending */
#define ACCESSORY_IS_START_REQUESTED _IO('M', 7)
+/* returns audio mode (set via the ACCESSORY_SET_AUDIO_MODE control request) */
+#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8)
#endif /* __LINUX_USB_F_ACCESSORY_H */
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index dd091cd..268aa48 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -351,6 +351,7 @@
void (*dump_regs)(struct usb_hcd *);
void (*enable_ulpi_control)(struct usb_hcd *hcd, u32 linestate);
void (*disable_ulpi_control)(struct usb_hcd *hcd);
+ void (*set_autosuspend_delay)(struct usb_device *);
};
extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index 920cf77..a998ac2 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -25,7 +25,7 @@
#include <linux/wakelock.h>
#include <linux/pm_qos.h>
#include <linux/hrtimer.h>
-
+#include <linux/power_supply.h>
/*
* The following are bit fields describing the usb_request.udc_priv word.
* These bit fields are set by function drivers that wish to queue
@@ -192,9 +192,8 @@
* @mhl_enable: indicates MHL connector or not.
* @disable_reset_on_disconnect: perform USB PHY and LINK reset
* on USB cable disconnection.
- * @enable_dcd: Enable Data Contact Detection circuit. if not set
- * wait for 600msec before proceeding to primary
- * detection.
+ * @pnoc_errata_fix: workaround needed for PNOC hardware bug that
+ * affects USB performance.
* @enable_lpm_on_suspend: Enable the USB core to go into Low
* Power Mode, when USB bus is suspended but cable
* is connected.
@@ -216,7 +215,7 @@
unsigned int mpm_otgsessvld_int;
bool mhl_enable;
bool disable_reset_on_disconnect;
- bool enable_dcd;
+ bool pnoc_errata_fix;
bool enable_lpm_on_dev_suspend;
bool core_clk_always_on_workaround;
struct msm_bus_scale_pdata *bus_scale_table;
@@ -384,6 +383,10 @@
u8 active_tmout;
struct hrtimer timer;
enum usb_vdd_type vdd_type;
+ struct power_supply usb_psy;
+ unsigned int online;
+ unsigned int host_mode;
+ unsigned int current_max;
};
struct msm_hsic_host_platform_data {
diff --git a/include/media/gpio-ir-recv.h b/include/media/gpio-ir-recv.h
index 3eab611..ffdf2f0 100644
--- a/include/media/gpio-ir-recv.h
+++ b/include/media/gpio-ir-recv.h
@@ -17,6 +17,7 @@
unsigned int gpio_nr;
bool active_low;
bool can_wakeup;
+ u32 swfi_latency;
};
#endif /* __GPIO_IR_RECV_H__ */
diff --git a/include/media/vcap_fmt.h b/include/media/vcap_fmt.h
index 2641720..3b1bd7c2 100644
--- a/include/media/vcap_fmt.h
+++ b/include/media/vcap_fmt.h
@@ -13,6 +13,7 @@
#ifndef VCAP_FMT_H
#define VCAP_FMT_H
+#include <linux/videodev2.h>
#define V4L2_BUF_TYPE_INTERLACED_IN_DECODER (V4L2_BUF_TYPE_PRIVATE)
diff --git a/include/media/vcap_v4l2.h b/include/media/vcap_v4l2.h
index b2a538c..6d684ef 100644
--- a/include/media/vcap_v4l2.h
+++ b/include/media/vcap_v4l2.h
@@ -38,9 +38,31 @@
} while (0)
#define VCAP_USEC (1000000)
+
+#define VCAP_STRIDE_ALIGN 0x10
+#define VCAP_STRIDE_CALC(x) (((x / VCAP_STRIDE_ALIGN) + \
+ (!(!(x % VCAP_STRIDE_ALIGN)))) * \
+ VCAP_STRIDE_ALIGN)
+
#define VCAP_BASE (dev->vcapbase)
#define VCAP_OFFSET(off) (VCAP_BASE + off)
+struct reg_range {
+ u32 min_val;
+ u32 max_val;
+};
+
+#define VCAP_REG_RANGE_1_MIN 0x0
+#define VCAP_REG_RANGE_1_MAX 0x48
+#define VCAP_REG_RANGE_2_MIN 0x100
+#define VCAP_REG_RANGE_2_MAX 0x104
+#define VCAP_REG_RANGE_3_MIN 0x400
+#define VCAP_REG_RANGE_3_MAX 0x7F0
+#define VCAP_REG_RANGE_4_MIN 0x800
+#define VCAP_REG_RANGE_4_MAX 0x8A0
+#define VCAP_REG_RANGE_5_MIN 0xC00
+#define VCAP_REG_RANGE_5_MAX 0xDF0
+
#define VCAP_SW_RESET_REQ (VCAP_BASE + 0x024)
#define VCAP_SW_RESET_STATUS (VCAP_BASE + 0x028)
@@ -128,6 +150,16 @@
struct vcap_client_data *cd;
};
+struct vcap_debugfs_params {
+ atomic_t vc_drop_count;
+ uint32_t vc_timestamp;
+ uint32_t vp_timestamp;
+ uint32_t vp_ewma;/* Exponential moving average */
+ uint32_t clk_rate;
+ uint32_t bw_request;
+ uint32_t reg_addr;
+};
+
struct vcap_dev {
struct v4l2_device v4l2_dev;
@@ -152,6 +184,11 @@
uint32_t bus_client_handle;
+ int domain_num;
+ struct device *vc_iommu_ctx;
+ struct device *vp_iommu_ctx;
+ struct iommu_domain *iommu_vcap_domain;
+
struct vcap_client_data *vc_client;
struct vcap_client_data *vp_client;
@@ -176,6 +213,7 @@
struct nr_param nr_param;
bool nr_update;
+ struct vcap_debugfs_params dbg_p;
};
struct vp_format_data {
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 602fe59..14ccf3e 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -267,6 +267,11 @@
#define MGMT_OP_LE_CANCEL_CREATE_CONN_WHITE_LIST 0xE005
+#define MGMT_OP_LE_CANCEL_CREATE_CONN 0xE006
+struct mgmt_cp_le_cancel_create_conn {
+ bdaddr_t bdaddr;
+} __packed;
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
diff --git a/include/net/scm.h b/include/net/scm.h
index d456f4c..0c0017c 100644
--- a/include/net/scm.h
+++ b/include/net/scm.h
@@ -71,9 +71,11 @@
}
static __inline__ int scm_send(struct socket *sock, struct msghdr *msg,
- struct scm_cookie *scm)
+ struct scm_cookie *scm, bool forcecreds)
{
memset(scm, 0, sizeof(*scm));
+ if (forcecreds)
+ scm_set_cred(scm, task_tgid(current), current_cred());
unix_get_peersec_dgram(sock, scm);
if (msg->msg_controllen <= 0)
return 0;
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h
index d902881..07179e9 100644
--- a/include/sound/apr_audio-v2.h
+++ b/include/sound/apr_audio-v2.h
@@ -1627,7 +1627,7 @@
* Supported values: #AFE_API_VERSION_HDMI_CONFIG
*/
-u16 dataype;
+u16 datatype;
/* data type
* Supported values:
* - #LINEAR_PCM_DATA
diff --git a/include/sound/apr_audio.h b/include/sound/apr_audio.h
index 90872c9..8c35ada 100644
--- a/include/sound/apr_audio.h
+++ b/include/sound/apr_audio.h
@@ -1162,6 +1162,7 @@
#define EAC3_DECODER 0x00010C3C
#define DTS 0x00010D88
#define DTS_LBR 0x00010DBB
+#define MP2 0x00010DBE
#define ATRAC 0x00010D89
#define MAT 0x00010D8A
#define G711_ALAW_FS 0x00010BF7
@@ -1170,6 +1171,7 @@
#define MPEG4_MULTI_AAC 0x00010D86
#define US_POINT_EPOS_FORMAT 0x00012310
#define US_RAW_FORMAT 0x0001127C
+#define US_PROX_FORMAT 0x00012721
#define MULTI_CHANNEL_PCM 0x00010C66
#define ASM_ENCDEC_SBCRATE 0x00010C13
diff --git a/include/sound/compress_params.h b/include/sound/compress_params.h
index 54af7d6a..02d69ea 100644
--- a/include/sound/compress_params.h
+++ b/include/sound/compress_params.h
@@ -87,7 +87,7 @@
#define SND_AUDIOCODEC_DTS_LBR ((__u32) 0x00000013)
#define SND_AUDIOCODEC_DTS_TRANSCODE_LOOPBACK ((__u32) 0x00000014)
#define SND_AUDIOCODEC_PASS_THROUGH ((__u32) 0x00000015)
-
+#define SND_AUDIOCODEC_MP2 ((__u32) 0x00000016)
/*
* Profile and modes are listed with bit masks. This allows for a
* more compact representation of fields that will not evolve
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 6cb456e..485e1c5 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -80,6 +80,7 @@
unsigned long offset);
int (*mmap)(struct snd_pcm_substream *substream, struct vm_area_struct *vma);
int (*ack)(struct snd_pcm_substream *substream);
+ int (*restart)(struct snd_pcm_substream *substream);
};
/*
@@ -110,6 +111,12 @@
#define SNDRV_PCM_POS_XRUN ((snd_pcm_uframes_t)-1)
+#define SNDRV_DMA_MODE (0)
+#define SNDRV_NON_DMA_MODE (1 << 0)
+#define SNDRV_RENDER_STOPPED (1 << 1)
+#define SNDRV_RENDER_RUNNING (1 << 2)
+
+
/* If you change this don't forget to change rates[] table in pcm_native.c */
#define SNDRV_PCM_RATE_5512 (1<<0) /* 5512Hz */
#define SNDRV_PCM_RATE_8000 (1<<1) /* 8000Hz */
@@ -299,6 +306,7 @@
unsigned int rate_num;
unsigned int rate_den;
unsigned int no_period_wakeup: 1;
+ unsigned int render_flag;
/* -- SW params -- */
int tstamp_mode; /* mmap timestamp is updated */
diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h
index 2ee5ff7..182da1c 100644
--- a/include/sound/q6asm-v2.h
+++ b/include/sound/q6asm-v2.h
@@ -66,6 +66,7 @@
#define SYNC_IO_MODE 0x0001
#define ASYNC_IO_MODE 0x0002
+#define COMPRESSED_IO 0x0040
#define NT_MODE 0x0400
diff --git a/include/sound/q6asm.h b/include/sound/q6asm.h
index 32d3aef..b0d74ba 100644
--- a/include/sound/q6asm.h
+++ b/include/sound/q6asm.h
@@ -50,6 +50,7 @@
#define FORMAT_AAC 0x0018
#define FORMAT_DTS_LBR 0x0019
#define FORMAT_PASS_THROUGH 0x0020
+#define FORMAT_MP2 0x0021
#define ENCDEC_SBCBITRATE 0x0001
#define ENCDEC_IMMEDIATE_DECODE 0x0002
@@ -284,7 +285,8 @@
uint32_t rate, uint32_t channels);
int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
- uint32_t rate, uint32_t channels);
+ uint32_t rate, uint32_t channels,
+ char *channel_map);
int q6asm_media_format_block_aac(struct audio_client *ac,
struct asm_aac_cfg *cfg);
diff --git a/include/trace/events/mpdcvs_trace.h b/include/trace/events/mpdcvs_trace.h
new file mode 100644
index 0000000..0db1378
--- /dev/null
+++ b/include/trace/events/mpdcvs_trace.h
@@ -0,0 +1,156 @@
+/* Copyright (c) 2012, Free Software 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.
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM mpdcvs_trace
+
+#if !defined(_TRACE_MPDCVS_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_MPDCVS_H
+
+#include <linux/tracepoint.h>
+
+DECLARE_EVENT_CLASS(msm_mp,
+
+ TP_PROTO(const char *name, int mp_val),
+
+ TP_ARGS(name, mp_val),
+
+ TP_STRUCT__entry(
+ __string(name, name)
+ __field(int, mp_val)
+ ),
+
+ TP_fast_assign(
+ __assign_str(name, name);
+ __entry->mp_val = mp_val;
+ ),
+
+ TP_printk("ev_name=%s ev_level=%d",
+ __get_str(name),
+ __entry->mp_val)
+);
+
+/* Core function of run_q */
+
+DEFINE_EVENT(msm_mp, msm_mp_runq,
+
+ TP_PROTO(const char *name, int mp_val),
+
+ TP_ARGS(name, mp_val)
+);
+
+DEFINE_EVENT(msm_mp, msm_mp_cpusonline,
+
+ TP_PROTO(const char *name, int mp_val),
+
+ TP_ARGS(name, mp_val)
+);
+
+DEFINE_EVENT(msm_mp, msm_mp_slacktime,
+
+ TP_PROTO(const char *name, int mp_val),
+
+ TP_ARGS(name, mp_val)
+);
+
+DECLARE_EVENT_CLASS(msm_dcvs,
+
+ TP_PROTO(const char *name, const char *cpuid, int val),
+
+ TP_ARGS(name, cpuid, val),
+
+ TP_STRUCT__entry(
+ __string(name, name)
+ __string(cpuid, cpuid)
+ __field(int, val)
+ ),
+
+ TP_fast_assign(
+ __assign_str(name, name);
+ __assign_str(cpuid, cpuid);
+ __entry->val = val;
+ ),
+
+ TP_printk("ev_name=%s d_name=%s ev_level=%d",
+ __get_str(name),
+ __get_str(cpuid),
+ __entry->val)
+);
+
+/* Core function of dcvs */
+
+DEFINE_EVENT(msm_dcvs, msm_dcvs_idle,
+
+ TP_PROTO(const char *name, const char *cpuid, int val),
+
+ TP_ARGS(name, cpuid, val)
+);
+
+DEFINE_EVENT(msm_dcvs, msm_dcvs_iowait,
+
+ TP_PROTO(const char *name, const char *cpuid, int val),
+
+ TP_ARGS(name, cpuid, val)
+);
+
+DEFINE_EVENT(msm_dcvs, msm_dcvs_slack_time,
+
+ TP_PROTO(const char *name, const char *cpuid, int val),
+
+ TP_ARGS(name, cpuid, val)
+);
+
+DECLARE_EVENT_CLASS(msm_dcvs_scm,
+
+ TP_PROTO(unsigned long cpuid, int ev_type, unsigned long param0,
+ unsigned long param1, unsigned long ret0, unsigned long ret1),
+
+ TP_ARGS(cpuid, ev_type, param0, param1, ret0, ret1),
+
+ TP_STRUCT__entry(
+ __field(unsigned long, cpuid)
+ __field(int, ev_type)
+ __field(unsigned long, param0)
+ __field(unsigned long, param1)
+ __field(unsigned long, ret0)
+ __field(unsigned long, ret1)
+ ),
+
+ TP_fast_assign(
+ __entry->cpuid = cpuid;
+ __entry->ev_type = ev_type;
+ __entry->param0 = param0;
+ __entry->param1 = param1;
+ __entry->ret0 = ret0;
+ __entry->ret1 = ret1;
+ ),
+
+ TP_printk("dev=%lu ev_type=%d ev_param0=%lu ev_param1=%lu ev_ret0=%lu ev_ret1=%lu",
+ __entry->cpuid,
+ __entry->ev_type,
+ __entry->param0,
+ __entry->param1,
+ __entry->ret0,
+ __entry->ret1)
+);
+
+DEFINE_EVENT(msm_dcvs_scm, msm_dcvs_scm_event,
+
+ TP_PROTO(unsigned long cpuid, int ev_type, unsigned long param0,
+ unsigned long param1, unsigned long ret0, unsigned long ret1),
+
+ TP_ARGS(cpuid, ev_type, param0, param1, ret0, ret1)
+);
+
+#endif /* _TRACE_MPDCVS_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/kernel/printk.c b/kernel/printk.c
index 4cf4670..7f51b03 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -1224,13 +1224,13 @@
unsigned long action, void *hcpu)
{
switch (action) {
- case CPU_ONLINE:
case CPU_DEAD:
case CPU_DOWN_FAILED:
case CPU_UP_CANCELED:
console_lock();
console_unlock();
break;
+ case CPU_ONLINE:
case CPU_DYING:
/* invoked with preemption disabled, so defer */
if (!console_trylock())
diff --git a/kernel/rcutree.c b/kernel/rcutree.c
index d0c5baf..4eec66e 100644
--- a/kernel/rcutree.c
+++ b/kernel/rcutree.c
@@ -295,7 +295,9 @@
static int
cpu_needs_another_gp(struct rcu_state *rsp, struct rcu_data *rdp)
{
- return *rdp->nxttail[RCU_DONE_TAIL] && !rcu_gp_in_progress(rsp);
+ return *rdp->nxttail[RCU_DONE_TAIL +
+ ACCESS_ONCE(rsp->completed) != rdp->completed] &&
+ !rcu_gp_in_progress(rsp);
}
/*
diff --git a/kernel/sched/Makefile b/kernel/sched/Makefile
index 9a7dd35..3ede7d9 100644
--- a/kernel/sched/Makefile
+++ b/kernel/sched/Makefile
@@ -11,7 +11,7 @@
CFLAGS_core.o := $(PROFILING) -fno-omit-frame-pointer
endif
-obj-y += core.o clock.o idle_task.o fair.o rt.o stop_task.o
+obj-y += core.o clock.o idle_task.o fair.o rt.o stop_task.o sched_avg.o
obj-$(CONFIG_SMP) += cpupri.o
obj-$(CONFIG_SCHED_AUTOGROUP) += auto_group.o
obj-$(CONFIG_SCHEDSTATS) += stats.o
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index fb3acba..451bd4f 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -916,11 +916,13 @@
static inline void inc_nr_running(struct rq *rq)
{
+ sched_update_nr_prod(cpu_of(rq), rq->nr_running, true);
rq->nr_running++;
}
static inline void dec_nr_running(struct rq *rq)
{
+ sched_update_nr_prod(cpu_of(rq), rq->nr_running, false);
rq->nr_running--;
}
diff --git a/kernel/sched/sched_avg.c b/kernel/sched/sched_avg.c
new file mode 100644
index 0000000..8eaf2f7
--- /dev/null
+++ b/kernel/sched/sched_avg.c
@@ -0,0 +1,106 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Scheduler hook for average runqueue determination
+ */
+#include <linux/module.h>
+#include <linux/percpu.h>
+#include <linux/hrtimer.h>
+#include <linux/sched.h>
+#include <linux/math64.h>
+
+static DEFINE_PER_CPU(u64, nr_prod_sum);
+static DEFINE_PER_CPU(u64, last_time);
+static DEFINE_PER_CPU(u64, nr);
+static DEFINE_PER_CPU(unsigned long, iowait_prod_sum);
+static DEFINE_PER_CPU(spinlock_t, nr_lock) = __SPIN_LOCK_UNLOCKED(nr_lock);
+static s64 last_get_time;
+
+/**
+ * sched_get_nr_running_avg
+ * @return: Average nr_running and iowait value since last poll.
+ * Returns the avg * 100 to return up to two decimal points
+ * of accuracy.
+ *
+ * Obtains the average nr_running value since the last poll.
+ * This function may not be called concurrently with itself
+ */
+void sched_get_nr_running_avg(int *avg, int *iowait_avg)
+{
+ int cpu;
+ u64 curr_time = sched_clock();
+ u64 diff = curr_time - last_get_time;
+ u64 tmp_avg = 0, tmp_iowait = 0;
+
+ *avg = 0;
+ *iowait_avg = 0;
+
+ if (!diff)
+ return;
+
+ last_get_time = curr_time;
+ /* read and reset nr_running counts */
+ for_each_possible_cpu(cpu) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&per_cpu(nr_lock, cpu), flags);
+ tmp_avg += per_cpu(nr_prod_sum, cpu);
+ tmp_avg += per_cpu(nr, cpu) *
+ (curr_time - per_cpu(last_time, cpu));
+ tmp_iowait = per_cpu(iowait_prod_sum, cpu);
+ tmp_iowait += nr_iowait_cpu(cpu) *
+ (curr_time - per_cpu(last_time, cpu));
+ per_cpu(last_time, cpu) = curr_time;
+ per_cpu(nr_prod_sum, cpu) = 0;
+ per_cpu(iowait_prod_sum, cpu) = 0;
+ spin_unlock_irqrestore(&per_cpu(nr_lock, cpu), flags);
+ }
+
+ *avg = (int)div64_u64(tmp_avg * 100, diff);
+ *iowait_avg = (int)div64_u64(tmp_iowait * 100, diff);
+
+ BUG_ON(*avg < 0);
+ pr_debug("%s - avg:%d\n", __func__, *avg);
+ BUG_ON(*iowait_avg < 0);
+ pr_debug("%s - avg:%d\n", __func__, *iowait_avg);
+}
+EXPORT_SYMBOL(sched_get_nr_running_avg);
+
+/**
+ * sched_update_nr_prod
+ * @cpu: The core id of the nr running driver.
+ * @nr: Updated nr running value for cpu.
+ * @inc: Whether we are increasing or decreasing the count
+ * @return: N/A
+ *
+ * Update average with latest nr_running value for CPU
+ */
+void sched_update_nr_prod(int cpu, unsigned long nr_running, bool inc)
+{
+ int diff;
+ s64 curr_time;
+ unsigned long flags;
+
+ spin_lock_irqsave(&per_cpu(nr_lock, cpu), flags);
+ curr_time = sched_clock();
+ diff = curr_time - per_cpu(last_time, cpu);
+ per_cpu(last_time, cpu) = curr_time;
+ per_cpu(nr, cpu) = nr_running + (inc ? 1 : -1);
+
+ BUG_ON(per_cpu(nr, cpu) < 0);
+
+ per_cpu(nr_prod_sum, cpu) += nr_running * diff;
+ per_cpu(iowait_prod_sum, cpu) += nr_iowait_cpu(cpu) * diff;
+ spin_unlock_irqrestore(&per_cpu(nr_lock, cpu), flags);
+}
+EXPORT_SYMBOL(sched_update_nr_prod);
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index d7deaaf..28eb7ea 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -2073,6 +2073,40 @@
return err;
}
+static int le_cancel_create_conn(struct sock *sk, u16 index,
+ unsigned char *data, u16 len)
+{
+ struct mgmt_cp_le_cancel_create_conn *cp = (void *) data;
+ struct hci_dev *hdev;
+ int err = 0;
+
+ if (len != sizeof(*cp))
+ return cmd_status(sk, index, MGMT_OP_LE_CANCEL_CREATE_CONN,
+ EINVAL);
+
+ hdev = hci_dev_get(index);
+
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_LE_CANCEL_CREATE_CONN,
+ ENODEV);
+
+ hci_dev_lock_bh(hdev);
+
+ if (!test_bit(HCI_UP, &hdev->flags)) {
+ err = cmd_status(sk, index, MGMT_OP_LE_CANCEL_CREATE_CONN,
+ ENETDOWN);
+ goto failed;
+ }
+
+ hci_le_cancel_create_connect(hdev, &cp->bdaddr);
+
+failed:
+ hci_dev_unlock_bh(hdev);
+ hci_dev_put(hdev);
+
+return err;
+}
+
static int set_local_name(struct sock *sk, u16 index, unsigned char *data,
u16 len)
{
@@ -2664,6 +2698,9 @@
case MGMT_OP_LE_CANCEL_CREATE_CONN_WHITE_LIST:
err = le_cancel_create_conn_white_list(sk, index);
break;
+ case MGMT_OP_LE_CANCEL_CREATE_CONN:
+ err = le_cancel_create_conn(sk, index, buf + sizeof(*hdr), len);
+ break;
default:
BT_DBG("Unknown op %u", opcode);
err = cmd_status(sk, index, opcode, 0x01);
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index faa48f7..59debb7 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1329,7 +1329,7 @@
if (NULL == siocb->scm)
siocb->scm = &scm;
- err = scm_send(sock, msg, siocb->scm);
+ err = scm_send(sock, msg, siocb->scm, true);
if (err < 0)
return err;
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index d510353..109e30b 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1446,7 +1446,7 @@
if (NULL == siocb->scm)
siocb->scm = &tmp_scm;
wait_for_unix_gc();
- err = scm_send(sock, msg, siocb->scm);
+ err = scm_send(sock, msg, siocb->scm, false);
if (err < 0)
return err;
@@ -1607,7 +1607,7 @@
if (NULL == siocb->scm)
siocb->scm = &tmp_scm;
wait_for_unix_gc();
- err = scm_send(sock, msg, siocb->scm);
+ err = scm_send(sock, msg, siocb->scm, false);
if (err < 0)
return err;
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 4c655c2..d2f60a0 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -1629,6 +1629,8 @@
$exec_file =~ /^.+\.ihex$/ or
$exec_file =~ /^.+\.hex$/ or
$exec_file =~ /^.+\.HEX$/ or
+ $exec_file =~ /^.+\.dts$/ or
+ $exec_file =~ /^.+\.dtsi$/ or
$exec_file =~ /^.+defconfig$/ or
$exec_file =~ /^Makefile$/ or
$exec_file =~ /^Kconfig$/) &&
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index b11118f..4636247 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -2477,6 +2477,7 @@
volatile struct snd_pcm_mmap_status *status;
volatile struct snd_pcm_mmap_control *control;
int err;
+ snd_pcm_uframes_t hw_avail;
memset(&sync_ptr, 0, sizeof(sync_ptr));
if (get_user(sync_ptr.flags, (unsigned __user *)&(_sync_ptr->flags)))
@@ -2499,6 +2500,16 @@
control->avail_min = sync_ptr.c.control.avail_min;
else
sync_ptr.c.control.avail_min = control->avail_min;
+
+ if (runtime->render_flag & SNDRV_NON_DMA_MODE) {
+ hw_avail = snd_pcm_playback_hw_avail(runtime);
+ if ((hw_avail >= runtime->start_threshold)
+ && (runtime->render_flag &
+ SNDRV_RENDER_STOPPED)) {
+ if (substream->ops->restart)
+ substream->ops->restart(substream);
+ }
+ }
sync_ptr.s.status.state = status->state;
sync_ptr.s.status.hw_ptr = status->hw_ptr;
sync_ptr.s.status.tstamp = status->tstamp;
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 5aabfee..1c9d86b 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -51,7 +51,7 @@
snd-soc-wcd9304-objs := wcd9304.o wcd9304-tables.o
snd-soc-wcd9310-objs := wcd9310.o wcd9310-tables.o
snd-soc-cs8427-objs := cs8427.o
-snd-soc-wcd9320-objs := wcd9320.o wcd9320-tables.o
+snd-soc-wcd9320-objs := wcd9xxx-resmgr.o wcd9320.o wcd9320-tables.o wcd9xxx-mbhc.o
snd-soc-wl1273-objs := wl1273.o
snd-soc-wm1250-ev1-objs := wm1250-ev1.o
snd-soc-wm2000-objs := wm2000.o
diff --git a/sound/soc/codecs/wcd9304-tables.c b/sound/soc/codecs/wcd9304-tables.c
index f0d76e8..ba0b6b6 100644
--- a/sound/soc/codecs/wcd9304-tables.c
+++ b/sound/soc/codecs/wcd9304-tables.c
@@ -419,6 +419,10 @@
[SITAR_A_QFUSE_DATA_OUT1] = 1,
[SITAR_A_QFUSE_DATA_OUT2] = 1,
[SITAR_A_QFUSE_DATA_OUT3] = 1,
+ [SITAR_A_QFUSE_DATA_OUT4] = 1,
+ [SITAR_A_QFUSE_DATA_OUT5] = 1,
+ [SITAR_A_QFUSE_DATA_OUT6] = 1,
+ [SITAR_A_QFUSE_DATA_OUT7] = 1,
[SITAR_A_CDC_CTL] = 1,
[SITAR_A_LEAKAGE_CTL] = 1,
[SITAR_A_INTR_MODE] = 1,
@@ -428,6 +432,9 @@
[SITAR_A_INTR_STATUS0] = 1,
[SITAR_A_INTR_STATUS1] = 1,
[SITAR_A_INTR_STATUS2] = 1,
+ [SITAR_A_INTR_CLEAR0] = 1,
+ [SITAR_A_INTR_CLEAR1] = 1,
+ [SITAR_A_INTR_CLEAR2] = 1,
[SITAR_A_INTR_LEVEL0] = 1,
[SITAR_A_INTR_LEVEL1] = 1,
[SITAR_A_INTR_LEVEL2] = 1,
@@ -593,24 +600,76 @@
[SITAR_A_CDC_ANC1_SPARE] = 1,
[SITAR_A_CDC_ANC1_SMLPF_CTL] = 1,
[SITAR_A_CDC_ANC1_DCFLT_CTL] = 1,
+ [SITAR_A_CDC_ANC2_CTL] = 1,
+ [SITAR_A_CDC_ANC2_SHIFT] = 1,
+ [SITAR_A_CDC_ANC2_IIR_B1_CTL] = 1,
+ [SITAR_A_CDC_ANC2_IIR_B2_CTL] = 1,
+ [SITAR_A_CDC_ANC2_IIR_B3_CTL] = 1,
+ [SITAR_A_CDC_ANC2_IIR_B4_CTL] = 1,
+ [SITAR_A_CDC_ANC2_LPF_B1_CTL] = 1,
+ [SITAR_A_CDC_ANC2_LPF_B2_CTL] = 1,
+ [SITAR_A_CDC_ANC2_LPF_B3_CTL] = 1,
+ [SITAR_A_CDC_ANC2_SPARE] = 1,
+ [SITAR_A_CDC_ANC2_SMLPF_CTL] = 1,
+ [SITAR_A_CDC_ANC2_DCFLT_CTL] = 1,
[SITAR_A_CDC_TX1_VOL_CTL_TIMER] = 1,
[SITAR_A_CDC_TX1_VOL_CTL_GAIN] = 1,
[SITAR_A_CDC_TX1_VOL_CTL_CFG] = 1,
[SITAR_A_CDC_TX1_MUX_CTL] = 1,
[SITAR_A_CDC_TX1_CLK_FS_CTL] = 1,
[SITAR_A_CDC_TX1_DMIC_CTL] = 1,
+ [SITAR_A_CDC_TX2_VOL_CTL_TIMER] = 1,
+ [SITAR_A_CDC_TX2_VOL_CTL_GAIN] = 1,
+ [SITAR_A_CDC_TX2_VOL_CTL_CFG] = 1,
+ [SITAR_A_CDC_TX2_MUX_CTL] = 1,
+ [SITAR_A_CDC_TX2_CLK_FS_CTL] = 1,
+ [SITAR_A_CDC_TX2_DMIC_CTL] = 1,
+ [SITAR_A_CDC_TX3_VOL_CTL_TIMER] = 1,
+ [SITAR_A_CDC_TX3_VOL_CTL_GAIN] = 1,
+ [SITAR_A_CDC_TX3_VOL_CTL_CFG] = 1,
+ [SITAR_A_CDC_TX3_MUX_CTL] = 1,
+ [SITAR_A_CDC_TX3_CLK_FS_CTL] = 1,
+ [SITAR_A_CDC_TX3_DMIC_CTL] = 1,
+ [SITAR_A_CDC_TX4_VOL_CTL_TIMER] = 1,
+ [SITAR_A_CDC_TX4_VOL_CTL_GAIN] = 1,
+ [SITAR_A_CDC_TX4_VOL_CTL_CFG] = 1,
+ [SITAR_A_CDC_TX4_MUX_CTL] = 1,
+ [SITAR_A_CDC_TX4_CLK_FS_CTL] = 1,
+ [SITAR_A_CDC_TX4_DMIC_CTL] = 1,
+ [SITAR_A_CDC_TX5_VOL_CTL_TIMER] = 1,
+ [SITAR_A_CDC_TX5_VOL_CTL_GAIN] = 1,
+ [SITAR_A_CDC_TX5_VOL_CTL_CFG] = 1,
+ [SITAR_A_CDC_TX5_MUX_CTL] = 1,
+ [SITAR_A_CDC_TX5_CLK_FS_CTL] = 1,
+ [SITAR_A_CDC_TX5_DMIC_CTL] = 1,
[SITAR_A_CDC_SRC1_PDA_CFG] = 1,
[SITAR_A_CDC_SRC1_FS_CTL] = 1,
+ [SITAR_A_CDC_SRC2_PDA_CFG] = 1,
+ [SITAR_A_CDC_SRC2_FS_CTL] = 1,
[SITAR_A_CDC_RX1_B1_CTL] = 1,
[SITAR_A_CDC_RX1_B2_CTL] = 1,
[SITAR_A_CDC_RX1_B3_CTL] = 1,
[SITAR_A_CDC_RX1_B4_CTL] = 1,
[SITAR_A_CDC_RX1_B5_CTL] = 1,
- [SITAR_A_CDC_RX2_B5_CTL] = 1,
- [SITAR_A_CDC_RX3_B5_CTL] = 1,
[SITAR_A_CDC_RX1_B6_CTL] = 1,
[SITAR_A_CDC_RX1_VOL_CTL_B1_CTL] = 1,
[SITAR_A_CDC_RX1_VOL_CTL_B2_CTL] = 1,
+ [SITAR_A_CDC_RX2_B1_CTL] = 1,
+ [SITAR_A_CDC_RX2_B2_CTL] = 1,
+ [SITAR_A_CDC_RX2_B3_CTL] = 1,
+ [SITAR_A_CDC_RX2_B4_CTL] = 1,
+ [SITAR_A_CDC_RX2_B5_CTL] = 1,
+ [SITAR_A_CDC_RX2_B6_CTL] = 1,
+ [SITAR_A_CDC_RX2_VOL_CTL_B1_CTL] = 1,
+ [SITAR_A_CDC_RX2_VOL_CTL_B2_CTL] = 1,
+ [SITAR_A_CDC_RX3_B1_CTL] = 1,
+ [SITAR_A_CDC_RX3_B2_CTL] = 1,
+ [SITAR_A_CDC_RX3_B3_CTL] = 1,
+ [SITAR_A_CDC_RX3_B4_CTL] = 1,
+ [SITAR_A_CDC_RX3_B5_CTL] = 1,
+ [SITAR_A_CDC_RX3_B6_CTL] = 1,
+ [SITAR_A_CDC_RX3_VOL_CTL_B1_CTL] = 1,
+ [SITAR_A_CDC_RX3_VOL_CTL_B2_CTL] = 1,
[SITAR_A_CDC_CLK_ANC_RESET_CTL] = 1,
[SITAR_A_CDC_CLK_RX_RESET_CTL] = 1,
[SITAR_A_CDC_CLK_TX_RESET_B1_CTL] = 1,
@@ -652,6 +711,21 @@
[SITAR_A_CDC_IIR1_COEF_B3_CTL] = 1,
[SITAR_A_CDC_IIR1_COEF_B4_CTL] = 1,
[SITAR_A_CDC_IIR1_COEF_B5_CTL] = 1,
+ [SITAR_A_CDC_IIR2_GAIN_B1_CTL] = 1,
+ [SITAR_A_CDC_IIR2_GAIN_B2_CTL] = 1,
+ [SITAR_A_CDC_IIR2_GAIN_B3_CTL] = 1,
+ [SITAR_A_CDC_IIR2_GAIN_B4_CTL] = 1,
+ [SITAR_A_CDC_IIR2_GAIN_B5_CTL] = 1,
+ [SITAR_A_CDC_IIR2_GAIN_B6_CTL] = 1,
+ [SITAR_A_CDC_IIR2_GAIN_B7_CTL] = 1,
+ [SITAR_A_CDC_IIR2_GAIN_B8_CTL] = 1,
+ [SITAR_A_CDC_IIR2_CTL] = 1,
+ [SITAR_A_CDC_IIR2_GAIN_TIMER_CTL] = 1,
+ [SITAR_A_CDC_IIR2_COEF_B1_CTL] = 1,
+ [SITAR_A_CDC_IIR2_COEF_B2_CTL] = 1,
+ [SITAR_A_CDC_IIR2_COEF_B3_CTL] = 1,
+ [SITAR_A_CDC_IIR2_COEF_B4_CTL] = 1,
+ [SITAR_A_CDC_IIR2_COEF_B5_CTL] = 1,
[SITAR_A_CDC_TOP_GAIN_UPDATE] = 1,
[SITAR_A_CDC_TOP_RDAC_DOUT_CTL] = 1,
[SITAR_A_CDC_DEBUG_B1_CTL] = 1,
@@ -669,6 +743,14 @@
[SITAR_A_CDC_COMP1_B6_CTL] = 1,
[SITAR_A_CDC_COMP1_SHUT_DOWN_STATUS] = 1,
[SITAR_A_CDC_COMP1_FS_CFG] = 1,
+ [SITAR_A_CDC_COMP2_B1_CTL] = 1,
+ [SITAR_A_CDC_COMP2_B2_CTL] = 1,
+ [SITAR_A_CDC_COMP2_B3_CTL] = 1,
+ [SITAR_A_CDC_COMP2_B4_CTL] = 1,
+ [SITAR_A_CDC_COMP2_B5_CTL] = 1,
+ [SITAR_A_CDC_COMP2_B6_CTL] = 1,
+ [SITAR_A_CDC_COMP2_SHUT_DOWN_STATUS] = 1,
+ [SITAR_A_CDC_COMP2_FS_CFG] = 1,
[SITAR_A_CDC_CONN_RX1_B1_CTL] = 1,
[SITAR_A_CDC_CONN_RX1_B2_CTL] = 1,
[SITAR_A_CDC_CONN_RX1_B3_CTL] = 1,
diff --git a/sound/soc/codecs/wcd9304.c b/sound/soc/codecs/wcd9304.c
index 9432a06..9f02134 100644
--- a/sound/soc/codecs/wcd9304.c
+++ b/sound/soc/codecs/wcd9304.c
@@ -41,34 +41,46 @@
#define ADC_DMIC_SEL_ADC 0
#define ADC_DMIC_SEL_DMIC 1
+#define NUM_AMIC 3
#define NUM_DECIMATORS 4
#define NUM_INTERPOLATORS 3
#define BITS_PER_REG 8
+
+enum {
+ AIF1_PB = 0,
+ AIF1_CAP,
+ NUM_CODEC_DAIS,
+};
+
+struct wcd9xxx_ch sitar_rx_chs[SITAR_RX_MAX] = {
+ WCD9XXX_CH(10, 0),
+ WCD9XXX_CH(11, 1),
+ WCD9XXX_CH(12, 2),
+ WCD9XXX_CH(13, 3),
+ WCD9XXX_CH(14, 4)
+};
+
+struct wcd9xxx_ch sitar_tx_chs[SITAR_TX_MAX] = {
+ WCD9XXX_CH(0, 0),
+ WCD9XXX_CH(1, 1),
+ WCD9XXX_CH(2, 2),
+ WCD9XXX_CH(3, 3),
+ WCD9XXX_CH(4, 4),
+};
+
#define SITAR_CFILT_FAST_MODE 0x00
#define SITAR_CFILT_SLOW_MODE 0x40
#define MBHC_FW_READ_ATTEMPTS 15
#define MBHC_FW_READ_TIMEOUT 2000000
+#define SLIM_CLOSE_TIMEOUT 1000
+
#define SITAR_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | SND_JACK_OC_HPHR)
#define SITAR_I2S_MASTER_MODE_MASK 0x08
#define SITAR_OCP_ATTEMPT 1
-#define AIF1_PB 1
-#define AIF1_CAP 2
-#define NUM_CODEC_DAIS 2
-#define SLIM_CLOSE_TIMEOUT 1000
-
-struct sitar_codec_dai_data {
- u32 rate;
- u32 *ch_num;
- u32 ch_act;
- u32 ch_tot;
- u32 ch_mask;
- wait_queue_head_t dai_wait;
-};
-
#define SITAR_MCLK_RATE_12288KHZ 12288000
#define SITAR_MCLK_RATE_9600KHZ 9600000
@@ -177,6 +189,11 @@
MBHC_STATE_RELEASE,
};
+static const u32 vport_check_table[NUM_CODEC_DAIS] = {
+ 0, /* AIF1_PB */
+ 0, /* AIF1_CAP */
+};
+
struct sitar_priv {
struct snd_soc_codec *codec;
u32 mclk_freq;
@@ -239,7 +256,7 @@
const struct firmware *mbhc_fw;
/* num of slim ports required */
- struct sitar_codec_dai_data dai[NUM_CODEC_DAIS];
+ struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS];
/* Currently, only used for mbhc purpose, to protect
* concurrent execution of mbhc threaded irq handlers and
@@ -934,6 +951,201 @@
SOC_DAPM_SINGLE("Switch", SITAR_A_RX_EAR_EN, 5, 1, 0),
};
+static int slim_tx_vport_validation(u32 dai_id, u32 port_id,
+ struct sitar_priv *sitar_p)
+{
+ struct wcd9xxx_ch *ch;
+ int ret = 0;
+ int index = 0;
+ u32 vtable = vport_check_table[dai_id];
+ pr_debug("%s: dai_id %u vtable 0x%x port_id %u\n", __func__,
+ dai_id, vtable, port_id);
+ while (vtable) {
+ if (vtable & 1) {
+ list_for_each_entry(ch,
+ &sitar_p->dai[index].wcd9xxx_ch_list,
+ list) {
+ pr_debug("%s: index %u ch->port%u vtable 0x%x\n",
+ __func__, index, ch->port, vtable);
+ if (ch->port == port_id) {
+ pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n",
+ __func__, port_id + 1,
+ (index + 1)/2);
+ ret = -EINVAL;
+ break;
+ }
+ }
+ }
+ if (ret)
+ break;
+ index++;
+ vtable = vtable >> 1;
+ }
+ return ret;
+}
+
+static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+ ucontrol->value.integer.value[0] = widget->value;
+ return 0;
+}
+
+static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
+ struct sitar_priv *sitar_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+ struct soc_multi_mixer_control *mixer =
+ ((struct soc_multi_mixer_control *)kcontrol->private_value);
+ u32 dai_id = widget->shift;
+ u32 port_id = mixer->shift;
+ u32 enable = ucontrol->value.integer.value[0];
+
+ mutex_lock(&codec->mutex);
+
+ if (sitar_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ if (dai_id != AIF1_CAP) {
+ dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+ __func__);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+ }
+
+ switch (dai_id) {
+ case AIF1_CAP:
+ if (enable && !(widget->value & 1 << port_id)) {
+ if (slim_tx_vport_validation(dai_id,
+ port_id, sitar_p)) {
+ pr_info("%s: TX%u is used by other virtual port\n",
+ __func__, port_id + 1);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+ widget->value |= 1 << port_id;
+ list_add_tail(&core->tx_chs[port_id].list,
+ &sitar_p->dai[dai_id].wcd9xxx_ch_list);
+ } else if (!enable && (widget->value & 1 << port_id)) {
+ widget->value &= ~(1<<port_id);
+ list_del_init(&core->tx_chs[port_id].list);
+ } else {
+ if (enable)
+ pr_info("%s: TX%u port is used by this virtual port\n",
+ __func__, port_id + 1);
+ else
+ pr_info("%s: TX%u port is not used by this virtual port\n",
+ __func__, port_id + 1);
+ /* avoid update power function */
+ mutex_unlock(&codec->mutex);
+ return 0;
+ }
+ break;
+ default:
+ pr_err("Unknown AIF %d\n", dai_id);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: name %s sname %s updated value %u shift %d\n", __func__,
+ widget->name, widget->sname, widget->value, widget->shift);
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, enable);
+ mutex_unlock(&codec->mutex);
+ return 0;
+}
+
+static int slim_rx_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+ ucontrol->value.enumerated.item[0] = widget->value;
+ return 0;
+}
+
+static const char * const slim_rx_mux_text[] = {
+ "ZERO", "AIF1_PB"
+};
+
+static int slim_rx_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
+ struct sitar_priv *sitar_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ u32 port_id = widget->shift;
+
+ widget->value = ucontrol->value.enumerated.item[0];
+
+ mutex_lock(&codec->mutex);
+
+ if (sitar_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ if (widget->value > 1) {
+ dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+ __func__);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+ }
+
+ switch (widget->value) {
+ case 0:
+ list_del_init(&core->rx_chs[port_id].list);
+ break;
+ case 1:
+ list_add_tail(&core->rx_chs[port_id].list,
+ &sitar_p->dai[AIF1_PB].wcd9xxx_ch_list);
+ break;
+ default:
+ pr_err("Unknown AIF %d\n", widget->value);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+ snd_soc_dapm_mux_update_power(widget, kcontrol, 1, widget->value, e);
+ mutex_unlock(&codec->mutex);
+ return 0;
+}
+
+static const struct soc_enum slim_rx_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text);
+
+static const struct snd_kcontrol_new sitar_aif_pb_mux[SITAR_RX_MAX] = {
+ SOC_DAPM_ENUM_EXT("SLIM RX1 MUX", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX2 MUX", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX3 MUX", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX4 MUX", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX5 MUX", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put)
+};
+
+static const struct snd_kcontrol_new sitar_aif_cap_mixer[SITAR_TX_MAX] = {
+ SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, SITAR_TX1, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, SITAR_TX2, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, SITAR_TX3, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, SITAR_TX4, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, SITAR_TX5, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+
static void sitar_codec_enable_adc_block(struct snd_soc_codec *codec,
int enable)
{
@@ -1712,7 +1924,7 @@
/* reset retry counter as PA is turned off signifying
* start of new OCP detection session
*/
- if (SITAR_IRQ_HPH_PA_OCPL_FAULT)
+ if (WCD9XXX_IRQ_HPH_PA_OCPL_FAULT)
sitar->hphlocp_cnt = 0;
else
sitar->hphrocp_cnt = 0;
@@ -1724,14 +1936,16 @@
{
struct sitar_priv *sitar = container_of(work, struct sitar_priv,
hphlocp_work);
- hphocp_off_report(sitar, SND_JACK_OC_HPHL, SITAR_IRQ_HPH_PA_OCPL_FAULT);
+ hphocp_off_report(sitar, SND_JACK_OC_HPHL,
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
}
static void hphrocp_off_report(struct work_struct *work)
{
struct sitar_priv *sitar = container_of(work, struct sitar_priv,
hphrocp_work);
- hphocp_off_report(sitar, SND_JACK_OC_HPHR, SITAR_IRQ_HPH_PA_OCPR_FAULT);
+ hphocp_off_report(sitar, SND_JACK_OC_HPHR,
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
}
static int sitar_hph_pa_event(struct snd_soc_dapm_widget *w,
@@ -1877,15 +2091,27 @@
SND_SOC_DAPM_MIXER("DAC1", SITAR_A_RX_EAR_EN, 6, 0, dac1_switch,
ARRAY_SIZE(dac1_switch)),
SND_SOC_DAPM_SUPPLY("EAR DRIVER", SITAR_A_RX_EAR_EN, 3, 0, NULL, 0),
- SND_SOC_DAPM_AIF_IN_E("SLIM RX1", "AIF1 Playback", 0, SND_SOC_NOPM, 0,
- 0, sitar_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_AIF_IN_E("SLIM RX2", "AIF1 Playback", 0, SND_SOC_NOPM, 0,
- 0, sitar_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_AIF_IN("SLIM RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_AIF_IN("SLIM RX4", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_AIF_IN("SLIM RX5", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
+ AIF1_PB, 0, sitar_codec_enable_slimrx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, SITAR_RX1, 0,
+ &sitar_aif_pb_mux[SITAR_RX1]),
+ SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, SITAR_RX2, 0,
+ &sitar_aif_pb_mux[SITAR_RX2]),
+ SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, SITAR_RX3, 0,
+ &sitar_aif_pb_mux[SITAR_RX3]),
+ SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, SITAR_RX4, 0,
+ &sitar_aif_pb_mux[SITAR_RX4]),
+ SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, SITAR_RX5, 0,
+ &sitar_aif_pb_mux[SITAR_RX5]),
+
+ SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0),
/* Headphone */
SND_SOC_DAPM_OUTPUT("HEADPHONE"),
@@ -2023,26 +2249,23 @@
SND_SOC_DAPM_MUX("ANC1 FB MUX", SND_SOC_NOPM, 0, 0, &anc1_fb_mux),
- SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, 0, 0, &sb_tx1_mux),
- SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, 0, 0, &sb_tx2_mux),
- SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, 0, 0, &sb_tx3_mux),
- SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, 0, 0, &sb_tx4_mux),
- SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, 0, 0, &sb_tx5_mux),
-
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX1", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
- 0, sitar_codec_enable_slimtx,
+ SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM,
+ AIF1_CAP, 0, sitar_codec_enable_slimtx,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX2", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
- 0, sitar_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0,
+ sitar_aif_cap_mixer, ARRAY_SIZE(sitar_aif_cap_mixer)),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX3", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
- 0, sitar_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX4", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
- 0, sitar_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, SITAR_TX1, 0,
+ &sb_tx1_mux),
+ SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, SITAR_TX2, 0,
+ &sb_tx2_mux),
+ SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, SITAR_TX3, 0,
+ &sb_tx3_mux),
+ SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, SITAR_TX3, 0,
+ &sb_tx4_mux),
+ SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, SITAR_TX3, 0,
+ &sb_tx5_mux),
SND_SOC_DAPM_AIF_OUT_E("SLIM TX5", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
0, sitar_codec_enable_slimtx,
@@ -2083,6 +2306,18 @@
{"SLIM TX3", NULL, "TX_I2S_CLK"},
{"SLIM TX4", NULL, "TX_I2S_CLK"},
};
+#define SLIM_MIXER(x) (\
+ {x, "SLIM TX1", "SLIM TX1 MUX"}, \
+ {x, "SLIM TX2", "SLIM TX2 MUX"}, \
+ {x, "SLIM TX3", "SLIM TX3 MUX"}, \
+ {x, "SLIM TX4", "SLIM TX4 MUX"})
+
+
+#define SLIM_MUX(x, y) (\
+ {"SLIM RX1 MUX", x, y}, \
+ {"SLIM RX2 MUX", x, y}, \
+ {"SLIM RX3 MUX", x, y}, \
+ {"SLIM RX4 MUX", x, y})
static const struct snd_soc_dapm_route audio_map[] = {
/* Earpiece (RX MIX1) */
@@ -2161,6 +2396,23 @@
{"RX3 MIX1", NULL, "ANC"},
/* SLIMBUS Connections */
+ {"AIF1 CAP", NULL, "AIF1_CAP Mixer"},
+
+ /* SLIM_MIXER("AIF1_CAP Mixer"),*/
+ {"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+ /* SLIM_MUX("AIF1_PB", "AIF1 PB"), */
+ {"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"},
+
+ {"SLIM RX1", NULL, "SLIM RX1 MUX"},
+ {"SLIM RX2", NULL, "SLIM RX2 MUX"},
+ {"SLIM RX3", NULL, "SLIM RX3 MUX"},
+ {"SLIM RX4", NULL, "SLIM RX4 MUX"},
/* Slimbus port 5 is non functional in Sitar 1.0 */
{"RX1 MIX1 INP1", "RX1", "SLIM RX1"},
@@ -2202,12 +2454,6 @@
/* TX */
- {"SLIM TX1", NULL, "SLIM TX1 MUX"},
- {"SLIM TX2", NULL, "SLIM TX2 MUX"},
- {"SLIM TX3", NULL, "SLIM TX3 MUX"},
- {"SLIM TX4", NULL, "SLIM TX4 MUX"},
- {"SLIM TX5", NULL, "SLIM TX5 MUX"},
-
{"SLIM TX1 MUX", "DEC1", "DEC1 MUX"},
{"SLIM TX2 MUX", "DEC2", "DEC2 MUX"},
{"SLIM TX3 MUX", "DEC3", "DEC3 MUX"},
@@ -2318,6 +2564,9 @@
{
int ret;
+ if (reg == SND_SOC_NOPM)
+ return 0;
+
BUG_ON(reg > SITAR_MAX_REGISTER);
if (!sitar_volatile(codec, reg)) {
@@ -2335,6 +2584,9 @@
unsigned int val;
int ret;
+ if (reg == SND_SOC_NOPM)
+ return 0;
+
BUG_ON(reg > SITAR_MAX_REGISTER);
if (!sitar_volatile(codec, reg) && sitar_readable(codec, reg) &&
@@ -2582,11 +2834,11 @@
return;
if (dai->id <= NUM_CODEC_DAIS) {
- if (sitar->dai[dai->id-1].ch_mask) {
+ if (sitar->dai[dai->id].ch_mask) {
active = 1;
pr_debug("%s(): Codec DAI: chmask[%d] = 0x%x\n",
- __func__, dai->id-1,
- sitar->dai[dai->id-1].ch_mask);
+ __func__, dai->id,
+ sitar->dai[dai->id].ch_mask);
}
}
@@ -2700,38 +2952,16 @@
{
struct sitar_priv *sitar = snd_soc_codec_get_drvdata(dai->codec);
- u32 i = 0;
+ struct wcd9xxx *core = dev_get_drvdata(dai->codec->dev->parent);
if (!tx_slot && !rx_slot) {
pr_err("%s: Invalid\n", __func__);
return -EINVAL;
}
pr_debug("%s: DAI-ID %x %d %d\n", __func__, dai->id, tx_num, rx_num);
- if (dai->id == AIF1_PB) {
- for (i = 0; i < rx_num; i++) {
- sitar->dai[dai->id - 1].ch_num[i] = rx_slot[i];
- sitar->dai[dai->id - 1].ch_act = 0;
- sitar->dai[dai->id - 1].ch_tot = rx_num;
- }
- } else if (dai->id == AIF1_CAP) {
- sitar->dai[dai->id - 1].ch_tot = tx_num;
- /* If all channels are already active,
- * Do not reset ch_act flag
- */
- if ((sitar->dai[dai->id - 1].ch_tot != 0)
- && (sitar->dai[dai->id - 1].ch_act ==
- sitar->dai[dai->id - 1].ch_tot)) {
- pr_info("%s: ch_act = %d, ch_tot = %d\n", __func__,
- sitar->dai[dai->id - 1].ch_act,
- sitar->dai[dai->id - 1].ch_tot);
-
- return 0;
- }
- sitar->dai[dai->id - 1].ch_act = 0;
-
- for (i = 0; i < tx_num; i++)
- sitar->dai[dai->id - 1].ch_num[i] = tx_slot[i];
- }
+ if (sitar->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+ wcd9xxx_init_slimslave(core, core->slim->laddr,
+ tx_num, tx_slot, rx_num, rx_slot);
return 0;
}
@@ -2740,33 +2970,38 @@
unsigned int *rx_num, unsigned int *rx_slot)
{
- struct wcd9xxx *sitar = dev_get_drvdata(dai->codec->control_data);
+ struct sitar_priv *sitar_p = snd_soc_codec_get_drvdata(dai->codec);
+ u32 i = 0;
+ struct wcd9xxx_ch *ch;
- u32 cnt = 0;
- u32 tx_ch[SLIM_MAX_TX_PORTS];
- u32 rx_ch[SLIM_MAX_RX_PORTS];
-
- if (!rx_slot && !tx_slot) {
- pr_err("%s: Invalid\n", __func__);
- return -EINVAL;
- }
- pr_debug("%s: DAI-ID %x\n", __func__, dai->id);
- /* for virtual port, codec driver needs to do
- * housekeeping, for now should be ok
- */
- wcd9xxx_get_channel(sitar, rx_ch, tx_ch);
- if (dai->id == AIF1_PB) {
- *rx_num = sitar_dai[dai->id - 1].playback.channels_max;
- while (cnt < *rx_num) {
- rx_slot[cnt] = rx_ch[cnt];
- cnt++;
+ switch (dai->id) {
+ case AIF1_PB:
+ if (!rx_slot || !rx_num) {
+ pr_err("%s: Invalid rx_slot 0x%x or rx_num 0x%x\n",
+ __func__, (u32) rx_slot, (u32) rx_num);
+ return -EINVAL;
}
- } else if (dai->id == AIF1_CAP) {
- *tx_num = sitar_dai[dai->id - 1].capture.channels_max;
- tx_slot[0] = tx_ch[cnt];
- tx_slot[1] = tx_ch[4 + cnt];
- tx_slot[2] = tx_ch[2 + cnt];
- tx_slot[3] = tx_ch[3 + cnt];
+ list_for_each_entry(ch, &sitar_p->dai[dai->id].wcd9xxx_ch_list,
+ list) {
+ rx_slot[i++] = ch->ch_num;
+ }
+ *rx_num = i;
+ break;
+ case AIF1_CAP:
+ if (!tx_slot || !tx_num) {
+ pr_err("%s: Invalid tx_slot 0x%x or tx_num 0x%x\n",
+ __func__, (u32) tx_slot, (u32) tx_num);
+ return -EINVAL;
+ }
+ list_for_each_entry(ch, &sitar_p->dai[dai->id].wcd9xxx_ch_list,
+ list) {
+ tx_slot[i++] = ch->ch_num;
+ }
+ *tx_num = i;
+ break;
+ default:
+ pr_err("%s: Invalid dai %d", __func__, dai->id);
+ return -EINVAL;
}
return 0;
}
@@ -2802,7 +3037,7 @@
break;
default:
pr_err("%s: Invalid sampling rate %d\n", __func__,
- params_rate(params));
+ params_rate(params));
return -EINVAL;
}
@@ -2839,13 +3074,14 @@
0x20, 0x00);
break;
default:
- pr_err("invalid format\n");
- break;
+ pr_err("%s: Unsupport format %d\n", __func__,
+ params_format(params));
+ return -EINVAL;
}
snd_soc_update_bits(codec, SITAR_A_CDC_CLK_TX_I2S_CTL,
0x03, tx_fs_rate);
} else {
- sitar->dai[dai->id - 1].rate = params_rate(params);
+ sitar->dai[dai->id].rate = params_rate(params);
}
}
@@ -2886,13 +3122,14 @@
0x20, 0x00);
break;
default:
- pr_err("invalid format\n");
+ pr_err("%s: Unsupport format %d\n", __func__,
+ params_format(params));
break;
}
snd_soc_update_bits(codec, SITAR_A_CDC_CLK_RX_I2S_CTL,
0x03, (rx_fs_rate >> 0x05));
} else {
- sitar->dai[dai->id - 1].rate = params_rate(params);
+ sitar->dai[dai->id].rate = params_rate(params);
}
}
@@ -2974,30 +3211,30 @@
static int sitar_codec_enable_chmask(struct sitar_priv *sitar,
int event, int index)
{
- int ret = 0;
- u32 k = 0;
+ int ret = 0;
+ struct wcd9xxx_ch *ch;
+
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- for (k = 0; k < sitar->dai[index].ch_tot; k++) {
- ret = wcd9xxx_get_slave_port(
- sitar->dai[index].ch_num[k]);
+ list_for_each_entry(ch,
+ &sitar->dai[index].wcd9xxx_ch_list, list) {
+ ret = wcd9xxx_get_slave_port(ch->ch_num);
if (ret < 0) {
- pr_err("%s: Invalid Slave port ID: %d\n",
- __func__, ret);
+ pr_err("%s: Invalid slave port ID: %d\n",
+ __func__, ret);
ret = -EINVAL;
break;
}
sitar->dai[index].ch_mask |= 1 << ret;
}
- ret = 0;
break;
case SND_SOC_DAPM_POST_PMD:
ret = wait_event_timeout(sitar->dai[index].dai_wait,
(sitar->dai[index].ch_mask == 0),
- msecs_to_jiffies(SLIM_CLOSE_TIMEOUT));
+ msecs_to_jiffies(SLIM_CLOSE_TIMEOUT));
if (!ret) {
- pr_err("%s: slim close tx/rx timeout\n",
- __func__);
+ pr_err("%s: Slim close tx/rx wait timeout\n",
+ __func__);
ret = -EINVAL;
} else {
ret = 0;
@@ -3010,70 +3247,46 @@
static int sitar_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct wcd9xxx *sitar;
+ struct wcd9xxx *core;
struct snd_soc_codec *codec = w->codec;
struct sitar_priv *sitar_p = snd_soc_codec_get_drvdata(codec);
- u32 j = 0;
- int ret = 0;
- codec->control_data = dev_get_drvdata(codec->dev->parent);
- sitar = codec->control_data;
+ int ret = 0;
+ struct wcd9xxx_codec_dai_data *dai;
+
+ core = dev_get_drvdata(codec->dev->parent);
/* Execute the callback only if interface type is slimbus */
if (sitar_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
- if (event == SND_SOC_DAPM_POST_PMD && (sitar != NULL))
- sitar_codec_pm_runtime_put(sitar);
+ if (event == SND_SOC_DAPM_POST_PMD && (core != NULL))
+ sitar_codec_pm_runtime_put(core);
return 0;
}
+ dai = &sitar_p->dai[w->shift];
+
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- for (j = 0; j < ARRAY_SIZE(sitar_dai); j++) {
- if (sitar_dai[j].id == AIF1_CAP)
- continue;
- if (!strncmp(w->sname,
- sitar_dai[j].playback.stream_name, 13)) {
- ++sitar_p->dai[j].ch_act;
- break;
- }
- }
- if (sitar_p->dai[j].ch_act == sitar_p->dai[j].ch_tot) {
- ret = sitar_codec_enable_chmask(sitar_p, event, j);
- ret = wcd9xxx_cfg_slim_sch_rx(sitar,
- sitar_p->dai[j].ch_num,
- sitar_p->dai[j].ch_tot,
- sitar_p->dai[j].rate);
- }
+ ret = sitar_codec_enable_chmask(sitar_p, SND_SOC_DAPM_POST_PMU,
+ w->shift);
+ ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+ dai->rate, dai->bit_width,
+ &dai->grph);
break;
case SND_SOC_DAPM_POST_PMD:
- for (j = 0; j < ARRAY_SIZE(sitar_dai); j++) {
- if (sitar_dai[j].id == AIF1_CAP)
- continue;
- if (!strncmp(w->sname,
- sitar_dai[j].playback.stream_name, 13)) {
- --sitar_p->dai[j].ch_act;
- break;
- }
- }
- if (!sitar_p->dai[j].ch_act) {
- wcd9xxx_close_slim_sch_rx(sitar,
- sitar_p->dai[j].ch_num,
- sitar_p->dai[j].ch_tot);
- ret = sitar_codec_enable_chmask(sitar_p, event, j);
- if (ret < 0) {
- ret = wcd9xxx_disconnect_port(sitar,
- sitar_p->dai[j].ch_num,
- sitar_p->dai[j].ch_tot,
- 1);
+ ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+ dai->grph);
+ ret = sitar_codec_enable_chmask(sitar_p, SND_SOC_DAPM_POST_PMD,
+ w->shift);
+ if (ret < 0) {
+ ret = wcd9xxx_disconnect_port(core,
+ &dai->wcd9xxx_ch_list,
+ dai->grph);
pr_info("%s: Disconnect RX port ret = %d\n",
- __func__, ret);
- }
- sitar_p->dai[j].rate = 0;
- memset(sitar_p->dai[j].ch_num, 0, (sizeof(u32)*
- sitar_p->dai[j].ch_tot));
- sitar_p->dai[j].ch_tot = 0;
- if (sitar != NULL)
- sitar_codec_pm_runtime_put(sitar);
+ __func__, ret);
}
+ if (core != NULL)
+ sitar_codec_pm_runtime_put(core);
+ break;
}
return ret;
}
@@ -3081,72 +3294,45 @@
static int sitar_codec_enable_slimtx(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct wcd9xxx *sitar;
+ struct wcd9xxx *core;
struct snd_soc_codec *codec = w->codec;
struct sitar_priv *sitar_p = snd_soc_codec_get_drvdata(codec);
- /* index to the DAI ID, for now hardcoding */
- u32 j = 0;
+ struct wcd9xxx_codec_dai_data *dai;
int ret = 0;
- codec->control_data = dev_get_drvdata(codec->dev->parent);
- sitar = codec->control_data;
+ core = dev_get_drvdata(codec->dev->parent);
/* Execute the callback only if interface type is slimbus */
if (sitar_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
- if (event == SND_SOC_DAPM_POST_PMD && (sitar != NULL))
- sitar_codec_pm_runtime_put(sitar);
+ if (event == SND_SOC_DAPM_POST_PMD && (core != NULL))
+ sitar_codec_pm_runtime_put(core);
return 0;
}
+ dai = &sitar_p->dai[w->shift];
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- for (j = 0; j < ARRAY_SIZE(sitar_dai); j++) {
- if (sitar_dai[j].id == AIF1_PB)
- continue;
- if (!strncmp(w->sname,
- sitar_dai[j].capture.stream_name, 13)) {
- ++sitar_p->dai[j].ch_act;
- break;
- }
- }
- if (sitar_p->dai[j].ch_act == sitar_p->dai[j].ch_tot) {
- ret = sitar_codec_enable_chmask(sitar_p, event, j);
- ret = wcd9xxx_cfg_slim_sch_tx(sitar,
- sitar_p->dai[j].ch_num,
- sitar_p->dai[j].ch_tot,
- sitar_p->dai[j].rate);
- }
+ ret = sitar_codec_enable_chmask(sitar_p, SND_SOC_DAPM_POST_PMU,
+ w->shift);
+ ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+ dai->rate, dai->bit_width,
+ &dai->grph);
break;
case SND_SOC_DAPM_POST_PMD:
- for (j = 0; j < ARRAY_SIZE(sitar_dai); j++) {
- if (sitar_dai[j].id == AIF1_PB)
- continue;
- if (!strncmp(w->sname,
- sitar_dai[j].capture.stream_name, 13)) {
- --sitar_p->dai[j].ch_act;
- break;
- }
+ ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+ dai->grph);
+ ret = sitar_codec_enable_chmask(sitar_p, SND_SOC_DAPM_POST_PMD,
+ w->shift);
+ if (ret < 0) {
+ ret = wcd9xxx_disconnect_port(core,
+ &dai->wcd9xxx_ch_list,
+ dai->grph);
+ pr_info("%s: Disconnect RX port ret = %d\n",
+ __func__, ret);
}
- if (!sitar_p->dai[j].ch_act) {
- wcd9xxx_close_slim_sch_tx(sitar,
- sitar_p->dai[j].ch_num,
- sitar_p->dai[j].ch_tot);
- ret = sitar_codec_enable_chmask(sitar_p, event, j);
- if (ret < 0) {
- ret = wcd9xxx_disconnect_port(sitar,
- sitar_p->dai[j].ch_num,
- sitar_p->dai[j].ch_tot,
- 0);
- pr_info("%s: Disconnect TX port, ret = %d\n",
- __func__, ret);
- }
- sitar_p->dai[j].rate = 0;
- memset(sitar_p->dai[j].ch_num, 0, (sizeof(u32)*
- sitar_p->dai[j].ch_tot));
- sitar_p->dai[j].ch_tot = 0;
- if (sitar != NULL)
- sitar_codec_pm_runtime_put(sitar);
- }
+ if (core != NULL)
+ sitar_codec_pm_runtime_put(core);
+ break;
}
return ret;
}
@@ -3186,7 +3372,7 @@
short bias_value;
struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
- wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
if (noreldetection)
sitar_turn_onoff_rel_detection(codec, false);
@@ -3222,7 +3408,7 @@
if (noreldetection)
sitar_turn_onoff_rel_detection(codec, true);
- wcd9xxx_enable_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
return bias_value;
}
@@ -3440,7 +3626,7 @@
sitar = snd_soc_codec_get_drvdata(codec);
calibration = sitar->mbhc_cfg.calibration;
- wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
sitar_turn_onoff_rel_detection(codec, false);
/* First compute the DCE / STA wait times
@@ -3523,7 +3709,7 @@
snd_soc_write(codec, SITAR_A_MBHC_SCALING_MUX_1, 0x84);
usleep_range(100, 100);
- wcd9xxx_enable_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
sitar_turn_onoff_rel_detection(codec, true);
}
@@ -3807,9 +3993,9 @@
}
sitar_set_and_turnoff_hph_padac(codec);
hphocp_off_report(sitar, SND_JACK_OC_HPHR,
- SITAR_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
hphocp_off_report(sitar, SND_JACK_OC_HPHL,
- SITAR_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
sitar->current_plug = PLUG_TYPE_NONE;
sitar->mbhc_polling_active = false;
} else {
@@ -4231,9 +4417,9 @@
snd_soc_update_bits(codec, SITAR_A_RX_HPH_OCP_CTL,
0x10, 0x10);
wcd9xxx_enable_irq(codec->control_data,
- SITAR_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
wcd9xxx_enable_irq(codec->control_data,
- SITAR_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
/* Bootup time detection */
sitar_hs_gpio_handler(codec);
}
@@ -4623,7 +4809,7 @@
0x10);
} else {
wcd9xxx_disable_irq(codec->control_data,
- SITAR_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
sitar->hphlocp_cnt = 0;
sitar->hph_status |= SND_JACK_OC_HPHL;
if (sitar->mbhc_cfg.headset_jack)
@@ -4656,7 +4842,7 @@
0x10);
} else {
wcd9xxx_disable_irq(codec->control_data,
- SITAR_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
sitar->hphrocp_cnt = 0;
sitar->hph_status |= SND_JACK_OC_HPHR;
if (sitar->mbhc_cfg.headset_jack)
@@ -4679,7 +4865,7 @@
pr_debug("%s: enter\n", __func__);
SITAR_ACQUIRE_LOCK(priv->codec_resource_lock);
- wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_INSERTION);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
@@ -4860,6 +5046,7 @@
u8 flag = pdata->amic_settings.use_pdata;
u8 i = 0, j = 0;
u8 val_txfe = 0, value = 0;
+ int amic_reg_count = 0;
if (!pdata) {
rc = -ENODEV;
@@ -4905,7 +5092,8 @@
snd_soc_update_bits(codec, SITAR_A_MICB_2_CTL, 0x10,
(pdata->micbias.bias2_cap_mode << 4));
- for (i = 0; i < 6; j++, i += 2) {
+ amic_reg_count = (NUM_AMIC % 2) ? NUM_AMIC + 1 : NUM_AMIC;
+ for (i = 0; i < amic_reg_count; j++, i += 2) {
if (flag & (0x01 << i)) {
value = (leg_mode & (0x01 << i)) ? 0x10 : 0x00;
val_txfe = (txfe_bypass & (0x01 << i)) ? 0x20 : 0x00;
@@ -4976,6 +5164,7 @@
SITAR_REG_VAL(SITAR_A_CDC_RX1_B6_CTL, 0x80),
SITAR_REG_VAL(SITAR_A_CDC_CLSG_FREQ_THRESH_B3_CTL, 0x1B),
+ SITAR_REG_VAL(SITAR_A_CDC_CLSG_FREQ_THRESH_B4_CTL, 0x5B),
};
@@ -5057,16 +5246,16 @@
static int sitar_codec_probe(struct snd_soc_codec *codec)
{
- struct sitar *control;
+ struct wcd9xxx *core;
struct sitar_priv *sitar;
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret = 0;
int i;
u8 sitar_version;
- int ch_cnt;
+ void *ptr = NULL;
codec->control_data = dev_get_drvdata(codec->dev->parent);
- control = codec->control_data;
+ core = codec->control_data;
sitar = kzalloc(sizeof(struct sitar_priv), GFP_KERNEL);
if (!sitar) {
@@ -5115,12 +5304,35 @@
ARRAY_SIZE(sitar_snd_controls));
snd_soc_dapm_new_controls(dapm, sitar_dapm_widgets,
ARRAY_SIZE(sitar_dapm_widgets));
+
+ ptr = kmalloc((sizeof(sitar_rx_chs) +
+ sizeof(sitar_tx_chs)), GFP_KERNEL);
+ if (!ptr) {
+ pr_err("%s: no mem for slim chan ctl data\n", __func__);
+ ret = -ENOMEM;
+ goto err_nomem_slimch;
+ }
if (sitar->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
snd_soc_dapm_new_controls(dapm, sitar_dapm_i2s_widgets,
ARRAY_SIZE(sitar_dapm_i2s_widgets));
snd_soc_dapm_add_routes(dapm, audio_i2s_map,
ARRAY_SIZE(audio_i2s_map));
+ for (i = 0; i < ARRAY_SIZE(sitar_i2s_dai); i++)
+ INIT_LIST_HEAD(&sitar->dai[i].wcd9xxx_ch_list);
}
+ if (sitar->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ for (i = 0; i < NUM_CODEC_DAIS; i++) {
+ INIT_LIST_HEAD(&sitar->dai[i].wcd9xxx_ch_list);
+ init_waitqueue_head(&sitar->dai[i].dai_wait);
+ }
+ }
+ core->num_rx_port = SITAR_RX_MAX;
+ core->rx_chs = ptr;
+ memcpy(core->rx_chs, sitar_rx_chs, sizeof(sitar_rx_chs));
+ core->num_tx_port = SITAR_TX_MAX;
+ core->tx_chs = ptr + sizeof(sitar_rx_chs);
+ memcpy(core->tx_chs, sitar_tx_chs, sizeof(sitar_tx_chs));
+
snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
sitar_version = snd_soc_read(codec, WCD9XXX_A_CHIP_VERSION);
@@ -5132,44 +5344,49 @@
snd_soc_dapm_sync(dapm);
- ret = wcd9xxx_request_irq(codec->control_data, SITAR_IRQ_MBHC_INSERTION,
+ ret = wcd9xxx_request_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_INSERTION,
sitar_hs_insert_irq, "Headset insert detect", sitar);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- SITAR_IRQ_MBHC_INSERTION);
+ WCD9XXX_IRQ_MBHC_INSERTION);
goto err_insert_irq;
}
- wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_INSERTION);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
- ret = wcd9xxx_request_irq(codec->control_data, SITAR_IRQ_MBHC_REMOVAL,
+ ret = wcd9xxx_request_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_REMOVAL,
sitar_hs_remove_irq, "Headset remove detect", sitar);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- SITAR_IRQ_MBHC_REMOVAL);
+ WCD9XXX_IRQ_MBHC_REMOVAL);
goto err_remove_irq;
}
- ret = wcd9xxx_request_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL,
+ ret = wcd9xxx_request_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_POTENTIAL,
sitar_dce_handler, "DC Estimation detect", sitar);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- SITAR_IRQ_MBHC_POTENTIAL);
+ WCD9XXX_IRQ_MBHC_POTENTIAL);
goto err_potential_irq;
}
- ret = wcd9xxx_request_irq(codec->control_data, SITAR_IRQ_MBHC_RELEASE,
- sitar_release_handler, "Button Release detect", sitar);
+ ret = wcd9xxx_request_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_RELEASE,
+ sitar_release_handler,
+ "Button Release detect", sitar);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- SITAR_IRQ_MBHC_RELEASE);
+ WCD9XXX_IRQ_MBHC_RELEASE);
goto err_release_irq;
}
- ret = wcd9xxx_request_irq(codec->control_data, SITAR_IRQ_SLIMBUS,
- sitar_slimbus_irq, "SLIMBUS Slave", sitar);
+ ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS,
+ sitar_slimbus_irq, "SLIMBUS Slave", sitar);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- SITAR_IRQ_SLIMBUS);
+ WCD9XXX_IRQ_SLIMBUS);
goto err_slimbus_irq;
}
@@ -5179,40 +5396,27 @@
ret = wcd9xxx_request_irq(codec->control_data,
- SITAR_IRQ_HPH_PA_OCPL_FAULT, sitar_hphl_ocp_irq,
- "HPH_L OCP detect", sitar);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+ sitar_hphl_ocp_irq,
+ "HPH_L OCP detect", sitar);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- SITAR_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
goto err_hphl_ocp_irq;
}
- wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_HPH_PA_OCPL_FAULT);
+ wcd9xxx_disable_irq(codec->control_data,
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
ret = wcd9xxx_request_irq(codec->control_data,
- SITAR_IRQ_HPH_PA_OCPR_FAULT, sitar_hphr_ocp_irq,
- "HPH_R OCP detect", sitar);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
+ sitar_hphr_ocp_irq, "HPH_R OCP detect",
+ sitar);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- SITAR_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
goto err_hphr_ocp_irq;
}
- wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_HPH_PA_OCPR_FAULT);
-
- for (i = 0; i < ARRAY_SIZE(sitar_dai); i++) {
- switch (sitar_dai[i].id) {
- case AIF1_PB:
- ch_cnt = sitar_dai[i].playback.channels_max;
- break;
- case AIF1_CAP:
- ch_cnt = sitar_dai[i].capture.channels_max;
- break;
- default:
- continue;
- }
- sitar->dai[i].ch_num = kzalloc((sizeof(unsigned int)*
- ch_cnt), GFP_KERNEL);
- init_waitqueue_head(&sitar->dai[i].dai_wait);
- }
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
codec->ignore_pmdown_time = 1;
@@ -5223,24 +5427,23 @@
return ret;
err_hphr_ocp_irq:
- wcd9xxx_free_irq(codec->control_data,
- SITAR_IRQ_HPH_PA_OCPL_FAULT, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+ sitar);
err_hphl_ocp_irq:
- wcd9xxx_free_irq(codec->control_data,
- SITAR_IRQ_SLIMBUS, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, sitar);
err_slimbus_irq:
- wcd9xxx_free_irq(codec->control_data,
- SITAR_IRQ_MBHC_RELEASE, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, sitar);
err_release_irq:
- wcd9xxx_free_irq(codec->control_data,
- SITAR_IRQ_MBHC_POTENTIAL, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL,
+ sitar);
err_potential_irq:
- wcd9xxx_free_irq(codec->control_data,
- SITAR_IRQ_MBHC_REMOVAL, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, sitar);
err_remove_irq:
- wcd9xxx_free_irq(codec->control_data,
- SITAR_IRQ_MBHC_INSERTION, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION,
+ sitar);
err_insert_irq:
+ kfree(ptr);
+err_nomem_slimch:
err_pdata:
mutex_destroy(&sitar->codec_resource_lock);
kfree(sitar);
@@ -5248,21 +5451,20 @@
}
static int sitar_codec_remove(struct snd_soc_codec *codec)
{
- int i;
struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
- wcd9xxx_free_irq(codec->control_data, SITAR_IRQ_SLIMBUS, sitar);
- wcd9xxx_free_irq(codec->control_data, SITAR_IRQ_MBHC_RELEASE, sitar);
- wcd9xxx_free_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL, sitar);
- wcd9xxx_free_irq(codec->control_data, SITAR_IRQ_MBHC_REMOVAL, sitar);
- wcd9xxx_free_irq(codec->control_data, SITAR_IRQ_MBHC_INSERTION, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL,
+ sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, sitar);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION,
+ sitar);
SITAR_ACQUIRE_LOCK(sitar->codec_resource_lock);
sitar_codec_disable_clock_block(codec);
SITAR_RELEASE_LOCK(sitar->codec_resource_lock);
sitar_codec_enable_bandgap(codec, SITAR_BANDGAP_OFF);
if (sitar->mbhc_fw)
release_firmware(sitar->mbhc_fw);
- for (i = 0; i < ARRAY_SIZE(sitar_dai); i++)
- kfree(sitar->dai[i].ch_num);
mutex_destroy(&sitar->codec_resource_lock);
kfree(sitar);
return 0;
diff --git a/sound/soc/codecs/wcd9304.h b/sound/soc/codecs/wcd9304.h
index 70b3f0b..13336ef 100644
--- a/sound/soc/codecs/wcd9304.h
+++ b/sound/soc/codecs/wcd9304.h
@@ -191,6 +191,26 @@
extern int sitar_mclk_enable(struct snd_soc_codec *codec, int mclk_enable,
bool dapm);
+/* Number of input and output Slimbus ports
+ */
+enum {
+ SITAR_RX1 = 0,
+ SITAR_RX2,
+ SITAR_RX3,
+ SITAR_RX4,
+ SITAR_RX5,
+ SITAR_RX_MAX,
+};
+
+enum {
+ SITAR_TX1 = 0,
+ SITAR_TX2,
+ SITAR_TX3,
+ SITAR_TX4,
+ SITAR_TX5,
+ SITAR_TX_MAX,
+};
+
extern void *sitar_mbhc_cal_btn_det_mp(const struct sitar_mbhc_btn_detect_cfg
*btn_det,
const enum sitar_mbhc_btn_det_mem mem);
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index deddbe8..564cad6 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -33,6 +33,9 @@
#include <linux/pm_runtime.h>
#include <linux/kernel.h>
#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/wakelock.h>
+#include <linux/suspend.h>
#include "wcd9310.h"
static int cfilt_adjust_ms = 10;
@@ -71,25 +74,33 @@
#define TABLA_OCP_ATTEMPT 1
-#define AIF1_PB 1
-#define AIF1_CAP 2
-#define AIF2_PB 3
-#define AIF2_CAP 4
-#define AIF3_CAP 5
-#define AIF3_PB 6
-
-#define NUM_CODEC_DAIS 6
-#define TABLA_COMP_DIGITAL_GAIN_OFFSET 3
-
-struct tabla_codec_dai_data {
- u32 rate;
- u32 *ch_num;
- u32 ch_act;
- u32 ch_tot;
- u32 ch_mask;
- wait_queue_head_t dai_wait;
+enum {
+ AIF1_PB = 0,
+ AIF1_CAP,
+ AIF2_PB,
+ AIF2_CAP,
+ AIF3_PB,
+ AIF3_CAP,
+ NUM_CODEC_DAIS,
};
+enum {
+ RX_MIX1_INP_SEL_ZERO = 0,
+ RX_MIX1_INP_SEL_SRC1,
+ RX_MIX1_INP_SEL_SRC2,
+ RX_MIX1_INP_SEL_IIR1,
+ RX_MIX1_INP_SEL_IIR2,
+ RX_MIX1_INP_SEL_RX1,
+ RX_MIX1_INP_SEL_RX2,
+ RX_MIX1_INP_SEL_RX3,
+ RX_MIX1_INP_SEL_RX4,
+ RX_MIX1_INP_SEL_RX5,
+ RX_MIX1_INP_SEL_RX6,
+ RX_MIX1_INP_SEL_RX7,
+};
+
+#define TABLA_COMP_DIGITAL_GAIN_OFFSET 3
+
#define TABLA_MCLK_RATE_12288KHZ 12288000
#define TABLA_MCLK_RATE_9600KHZ 9600000
@@ -252,6 +263,38 @@
static struct hpf_work tx_hpf_work[NUM_DECIMATORS];
+static const struct wcd9xxx_ch tabla_rx_chs[TABLA_RX_MAX] = {
+ WCD9XXX_CH(10, 0),
+ WCD9XXX_CH(11, 1),
+ WCD9XXX_CH(12, 2),
+ WCD9XXX_CH(13, 3),
+ WCD9XXX_CH(14, 4),
+ WCD9XXX_CH(15, 5),
+ WCD9XXX_CH(16, 6)
+};
+
+static const struct wcd9xxx_ch tabla_tx_chs[TABLA_TX_MAX] = {
+ WCD9XXX_CH(0, 0),
+ WCD9XXX_CH(1, 1),
+ WCD9XXX_CH(2, 2),
+ WCD9XXX_CH(3, 3),
+ WCD9XXX_CH(4, 4),
+ WCD9XXX_CH(5, 5),
+ WCD9XXX_CH(6, 6),
+ WCD9XXX_CH(7, 7),
+ WCD9XXX_CH(8, 8),
+ WCD9XXX_CH(9, 9)
+};
+
+static const u32 vport_check_table[NUM_CODEC_DAIS] = {
+ 0, /* AIF1_PB */
+ (1 << AIF2_CAP) | (1 << AIF3_CAP), /* AIF1_CAP */
+ 0, /* AIF2_PB */
+ (1 << AIF1_CAP) | (1 << AIF3_CAP), /* AIF2_CAP */
+ 0, /* AIF2_PB */
+ (1 << AIF1_CAP) | (1 << AIF2_CAP), /* AIF2_CAP */
+};
+
struct tabla_priv {
struct snd_soc_codec *codec;
struct tabla_reg_address reg_addr;
@@ -307,7 +350,7 @@
const struct firmware *mbhc_fw;
/* num of slim ports required */
- struct tabla_codec_dai_data dai[NUM_CODEC_DAIS];
+ struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS];
/*compander*/
int comp_enabled[COMPANDER_MAX];
@@ -340,6 +383,9 @@
*/
struct work_struct hs_correct_plug_work_nogpio;
+ bool gpio_irq_resend;
+ struct wake_lock irq_resend_wlock;
+
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_poke;
struct dentry *debugfs_mbhc;
@@ -1676,6 +1722,238 @@
static const struct snd_kcontrol_new lineout4_ground_switch =
SOC_DAPM_SINGLE("Switch", TABLA_A_RX_LINE_4_DAC_CTL, 6, 1, 0);
+static int slim_tx_vport_validation(u32 dai_id, u32 port_id,
+ struct tabla_priv *tabla_p)
+{
+ struct wcd9xxx_ch *ch;
+ int ret = 0;
+ int index = 0;
+ u32 vtable = vport_check_table[dai_id];
+ pr_debug("%s: dai_id %u vtable 0x%x port_id %u\n", __func__,
+ dai_id, vtable, port_id);
+ while (vtable) {
+ if (vtable & 1) {
+ list_for_each_entry(ch,
+ &tabla_p->dai[index].wcd9xxx_ch_list,
+ list) {
+ pr_debug("%s: index %u ch->port %u vtable 0x%x\n",
+ __func__, index, ch->port, vtable);
+ if (ch->port == port_id) {
+ pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n",
+ __func__, port_id + 1,
+ (index + 1)/2);
+ ret = -EINVAL;
+ break;
+ }
+ }
+ }
+ if (ret)
+ break;
+ index++;
+ vtable = vtable >> 1;
+ }
+ return ret;
+}
+
+/* virtual port entries */
+static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+ ucontrol->value.integer.value[0] = widget->value;
+ return 0;
+}
+
+static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
+ struct tabla_priv *tabla_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+ struct soc_multi_mixer_control *mixer =
+ ((struct soc_multi_mixer_control *)kcontrol->private_value);
+ u32 dai_id = widget->shift;
+ u32 port_id = mixer->shift;
+ u32 enable = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: wname %s cname %s value %u shift %d item %ld\n", __func__,
+ widget->name, ucontrol->id.name, widget->value, widget->shift,
+ ucontrol->value.integer.value[0]);
+
+ mutex_lock(&codec->mutex);
+ if (tabla_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ if (dai_id != AIF1_CAP) {
+ dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+ __func__);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+ }
+ switch (dai_id) {
+ case AIF1_CAP:
+ case AIF2_CAP:
+ case AIF3_CAP:
+ /* only add to the list if value not set
+ */
+ if (enable && !(widget->value & 1 << port_id)) {
+ if (slim_tx_vport_validation(dai_id,
+ port_id, tabla_p)) {
+ pr_info("%s: TX%u is used by other virtual port\n",
+ __func__, port_id + 1);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+ widget->value |= 1 << port_id;
+ list_add_tail(&core->tx_chs[port_id].list,
+ &tabla_p->dai[dai_id].wcd9xxx_ch_list
+ );
+ } else if (!enable && (widget->value & 1 << port_id)) {
+ widget->value &= ~(1 << port_id);
+ list_del_init(&core->tx_chs[port_id].list);
+ } else {
+ if (enable)
+ pr_info("%s: TX%u port is used by this virtual port\n",
+ __func__, port_id + 1);
+ else
+ pr_info("%s: TX%u port is not used by this virtual port\n",
+ __func__, port_id + 1);
+ /* avoid update power function */
+ mutex_unlock(&codec->mutex);
+ return 0;
+ }
+ break;
+ default:
+ pr_err("Unknown AIF %d\n", dai_id);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+ pr_debug("%s: name %s sname %s updated value %u shift %d\n", __func__,
+ widget->name, widget->sname, widget->value, widget->shift);
+
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, enable);
+
+ mutex_unlock(&codec->mutex);
+ return 0;
+}
+
+static int slim_rx_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+ ucontrol->value.enumerated.item[0] = widget->value;
+ return 0;
+}
+
+static const char *const slim_rx_mux_text[] = {
+ "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB"
+};
+
+static int slim_rx_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
+ struct tabla_priv *tabla_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ u32 port_id = widget->shift;
+
+ pr_debug("%s: wname %s cname %s value %u shift %d item %u\n", __func__,
+ widget->name, ucontrol->id.name, widget->value, widget->shift,
+ ucontrol->value.enumerated.item[0]);
+
+ widget->value = ucontrol->value.enumerated.item[0];
+
+ mutex_lock(&codec->mutex);
+
+ if (tabla_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ if (widget->value > 1) {
+ dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+ __func__);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+ }
+ /* value need to match the Virtual port and AIF number
+ */
+ switch (widget->value) {
+ case 0:
+ list_del_init(&core->rx_chs[port_id].list);
+ break;
+ case 1:
+ list_add_tail(&core->rx_chs[port_id].list,
+ &tabla_p->dai[AIF1_PB].wcd9xxx_ch_list);
+ break;
+ case 2:
+ list_add_tail(&core->rx_chs[port_id].list,
+ &tabla_p->dai[AIF2_PB].wcd9xxx_ch_list);
+ break;
+ case 3:
+ list_add_tail(&core->rx_chs[port_id].list,
+ &tabla_p->dai[AIF3_PB].wcd9xxx_ch_list);
+ break;
+ default:
+ pr_err("Unknown AIF %d\n", widget->value);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+
+ snd_soc_dapm_mux_update_power(widget, kcontrol, 1, widget->value, e);
+
+ mutex_unlock(&codec->mutex);
+ return 0;
+}
+
+static const struct soc_enum slim_rx_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text);
+
+static const struct snd_kcontrol_new slim_rx_mux[TABLA_RX_MAX] = {
+ SOC_DAPM_ENUM_EXT("SLIM RX1 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX2 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX3 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX4 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX5 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX6 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX7 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+};
+
+static const struct snd_kcontrol_new aif_cap_mixer[] = {
+ SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TABLA_TX1, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TABLA_TX2, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TABLA_TX3, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TABLA_TX4, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TABLA_TX5, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TABLA_TX6, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TABLA_TX7, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TABLA_TX8, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TABLA_TX9, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TABLA_TX10, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
static void tabla_codec_enable_adc_block(struct snd_soc_codec *codec,
int enable)
{
@@ -2905,7 +3183,7 @@
/* reset retry counter as PA is turned off signifying
* start of new OCP detection session
*/
- if (TABLA_IRQ_HPH_PA_OCPL_FAULT)
+ if (WCD9XXX_IRQ_HPH_PA_OCPL_FAULT)
tabla->hphlocp_cnt = 0;
else
tabla->hphrocp_cnt = 0;
@@ -2917,14 +3195,16 @@
{
struct tabla_priv *tabla = container_of(work, struct tabla_priv,
hphlocp_work);
- hphocp_off_report(tabla, SND_JACK_OC_HPHL, TABLA_IRQ_HPH_PA_OCPL_FAULT);
+ hphocp_off_report(tabla, SND_JACK_OC_HPHL,
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
}
static void hphrocp_off_report(struct work_struct *work)
{
struct tabla_priv *tabla = container_of(work, struct tabla_priv,
hphrocp_work);
- hphocp_off_report(tabla, SND_JACK_OC_HPHR, TABLA_IRQ_HPH_PA_OCPR_FAULT);
+ hphocp_off_report(tabla, SND_JACK_OC_HPHR,
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
}
static int tabla_hph_pa_event(struct snd_soc_dapm_widget *w,
@@ -3114,13 +3394,47 @@
static const struct snd_soc_dapm_route audio_map[] = {
/* SLIMBUS Connections */
- {"SLIM TX1", NULL, "SLIM TX1 MUX"},
- {"SLIM TX1 MUX", "DEC1", "DEC1 MUX"},
+ {"AIF1 CAP", NULL, "AIF1_CAP Mixer"},
+ {"AIF2 CAP", NULL, "AIF2_CAP Mixer"},
+ {"AIF3 CAP", NULL, "AIF3_CAP Mixer"},
- {"SLIM TX2", NULL, "SLIM TX2 MUX"},
+ /* SLIM_MIXER("AIF1_CAP Mixer"),*/
+ {"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+ /* SLIM_MIXER("AIF2_CAP Mixer"),*/
+ {"AIF2_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+ /* SLIM_MIXER("AIF3_CAP Mixer"),*/
+ {"AIF3_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+
+ {"SLIM TX1 MUX", "DEC1", "DEC1 MUX"},
{"SLIM TX2 MUX", "DEC2", "DEC2 MUX"},
- {"SLIM TX3", NULL, "SLIM TX3 MUX"},
{"SLIM TX3 MUX", "DEC3", "DEC3 MUX"},
{"SLIM TX3 MUX", "RMIX1", "RX1 MIX1"},
{"SLIM TX3 MUX", "RMIX2", "RX2 MIX1"},
@@ -3130,10 +3444,8 @@
{"SLIM TX3 MUX", "RMIX6", "RX6 MIX1"},
{"SLIM TX3 MUX", "RMIX7", "RX7 MIX1"},
- {"SLIM TX4", NULL, "SLIM TX4 MUX"},
{"SLIM TX4 MUX", "DEC4", "DEC4 MUX"},
- {"SLIM TX5", NULL, "SLIM TX5 MUX"},
{"SLIM TX5 MUX", "DEC5", "DEC5 MUX"},
{"SLIM TX5 MUX", "RMIX1", "RX1 MIX1"},
{"SLIM TX5 MUX", "RMIX2", "RX2 MIX1"},
@@ -3143,10 +3455,8 @@
{"SLIM TX5 MUX", "RMIX6", "RX6 MIX1"},
{"SLIM TX5 MUX", "RMIX7", "RX7 MIX1"},
- {"SLIM TX6", NULL, "SLIM TX6 MUX"},
{"SLIM TX6 MUX", "DEC6", "DEC6 MUX"},
- {"SLIM TX7", NULL, "SLIM TX7 MUX"},
{"SLIM TX7 MUX", "DEC1", "DEC1 MUX"},
{"SLIM TX7 MUX", "DEC2", "DEC2 MUX"},
{"SLIM TX7 MUX", "DEC3", "DEC3 MUX"},
@@ -3165,7 +3475,6 @@
{"SLIM TX7 MUX", "RMIX6", "RX6 MIX1"},
{"SLIM TX7 MUX", "RMIX7", "RX7 MIX1"},
- {"SLIM TX8", NULL, "SLIM TX8 MUX"},
{"SLIM TX8 MUX", "DEC1", "DEC1 MUX"},
{"SLIM TX8 MUX", "DEC2", "DEC2 MUX"},
{"SLIM TX8 MUX", "DEC3", "DEC3 MUX"},
@@ -3177,7 +3486,6 @@
{"SLIM TX8 MUX", "DEC9", "DEC9 MUX"},
{"SLIM TX8 MUX", "DEC10", "DEC10 MUX"},
- {"SLIM TX9", NULL, "SLIM TX9 MUX"},
{"SLIM TX9 MUX", "DEC1", "DEC1 MUX"},
{"SLIM TX9 MUX", "DEC2", "DEC2 MUX"},
{"SLIM TX9 MUX", "DEC3", "DEC3 MUX"},
@@ -3189,7 +3497,6 @@
{"SLIM TX9 MUX", "DEC9", "DEC9 MUX"},
{"SLIM TX9 MUX", "DEC10", "DEC10 MUX"},
- {"SLIM TX10", NULL, "SLIM TX10 MUX"},
{"SLIM TX10 MUX", "DEC1", "DEC1 MUX"},
{"SLIM TX10 MUX", "DEC2", "DEC2 MUX"},
{"SLIM TX10 MUX", "DEC3", "DEC3 MUX"},
@@ -3304,6 +3611,40 @@
{"RX3 MIX2", NULL, "RX3 MIX2 INP1"},
{"RX3 MIX2", NULL, "RX3 MIX2 INP2"},
+ /* SLIM_MUX("AIF1_PB", "AIF1 PB"),*/
+ {"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX5 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX6 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX7 MUX", "AIF1_PB", "AIF1 PB"},
+ /* SLIM_MUX("AIF2_PB", "AIF2 PB"),*/
+ {"SLIM RX1 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX2 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX3 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX4 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX5 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX6 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX7 MUX", "AIF2_PB", "AIF2 PB"},
+ /* SLIM_MUX("AIF3_PB", "AIF3 PB"),*/
+ {"SLIM RX1 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX2 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX3 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX4 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX5 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX6 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX7 MUX", "AIF3_PB", "AIF3 PB"},
+
+ {"SLIM RX1", NULL, "SLIM RX1 MUX"},
+ {"SLIM RX2", NULL, "SLIM RX2 MUX"},
+ {"SLIM RX3", NULL, "SLIM RX3 MUX"},
+ {"SLIM RX4", NULL, "SLIM RX4 MUX"},
+ {"SLIM RX5", NULL, "SLIM RX5 MUX"},
+ {"SLIM RX6", NULL, "SLIM RX6 MUX"},
+ {"SLIM RX7", NULL, "SLIM RX7 MUX"},
+
+ /* Mixer control for output path */
{"RX1 MIX1 INP1", "RX1", "SLIM RX1"},
{"RX1 MIX1 INP1", "RX2", "SLIM RX2"},
{"RX1 MIX1 INP1", "RX3", "SLIM RX3"},
@@ -3653,6 +3994,10 @@
unsigned int value)
{
int ret;
+
+ if (reg == SND_SOC_NOPM)
+ return 0;
+
BUG_ON(reg > TABLA_MAX_REGISTER);
if (!tabla_volatile(codec, reg)) {
@@ -3670,6 +4015,9 @@
unsigned int val;
int ret;
+ if (reg == SND_SOC_NOPM)
+ return 0;
+
BUG_ON(reg > TABLA_MAX_REGISTER);
if (!tabla_volatile(codec, reg) && tabla_readable(codec, reg) &&
@@ -3793,10 +4141,10 @@
return;
if (dai->id <= NUM_CODEC_DAIS) {
- if (tabla->dai[dai->id-1].ch_mask) {
+ if (tabla->dai[dai->id].ch_mask) {
active = 1;
pr_debug("%s(): Codec DAI: chmask[%d] = 0x%x\n",
- __func__, dai->id-1, tabla->dai[dai->id-1].ch_mask);
+ __func__, dai->id, tabla->dai[dai->id].ch_mask);
}
}
@@ -3917,39 +4265,20 @@
{
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(dai->codec);
- u32 i = 0;
+ struct wcd9xxx *core = dev_get_drvdata(dai->codec->dev->parent);
+
if (!tx_slot && !rx_slot) {
pr_err("%s: Invalid\n", __func__);
return -EINVAL;
}
- pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n",
- __func__, dai->name, dai->id, tx_num, rx_num);
+ pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n"
+ "tabla->intf_type %d\n",
+ __func__, dai->name, dai->id, tx_num, rx_num,
+ tabla->intf_type);
- if (dai->id == AIF1_PB || dai->id == AIF2_PB || dai->id == AIF3_PB) {
- for (i = 0; i < rx_num; i++) {
- tabla->dai[dai->id - 1].ch_num[i] = rx_slot[i];
- tabla->dai[dai->id - 1].ch_act = 0;
- tabla->dai[dai->id - 1].ch_tot = rx_num;
- }
- } else if (dai->id == AIF1_CAP || dai->id == AIF2_CAP ||
- dai->id == AIF3_CAP) {
- tabla->dai[dai->id - 1].ch_tot = tx_num;
- /* All channels are already active.
- * do not reset ch_act flag
- */
- if ((tabla->dai[dai->id - 1].ch_tot != 0)
- && (tabla->dai[dai->id - 1].ch_act ==
- tabla->dai[dai->id - 1].ch_tot)) {
- pr_info("%s: ch_act = %d, ch_tot = %d\n", __func__,
- tabla->dai[dai->id - 1].ch_act,
- tabla->dai[dai->id - 1].ch_tot);
- return 0;
- }
-
- tabla->dai[dai->id - 1].ch_act = 0;
- for (i = 0; i < tx_num; i++)
- tabla->dai[dai->id - 1].ch_num[i] = tx_slot[i];
- }
+ if (tabla->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+ wcd9xxx_init_slimslave(core, core->slim->laddr,
+ tx_num, tx_slot, rx_num, rx_slot);
return 0;
}
@@ -3958,189 +4287,97 @@
unsigned int *rx_num, unsigned int *rx_slot)
{
- struct wcd9xxx *tabla = dev_get_drvdata(dai->codec->control_data);
+ struct tabla_priv *tabla_p = snd_soc_codec_get_drvdata(dai->codec);
+ u32 i = 0;
+ struct wcd9xxx_ch *ch;
- u32 cnt = 0;
- u32 tx_ch[SLIM_MAX_TX_PORTS];
- u32 rx_ch[SLIM_MAX_RX_PORTS];
+ switch (dai->id) {
+ case AIF1_PB:
+ case AIF2_PB:
+ case AIF3_PB:
+ if (!rx_slot || !rx_num) {
+ pr_err("%s: Invalid rx_slot %d or rx_num %d\n",
+ __func__, (u32) rx_slot, (u32) rx_num);
+ return -EINVAL;
+ }
+ list_for_each_entry(ch, &tabla_p->dai[dai->id].wcd9xxx_ch_list,
+ list) {
+ rx_slot[i++] = ch->ch_num;
+ }
+ *rx_num = i;
+ break;
+ case AIF1_CAP:
+ case AIF2_CAP:
+ case AIF3_CAP:
+ if (!tx_slot || !tx_num) {
+ pr_err("%s: Invalid tx_slot %d or tx_num %d\n",
+ __func__, (u32) tx_slot, (u32) tx_num);
+ return -EINVAL;
+ }
+ list_for_each_entry(ch, &tabla_p->dai[dai->id].wcd9xxx_ch_list,
+ list) {
+ tx_slot[i++] = ch->ch_num;
+ }
+ *tx_num = i;
+ break;
- if (!rx_slot && !tx_slot) {
- pr_err("%s: Invalid\n", __func__);
- return -EINVAL;
+ default:
+ pr_err("%s: Invalid DAI ID %x\n", __func__, dai->id);
+ break;
}
-
- /* for virtual port, codec driver needs to do
- * housekeeping, for now should be ok
- */
- wcd9xxx_get_channel(tabla, rx_ch, tx_ch);
- if (dai->id == AIF1_PB) {
- *rx_num = tabla_dai[dai->id - 1].playback.channels_max;
- while (cnt < *rx_num) {
- rx_slot[cnt] = rx_ch[cnt];
- cnt++;
- }
- } else if (dai->id == AIF1_CAP) {
- *tx_num = tabla_dai[dai->id - 1].capture.channels_max;
- while (cnt < *tx_num) {
- tx_slot[cnt] = tx_ch[6 + cnt];
- cnt++;
- }
- } else if (dai->id == AIF2_PB) {
- *rx_num = tabla_dai[dai->id - 1].playback.channels_max;
- while (cnt < *rx_num) {
- rx_slot[cnt] = rx_ch[5 + cnt];
- cnt++;
- }
- } else if (dai->id == AIF2_CAP) {
- *tx_num = tabla_dai[dai->id - 1].capture.channels_max;
- tx_slot[0] = tx_ch[cnt];
- tx_slot[1] = tx_ch[1 + cnt];
- tx_slot[2] = tx_ch[5 + cnt];
- tx_slot[3] = tx_ch[3 + cnt];
-
- } else if (dai->id == AIF3_PB) {
- *rx_num = tabla_dai[dai->id - 1].playback.channels_max;
- rx_slot[0] = rx_ch[3];
- rx_slot[1] = rx_ch[4];
-
- } else if (dai->id == AIF3_CAP) {
- *tx_num = tabla_dai[dai->id - 1].capture.channels_max;
- tx_slot[cnt] = tx_ch[2 + cnt];
- tx_slot[cnt + 1] = tx_ch[4 + cnt];
- }
- pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n",
- __func__, dai->name, dai->id, *tx_num, *rx_num);
-
-
return 0;
}
-static struct snd_soc_dapm_widget tabla_dapm_aif_in_widgets[] = {
-
- SND_SOC_DAPM_AIF_IN_E("SLIM RX1", "AIF1 Playback", 0, SND_SOC_NOPM, 1,
- 0, tabla_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_IN_E("SLIM RX2", "AIF1 Playback", 0, SND_SOC_NOPM, 2,
- 0, tabla_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_IN_E("SLIM RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 3,
- 0, tabla_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_IN_E("SLIM RX4", "AIF3 Playback", 0, SND_SOC_NOPM, 4,
- 0, tabla_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_IN_E("SLIM RX5", "AIF3 Playback", 0, SND_SOC_NOPM, 5,
- 0, tabla_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_IN_E("SLIM RX6", "AIF2 Playback", 0, SND_SOC_NOPM, 6,
- 0, tabla_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_IN_E("SLIM RX7", "AIF2 Playback", 0, SND_SOC_NOPM, 7,
- 0, tabla_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-};
-
-static struct snd_soc_dapm_widget tabla_dapm_aif_out_widgets[] = {
-
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX1", "AIF2 Capture", 0, SND_SOC_NOPM, 1,
- 0, tabla_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX2", "AIF2 Capture", 0, SND_SOC_NOPM, 2,
- 0, tabla_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX3", "AIF3 Capture", 0, SND_SOC_NOPM, 3,
- 0, tabla_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX4", "AIF2 Capture", 0, SND_SOC_NOPM, 4,
- 0, tabla_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX5", "AIF3 Capture", 0, SND_SOC_NOPM, 5,
- 0, tabla_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX6", "AIF2 Capture", 0, SND_SOC_NOPM, 6,
- 0, tabla_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX7", "AIF1 Capture", 0, SND_SOC_NOPM, 7,
- 0, tabla_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX8", "AIF1 Capture", 0, SND_SOC_NOPM, 8,
- 0, tabla_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX9", "AIF1 Capture", 0, SND_SOC_NOPM, 9,
- 0, tabla_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX10", "AIF1 Capture", 0, SND_SOC_NOPM, 10,
- 0, tabla_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-};
-
static int tabla_set_interpolator_rate(struct snd_soc_dai *dai,
- u8 rx_fs_rate_reg_val, u32 compander_fs, u32 sample_rate)
+ u8 rx_fs_rate_reg_val,
+ u32 compander_fs,
+ u32 sample_rate)
{
- u32 i, j;
+ u32 j;
u8 rx_mix1_inp;
u16 rx_mix_1_reg_1, rx_mix_1_reg_2;
u16 rx_fs_reg;
u8 rx_mix_1_reg_1_val, rx_mix_1_reg_2_val;
struct snd_soc_codec *codec = dai->codec;
+ struct wcd9xxx_ch *ch;
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_widget *w = tabla_dapm_aif_in_widgets;
- for (i = 0; i < ARRAY_SIZE(tabla_dapm_aif_in_widgets); i++) {
+ list_for_each_entry(ch, &tabla->dai[dai->id].wcd9xxx_ch_list, list) {
- if (strncmp(dai->driver->playback.stream_name, w[i].sname, 13))
- continue;
+ rx_mix1_inp = ch->port - RX_MIX1_INP_SEL_RX1;
- rx_mix1_inp = w[i].shift + 4;
-
- if ((rx_mix1_inp < 0x5) || (rx_mix1_inp > 0xB)) {
-
- pr_err("%s: Invalid SLIM RX%u port. widget = %s\n",
- __func__, rx_mix1_inp - 4 , w[i].name);
+ if ((rx_mix1_inp < RX_MIX1_INP_SEL_RX1) ||
+ (rx_mix1_inp > RX_MIX1_INP_SEL_RX7)) {
+ pr_err("%s: Invalid TABLA_RX%u port. Dai ID is %d\n",
+ __func__, rx_mix1_inp - 5 , dai->id);
return -EINVAL;
}
rx_mix_1_reg_1 = TABLA_A_CDC_CONN_RX1_B1_CTL;
for (j = 0; j < NUM_INTERPOLATORS; j++) {
-
rx_mix_1_reg_2 = rx_mix_1_reg_1 + 1;
rx_mix_1_reg_1_val = snd_soc_read(codec,
- rx_mix_1_reg_1);
+ rx_mix_1_reg_1);
rx_mix_1_reg_2_val = snd_soc_read(codec,
- rx_mix_1_reg_2);
+ rx_mix_1_reg_2);
if (((rx_mix_1_reg_1_val & 0x0F) == rx_mix1_inp) ||
- (((rx_mix_1_reg_1_val >> 4) & 0x0F) == rx_mix1_inp)
- || ((rx_mix_1_reg_2_val & 0x0F) == rx_mix1_inp)) {
+ (((rx_mix_1_reg_1_val >> 4) & 0x0F) == rx_mix1_inp) ||
+ ((rx_mix_1_reg_2_val & 0x0F) == rx_mix1_inp)) {
rx_fs_reg = TABLA_A_CDC_RX1_B5_CTL + 8 * j;
- pr_debug("%s: %s connected to RX%u\n", __func__,
- w[i].name, j + 1);
+ pr_debug("%s: AIF_PB DAI(%d) connected to RX%u\n",
+ __func__, dai->id, j + 1);
pr_debug("%s: set RX%u sample rate to %u\n",
__func__, j + 1, sample_rate);
snd_soc_update_bits(codec, rx_fs_reg,
- 0xE0, rx_fs_rate_reg_val);
+ 0xE0, rx_fs_rate_reg_val);
if (comp_rx_path[j] < COMPANDER_MAX)
tabla->comp_fs[comp_rx_path[j]]
@@ -4156,26 +4393,26 @@
}
static int tabla_set_decimator_rate(struct snd_soc_dai *dai,
- u8 tx_fs_rate_reg_val, u32 sample_rate)
+ u8 tx_fs_rate_reg_val,
+ u32 sample_rate)
{
struct snd_soc_codec *codec = dai->codec;
- struct snd_soc_dapm_widget *w = tabla_dapm_aif_out_widgets;
-
- u32 i, tx_port;
+ struct wcd9xxx_ch *ch;
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+ u32 tx_port;
u16 tx_port_reg, tx_fs_reg;
u8 tx_port_reg_val;
s8 decimator;
- for (i = 0; i < ARRAY_SIZE(tabla_dapm_aif_out_widgets); i++) {
+ list_for_each_entry(ch, &tabla->dai[dai->id].wcd9xxx_ch_list, list) {
- if (strncmp(dai->driver->capture.stream_name, w[i].sname, 12))
- continue;
-
- tx_port = w[i].shift;
+ tx_port = ch->port + 1;
+ pr_debug("%s: dai->id = %d, tx_port = %d",
+ __func__, dai->id, tx_port);
if ((tx_port < 1) || (tx_port > NUM_DECIMATORS)) {
- pr_err("%s: Invalid SLIM TX%u port. widget = %s\n",
- __func__, tx_port, w[i].name);
+ pr_err("%s: Invalid SLIM TX%u port. DAI ID is %d\n",
+ __func__, tx_port, dai->id);
return -EINVAL;
}
@@ -4204,38 +4441,38 @@
if (decimator) { /* SLIM_TX port has a DEC as input */
tx_fs_reg = TABLA_A_CDC_TX1_CLK_FS_CTL +
- 8 * (decimator - 1);
+ 8 * (decimator - 1);
pr_debug("%s: set DEC%u (-> SLIM_TX%u) rate to %u\n",
__func__, decimator, tx_port, sample_rate);
snd_soc_update_bits(codec, tx_fs_reg, 0x07,
- tx_fs_rate_reg_val);
+ tx_fs_rate_reg_val);
} else {
if ((tx_port_reg_val >= 0x1) &&
- (tx_port_reg_val <= 0x7)) {
+ (tx_port_reg_val <= 0x7)) {
pr_debug("%s: RMIX%u going to SLIM TX%u\n",
__func__, tx_port_reg_val, tx_port);
} else if ((tx_port_reg_val >= 0x8) &&
- (tx_port_reg_val <= 0x11)) {
+ (tx_port_reg_val <= 0x11)) {
pr_err("%s: ERROR: Should not be here\n",
- __func__);
- pr_err("%s: ERROR: DEC connected to SLIM TX%u\n"
- , __func__, tx_port);
+ __func__);
+ pr_err("%s: ERROR: DEC connected to SLIM TX%u\n",
+ __func__, tx_port);
return -EINVAL;
} else if (tx_port_reg_val == 0) {
pr_debug("%s: no signal to SLIM TX%u\n",
- __func__, tx_port);
+ __func__, tx_port);
} else {
- pr_err("%s: ERROR: wrong signal to SLIM TX%u\n"
- , __func__, tx_port);
- pr_err("%s: ERROR: wrong signal = %u\n"
- , __func__, tx_port_reg_val);
+ pr_err("%s: ERROR: wrong signal to SLIM TX%u\n",
+ __func__, tx_port);
+ pr_err("%s: ERROR: wrong signal = %u\n",
+ __func__, tx_port_reg_val);
return -EINVAL;
}
}
@@ -4244,8 +4481,8 @@
}
static int tabla_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(dai->codec);
@@ -4254,8 +4491,8 @@
int ret;
pr_debug("%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__,
- dai->name, dai->id, params_rate(params),
- params_channels(params));
+ dai->name, dai->id, params_rate(params),
+ params_channels(params));
switch (params_rate(params)) {
case 8000:
@@ -4290,7 +4527,7 @@
break;
default:
pr_err("%s: Invalid sampling rate %d\n", __func__,
- params_rate(params));
+ params_rate(params));
return -EINVAL;
}
@@ -4298,10 +4535,10 @@
case SNDRV_PCM_STREAM_CAPTURE:
ret = tabla_set_decimator_rate(dai, tx_fs_rate_reg_val,
- params_rate(params));
+ params_rate(params));
if (ret < 0) {
pr_err("%s: set decimator rate failed %d\n", __func__,
- ret);
+ ret);
return ret;
}
@@ -4316,24 +4553,34 @@
TABLA_A_CDC_CLK_TX_I2S_CTL, 0x20, 0x00);
break;
default:
- pr_err("%s: invalid TX format %u\n", __func__,
- params_format(params));
+ pr_err("%s: Invalid format %d\n", __func__,
+ params_format(params));
return -EINVAL;
}
snd_soc_update_bits(codec, TABLA_A_CDC_CLK_TX_I2S_CTL,
- 0x07, tx_fs_rate_reg_val);
+ 0x07, tx_fs_rate_reg_val);
} else {
- tabla->dai[dai->id - 1].rate = params_rate(params);
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ tabla->dai[dai->id].bit_width = 16;
+ break;
+ default:
+ pr_err("%s: Invalid TX format %d\n", __func__,
+ params_format(params));
+ return -EINVAL;
+ }
+ tabla->dai[dai->id].rate = params_rate(params);
}
break;
case SNDRV_PCM_STREAM_PLAYBACK:
ret = tabla_set_interpolator_rate(dai, rx_fs_rate_reg_val,
- compander_fs, params_rate(params));
+ compander_fs,
+ params_rate(params));
if (ret < 0) {
pr_err("%s: set decimator rate failed %d\n", __func__,
- ret);
+ ret);
return ret;
}
@@ -4348,20 +4595,29 @@
TABLA_A_CDC_CLK_RX_I2S_CTL, 0x20, 0x00);
break;
default:
- pr_err("%s: invalid RX format %u\n", __func__,
- params_format(params));
+ pr_err("%s: Invalid RX format %d\n", __func__,
+ params_format(params));
return -EINVAL;
}
snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_I2S_CTL,
0x03, (rx_fs_rate_reg_val >> 0x05));
} else {
- tabla->dai[dai->id - 1].rate = params_rate(params);
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ tabla->dai[dai->id].bit_width = 16;
+ break;
+ default:
+ pr_err("%s: Invalid format %d\n", __func__,
+ params_format(params));
+ return -EINVAL;
+ }
+ tabla->dai[dai->id].rate = params_rate(params);
}
break;
default:
pr_err("%s: Invalid stream type %d\n", __func__,
- substream->stream);
+ substream->stream);
return -EINVAL;
}
return 0;
@@ -4467,7 +4723,7 @@
static struct snd_soc_dai_driver tabla_i2s_dai[] = {
{
.name = "tabla_i2s_rx1",
- .id = 1,
+ .id = AIF1_PB,
.playback = {
.stream_name = "AIF1 Playback",
.rates = WCD9310_RATES,
@@ -4481,7 +4737,7 @@
},
{
.name = "tabla_i2s_tx1",
- .id = 2,
+ .id = AIF1_CAP,
.capture = {
.stream_name = "AIF1 Capture",
.rates = WCD9310_RATES,
@@ -4496,15 +4752,16 @@
};
static int tabla_codec_enable_chmask(struct tabla_priv *tabla_p,
- int event, int index)
+ int event, int index)
{
int ret = 0;
- u32 k = 0;
+ struct wcd9xxx_ch *ch;
+
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- for (k = 0; k < tabla_p->dai[index].ch_tot; k++) {
- ret = wcd9xxx_get_slave_port(
- tabla_p->dai[index].ch_num[k]);
+ list_for_each_entry(ch,
+ &tabla_p->dai[index].wcd9xxx_ch_list, list) {
+ ret = wcd9xxx_get_slave_port(ch->ch_num);
if (ret < 0) {
pr_err("%s: Invalid slave port ID: %d\n",
__func__, ret);
@@ -4513,7 +4770,6 @@
}
tabla_p->dai[index].ch_mask |= 1 << ret;
}
- ret = 0;
break;
case SND_SOC_DAPM_POST_PMD:
ret = wait_event_timeout(tabla_p->dai[index].dai_wait,
@@ -4523,191 +4779,134 @@
pr_err("%s: Slim close tx/rx wait timeout\n",
__func__);
ret = -EINVAL;
- } else
- ret = 0;
+ }
break;
}
return ret;
}
static int tabla_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol,
+ int event)
{
- struct wcd9xxx *tabla;
+ struct wcd9xxx *core;
struct snd_soc_codec *codec = w->codec;
struct tabla_priv *tabla_p = snd_soc_codec_get_drvdata(codec);
- u32 j = 0;
- int ret = 0;
- codec->control_data = dev_get_drvdata(codec->dev->parent);
- tabla = codec->control_data;
+ u32 ret = 0;
+ struct wcd9xxx_codec_dai_data *dai;
+
+ core = dev_get_drvdata(codec->dev->parent);
+
+ pr_debug("%s: event called! codec name %s num_dai %d\n"
+ "stream name %s event %d\n",
+ __func__, w->codec->name, w->codec->num_dai,
+ w->sname, event);
/* Execute the callback only if interface type is slimbus */
if (tabla_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
- if (event == SND_SOC_DAPM_POST_PMD && (tabla != NULL) &&
- (tabla->dev != NULL) &&
- (tabla->dev->parent != NULL)) {
- pm_runtime_mark_last_busy(tabla->dev->parent);
- pm_runtime_put(tabla->dev->parent);
+ if (event == SND_SOC_DAPM_POST_PMD && (core != NULL) &&
+ (core->dev != NULL) &&
+ (core->dev->parent != NULL)) {
+ pm_runtime_mark_last_busy(core->dev->parent);
+ pm_runtime_put(core->dev->parent);
}
return 0;
}
-
- pr_debug("%s: %s %d\n", __func__, w->name, event);
+ pr_debug("%s: w->name %s w->shift %d event %d\n",
+ __func__, w->name, w->shift, event);
+ dai = &tabla_p->dai[w->shift];
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- for (j = 0; j < ARRAY_SIZE(tabla_dai); j++) {
- if ((tabla_dai[j].id == AIF1_CAP) ||
- (tabla_dai[j].id == AIF2_CAP) ||
- (tabla_dai[j].id == AIF3_CAP))
- continue;
- if (!strncmp(w->sname,
- tabla_dai[j].playback.stream_name, 13)) {
- ++tabla_p->dai[j].ch_act;
- break;
- }
- }
- if (tabla_p->dai[j].ch_act == tabla_p->dai[j].ch_tot) {
- ret = tabla_codec_enable_chmask(tabla_p,
- SND_SOC_DAPM_POST_PMU,
- j);
- ret = wcd9xxx_cfg_slim_sch_rx(tabla,
- tabla_p->dai[j].ch_num,
- tabla_p->dai[j].ch_tot,
- tabla_p->dai[j].rate);
- }
+ ret = tabla_codec_enable_chmask(tabla_p, SND_SOC_DAPM_POST_PMU,
+ w->shift);
+ ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+ dai->rate, dai->bit_width,
+ &dai->grph);
break;
case SND_SOC_DAPM_POST_PMD:
- for (j = 0; j < ARRAY_SIZE(tabla_dai); j++) {
- if ((tabla_dai[j].id == AIF1_CAP) ||
- (tabla_dai[j].id == AIF2_CAP) ||
- (tabla_dai[j].id == AIF3_CAP))
- continue;
- if (!strncmp(w->sname,
- tabla_dai[j].playback.stream_name, 13)) {
- if (tabla_p->dai[j].ch_act)
- --tabla_p->dai[j].ch_act;
- break;
- }
+ ret = wcd9xxx_close_slim_sch_rx(core,
+ &dai->wcd9xxx_ch_list,
+ dai->grph);
+ ret = tabla_codec_enable_chmask(tabla_p, SND_SOC_DAPM_POST_PMD,
+ w->shift);
+ if (ret < 0) {
+ ret = wcd9xxx_disconnect_port(core,
+ &dai->wcd9xxx_ch_list,
+ dai->grph);
+ pr_info("%s: Disconnect RX port, ret = %d\n",
+ __func__, ret);
}
- if (!tabla_p->dai[j].ch_act) {
- ret = wcd9xxx_close_slim_sch_rx(tabla,
- tabla_p->dai[j].ch_num,
- tabla_p->dai[j].ch_tot);
- ret = tabla_codec_enable_chmask(tabla_p,
- SND_SOC_DAPM_POST_PMD,
- j);
- if (ret < 0) {
- ret = wcd9xxx_disconnect_port(tabla,
- tabla_p->dai[j].ch_num,
- tabla_p->dai[j].ch_tot,
- 1);
- pr_info("%s: Disconnect RX port ret = %d\n",
- __func__, ret);
- }
- tabla_p->dai[j].rate = 0;
- memset(tabla_p->dai[j].ch_num, 0, (sizeof(u32)*
- tabla_p->dai[j].ch_tot));
- tabla_p->dai[j].ch_tot = 0;
-
- if ((tabla != NULL) &&
- (tabla->dev != NULL) &&
- (tabla->dev->parent != NULL)) {
- pm_runtime_mark_last_busy(tabla->dev->parent);
- pm_runtime_put(tabla->dev->parent);
- }
+ if ((core != NULL) &&
+ (core->dev != NULL) &&
+ (core->dev->parent != NULL)) {
+ pm_runtime_mark_last_busy(core->dev->parent);
+ pm_runtime_put(core->dev->parent);
}
+ break;
}
+
return ret;
}
static int tabla_codec_enable_slimtx(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol,
+ int event)
{
- struct wcd9xxx *tabla;
+ struct wcd9xxx *core;
struct snd_soc_codec *codec = w->codec;
struct tabla_priv *tabla_p = snd_soc_codec_get_drvdata(codec);
- /* index to the DAI ID, for now hardcoding */
- u32 j = 0;
- int ret = 0;
+ u32 ret = 0;
+ struct wcd9xxx_codec_dai_data *dai;
- codec->control_data = dev_get_drvdata(codec->dev->parent);
- tabla = codec->control_data;
+ core = dev_get_drvdata(codec->dev->parent);
+
+ pr_debug("%s: event called! codec name %s num_dai %d\n"
+ "stream name %s\n", __func__, w->codec->name,
+ w->codec->num_dai, w->sname);
/* Execute the callback only if interface type is slimbus */
if (tabla_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
- if (event == SND_SOC_DAPM_POST_PMD && (tabla != NULL) &&
- (tabla->dev != NULL) &&
- (tabla->dev->parent != NULL)) {
- pm_runtime_mark_last_busy(tabla->dev->parent);
- pm_runtime_put(tabla->dev->parent);
+ if (event == SND_SOC_DAPM_POST_PMD && (core != NULL) &&
+ (core->dev != NULL) &&
+ (core->dev->parent != NULL)) {
+ pm_runtime_mark_last_busy(core->dev->parent);
+ pm_runtime_put(core->dev->parent);
}
return 0;
}
pr_debug("%s(): %s %d\n", __func__, w->name, event);
+ dai = &tabla_p->dai[w->shift];
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- for (j = 0; j < ARRAY_SIZE(tabla_dai); j++) {
- if (tabla_dai[j].id == AIF1_PB ||
- tabla_dai[j].id == AIF2_PB ||
- tabla_dai[j].id == AIF3_PB)
- continue;
- if (!strncmp(w->sname,
- tabla_dai[j].capture.stream_name, 13)) {
- ++tabla_p->dai[j].ch_act;
- break;
- }
- }
- if (tabla_p->dai[j].ch_act == tabla_p->dai[j].ch_tot) {
- ret = tabla_codec_enable_chmask(tabla_p,
- SND_SOC_DAPM_POST_PMU,
- j);
- ret = wcd9xxx_cfg_slim_sch_tx(tabla,
- tabla_p->dai[j].ch_num,
- tabla_p->dai[j].ch_tot,
- tabla_p->dai[j].rate);
- }
+ ret = tabla_codec_enable_chmask(tabla_p, SND_SOC_DAPM_POST_PMU,
+ w->shift);
+ ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+ dai->rate,
+ dai->bit_width,
+ &dai->grph);
break;
case SND_SOC_DAPM_POST_PMD:
- for (j = 0; j < ARRAY_SIZE(tabla_dai); j++) {
- if (tabla_dai[j].id == AIF1_PB ||
- tabla_dai[j].id == AIF2_PB ||
- tabla_dai[j].id == AIF3_PB)
- continue;
- if (!strncmp(w->sname,
- tabla_dai[j].capture.stream_name, 13)) {
- --tabla_p->dai[j].ch_act;
- break;
- }
+ ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+ dai->grph);
+ ret = tabla_codec_enable_chmask(tabla_p, SND_SOC_DAPM_POST_PMD,
+ w->shift);
+ if (ret < 0) {
+ ret = wcd9xxx_disconnect_port(core,
+ &dai->wcd9xxx_ch_list,
+ dai->grph);
+ pr_info("%s: Disconnect TX port, ret = %d\n",
+ __func__, ret);
}
- if (!tabla_p->dai[j].ch_act) {
- ret = wcd9xxx_close_slim_sch_tx(tabla,
- tabla_p->dai[j].ch_num,
- tabla_p->dai[j].ch_tot);
- ret = tabla_codec_enable_chmask(tabla_p,
- SND_SOC_DAPM_POST_PMD,
- j);
- if (ret < 0) {
- ret = wcd9xxx_disconnect_port(tabla,
- tabla_p->dai[j].ch_num,
- tabla_p->dai[j].ch_tot, 0);
- pr_info("%s: Disconnect TX port, ret = %d\n",
- __func__, ret);
- }
-
- tabla_p->dai[j].rate = 0;
- memset(tabla_p->dai[j].ch_num, 0, (sizeof(u32)*
- tabla_p->dai[j].ch_tot));
- tabla_p->dai[j].ch_tot = 0;
- if ((tabla != NULL) &&
- (tabla->dev != NULL) &&
- (tabla->dev->parent != NULL)) {
- pm_runtime_mark_last_busy(tabla->dev->parent);
- pm_runtime_put(tabla->dev->parent);
- }
+ if ((core != NULL) &&
+ (core->dev != NULL) &&
+ (core->dev->parent != NULL)) {
+ pm_runtime_mark_last_busy(core->dev->parent);
+ pm_runtime_put(core->dev->parent);
}
+ break;
}
return ret;
}
@@ -4726,6 +4925,38 @@
SND_SOC_DAPM_MIXER("DAC1", SND_SOC_NOPM, 0, 0, dac1_switch,
ARRAY_SIZE(dac1_switch)),
+ SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
+ AIF1_PB, 0, tabla_codec_enable_slimrx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM,
+ AIF2_PB, 0, tabla_codec_enable_slimrx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM,
+ AIF3_PB, 0, tabla_codec_enable_slimrx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, TABLA_RX1, 0,
+ &slim_rx_mux[TABLA_RX1]),
+ SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, TABLA_RX2, 0,
+ &slim_rx_mux[TABLA_RX2]),
+ SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, TABLA_RX3, 0,
+ &slim_rx_mux[TABLA_RX3]),
+ SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, TABLA_RX4, 0,
+ &slim_rx_mux[TABLA_RX4]),
+ SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, TABLA_RX5, 0,
+ &slim_rx_mux[TABLA_RX5]),
+ SND_SOC_DAPM_MUX("SLIM RX6 MUX", SND_SOC_NOPM, TABLA_RX6, 0,
+ &slim_rx_mux[TABLA_RX6]),
+ SND_SOC_DAPM_MUX("SLIM RX7 MUX", SND_SOC_NOPM, TABLA_RX7, 0,
+ &slim_rx_mux[TABLA_RX7]),
+
+ SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0),
/* Headphone */
SND_SOC_DAPM_OUTPUT("HEADPHONE"),
SND_SOC_DAPM_PGA_E("HPHL", TABLA_A_RX_HPH_CNP_EN, 5, 0, NULL, 0,
@@ -5005,16 +5236,47 @@
tabla_codec_enable_adc, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, 0, 0, &sb_tx1_mux),
- SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, 0, 0, &sb_tx2_mux),
- SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, 0, 0, &sb_tx3_mux),
- SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, 0, 0, &sb_tx4_mux),
- SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, 0, 0, &sb_tx5_mux),
- SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, 0, 0, &sb_tx6_mux),
- SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, 0, 0, &sb_tx7_mux),
- SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, 0, 0, &sb_tx8_mux),
- SND_SOC_DAPM_MUX("SLIM TX9 MUX", SND_SOC_NOPM, 0, 0, &sb_tx9_mux),
- SND_SOC_DAPM_MUX("SLIM TX10 MUX", SND_SOC_NOPM, 0, 0, &sb_tx10_mux),
+ SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM,
+ AIF1_CAP, 0, tabla_codec_enable_slimtx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM,
+ AIF2_CAP, 0, tabla_codec_enable_slimtx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM,
+ AIF3_CAP, 0, tabla_codec_enable_slimtx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0,
+ aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
+
+ SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0,
+ aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
+
+ SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0,
+ aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
+
+ SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, TABLA_TX1, 0,
+ &sb_tx1_mux),
+ SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, TABLA_TX2, 0,
+ &sb_tx2_mux),
+ SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, TABLA_TX3, 0,
+ &sb_tx3_mux),
+ SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, TABLA_TX4, 0,
+ &sb_tx4_mux),
+ SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, TABLA_TX5, 0,
+ &sb_tx5_mux),
+ SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, TABLA_TX6, 0,
+ &sb_tx6_mux),
+ SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, TABLA_TX7, 0,
+ &sb_tx7_mux),
+ SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, TABLA_TX8, 0,
+ &sb_tx8_mux),
+ SND_SOC_DAPM_MUX("SLIM TX9 MUX", SND_SOC_NOPM, TABLA_TX9, 0,
+ &sb_tx9_mux),
+ SND_SOC_DAPM_MUX("SLIM TX10 MUX", SND_SOC_NOPM, TABLA_TX10, 0,
+ &sb_tx10_mux),
/* Digital Mic Inputs */
SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
@@ -5114,7 +5376,7 @@
short bias_value;
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
if (noreldetection)
tabla_turn_onoff_rel_detection(codec, false);
@@ -5150,7 +5412,7 @@
if (noreldetection)
tabla_turn_onoff_rel_detection(codec, true);
- wcd9xxx_enable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
return bias_value;
}
@@ -5339,9 +5601,9 @@
}
tabla_set_and_turnoff_hph_padac(codec);
hphocp_off_report(tabla, SND_JACK_OC_HPHR,
- TABLA_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
hphocp_off_report(tabla, SND_JACK_OC_HPHL,
- TABLA_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
tabla->current_plug = PLUG_TYPE_NONE;
tabla->mbhc_polling_active = false;
} else {
@@ -5504,7 +5766,7 @@
snd_soc_update_bits(codec, tabla->reg_addr.micb_4_mbhc, 0x3,
tabla->mbhc_cfg.micbias);
- wcd9xxx_enable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
+ wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
pr_debug("%s: leave\n", __func__);
return 0;
@@ -5634,7 +5896,7 @@
tabla = snd_soc_codec_get_drvdata(codec);
calibration = tabla->mbhc_cfg.calibration;
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
tabla_turn_onoff_rel_detection(codec, false);
/* First compute the DCE / STA wait times
@@ -5740,7 +6002,7 @@
snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x84);
usleep_range(100, 100);
- wcd9xxx_enable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
tabla_turn_onoff_rel_detection(codec, true);
}
@@ -6328,7 +6590,7 @@
0x10);
} else {
wcd9xxx_disable_irq(codec->control_data,
- TABLA_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
tabla->hph_status |= SND_JACK_OC_HPHL;
if (tabla->mbhc_cfg.headset_jack)
tabla_snd_soc_jack_report(tabla,
@@ -6362,7 +6624,7 @@
0x10);
} else {
wcd9xxx_disable_irq(codec->control_data,
- TABLA_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
tabla->hph_status |= SND_JACK_OC_HPHR;
if (tabla->mbhc_cfg.headset_jack)
tabla_snd_soc_jack_report(tabla,
@@ -6971,7 +7233,7 @@
wcd9xxx_unlock_sleep(core);
} else {
wcd9xxx_enable_irq(codec->control_data,
- TABLA_IRQ_MBHC_INSERTION);
+ WCD9XXX_IRQ_MBHC_INSERTION);
pr_err("%s: Error detecting plug insertion\n",
__func__);
}
@@ -7009,7 +7271,7 @@
pr_debug("%s: enter\n", __func__);
TABLA_ACQUIRE_LOCK(priv->codec_resource_lock);
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
is_mb_trigger = !!(snd_soc_read(codec, priv->mbhc_bias_regs.mbhc_reg) &
0x10);
@@ -7256,7 +7518,8 @@
snd_soc_update_bits(codec, tabla->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x13, 0x00);
snd_soc_update_bits(codec, tabla->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
- wcd9xxx_disable_irq_sync(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
+ wcd9xxx_disable_irq_sync(codec->control_data,
+ WCD9XXX_IRQ_MBHC_INSERTION);
tabla_codec_detect_plug_type(codec);
wcd9xxx_unlock_sleep(tabla_core);
}
@@ -7352,9 +7615,18 @@
{
int r = IRQ_HANDLED;
struct snd_soc_codec *codec = data;
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
if (unlikely(wcd9xxx_lock_sleep(codec->control_data) == false)) {
pr_warn("%s: failed to hold suspend\n", __func__);
+ /*
+ * Give up this IRQ for now and resend this IRQ so IRQ can be
+ * handled after system resume
+ */
+ TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
+ tabla->gpio_irq_resend = true;
+ TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
+ wake_lock_timeout(&tabla->irq_resend_wlock, HZ);
r = IRQ_NONE;
} else {
tabla_hs_gpio_handler(codec);
@@ -7470,9 +7742,9 @@
if (!IS_ERR_VALUE(ret)) {
snd_soc_update_bits(codec, TABLA_A_RX_HPH_OCP_CTL, 0x10, 0x10);
wcd9xxx_enable_irq(codec->control_data,
- TABLA_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
wcd9xxx_enable_irq(codec->control_data,
- TABLA_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
if (tabla->mbhc_cfg.gpio) {
ret = request_threaded_irq(tabla->mbhc_cfg.gpio_irq,
@@ -7590,7 +7862,6 @@
int i, j, port_id, k, ch_mask_temp;
unsigned long slimbus_value;
u8 val;
-
for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++) {
slimbus_value = wcd9xxx_interface_reg_read(codec->control_data,
TABLA_SLIM_PGD_PORT_INT_STATUS0 + i);
@@ -7604,17 +7875,18 @@
pr_err_ratelimited("underflow error on port %x,"
" value %x\n", i*8 + j, val);
if (val & 0x4) {
- pr_debug("%s: port %x disconnect value %x\n",
- __func__, i*8 + j, val);
port_id = i*8 + j;
for (k = 0; k < ARRAY_SIZE(tabla_dai); k++) {
ch_mask_temp = 1 << port_id;
+ pr_debug("%s: tabla_p->dai[%d].ch_mask = 0x%x\n",
+ __func__, k,
+ tabla_p->dai[k].ch_mask);
if (ch_mask_temp &
tabla_p->dai[k].ch_mask) {
tabla_p->dai[k].ch_mask &=
- ~ch_mask_temp;
+ ~ch_mask_temp;
if (!tabla_p->dai[k].ch_mask)
- wake_up(
+ wake_up(
&tabla_p->dai[k].dai_wait);
}
}
@@ -8096,7 +8368,7 @@
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret = 0;
int i;
- int ch_cnt;
+ void *ptr = NULL;
codec->control_data = dev_get_drvdata(codec->dev->parent);
control = codec->control_data;
@@ -8156,8 +8428,6 @@
goto err_pdata;
}
-// snd_soc_add_codec_controls(codec, tabla_snd_controls,
-// ARRAY_SIZE(tabla_snd_controls));
if (TABLA_IS_1_X(control->version))
snd_soc_add_codec_controls(codec, tabla_1_x_snd_controls,
ARRAY_SIZE(tabla_1_x_snd_controls));
@@ -8165,15 +8435,6 @@
snd_soc_add_codec_controls(codec, tabla_2_higher_snd_controls,
ARRAY_SIZE(tabla_2_higher_snd_controls));
-// snd_soc_dapm_new_controls(dapm, tabla_dapm_widgets,
-// ARRAY_SIZE(tabla_dapm_widgets));
-
- snd_soc_dapm_new_controls(dapm, tabla_dapm_aif_in_widgets,
- ARRAY_SIZE(tabla_dapm_aif_in_widgets));
-
- snd_soc_dapm_new_controls(dapm, tabla_dapm_aif_out_widgets,
- ARRAY_SIZE(tabla_dapm_aif_out_widgets));
-
if (TABLA_IS_1_X(control->version))
snd_soc_dapm_new_controls(dapm, tabla_1_x_dapm_widgets,
ARRAY_SIZE(tabla_1_x_dapm_widgets));
@@ -8181,13 +8442,35 @@
snd_soc_dapm_new_controls(dapm, tabla_2_higher_dapm_widgets,
ARRAY_SIZE(tabla_2_higher_dapm_widgets));
+
+ ptr = kmalloc((sizeof(tabla_rx_chs) +
+ sizeof(tabla_tx_chs)), GFP_KERNEL);
+ if (!ptr) {
+ pr_err("%s: no mem for slim chan ctl data\n", __func__);
+ ret = -ENOMEM;
+ goto err_nomem_slimch;
+ }
if (tabla->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
snd_soc_dapm_new_controls(dapm, tabla_dapm_i2s_widgets,
ARRAY_SIZE(tabla_dapm_i2s_widgets));
snd_soc_dapm_add_routes(dapm, audio_i2s_map,
ARRAY_SIZE(audio_i2s_map));
+ for (i = 0; i < ARRAY_SIZE(tabla_i2s_dai); i++)
+ INIT_LIST_HEAD(&tabla->dai[i].wcd9xxx_ch_list);
+ } else if (tabla->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ for (i = 0; i < NUM_CODEC_DAIS; i++) {
+ INIT_LIST_HEAD(&tabla->dai[i].wcd9xxx_ch_list);
+ init_waitqueue_head(&tabla->dai[i].dai_wait);
+ }
}
-// snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
+
+ control->num_rx_port = TABLA_RX_MAX;
+ control->rx_chs = ptr;
+ memcpy(control->rx_chs, tabla_rx_chs, sizeof(tabla_rx_chs));
+ control->num_tx_port = TABLA_TX_MAX;
+ control->tx_chs = ptr + sizeof(tabla_rx_chs);
+ memcpy(control->tx_chs, tabla_tx_chs, sizeof(tabla_tx_chs));
+
if (TABLA_IS_1_X(control->version)) {
snd_soc_dapm_add_routes(dapm, tabla_1_x_lineout_2_to_4_map,
@@ -8203,44 +8486,50 @@
snd_soc_dapm_sync(dapm);
- ret = wcd9xxx_request_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION,
+ ret = wcd9xxx_request_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_INSERTION,
tabla_hs_insert_irq, "Headset insert detect", tabla);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TABLA_IRQ_MBHC_INSERTION);
+ WCD9XXX_IRQ_MBHC_INSERTION);
goto err_insert_irq;
}
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
- ret = wcd9xxx_request_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL,
- tabla_hs_remove_irq, "Headset remove detect", tabla);
+ ret = wcd9xxx_request_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_REMOVAL,
+ tabla_hs_remove_irq,
+ "Headset remove detect", tabla);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TABLA_IRQ_MBHC_REMOVAL);
+ WCD9XXX_IRQ_MBHC_REMOVAL);
goto err_remove_irq;
}
- ret = wcd9xxx_request_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL,
- tabla_dce_handler, "DC Estimation detect", tabla);
+ ret = wcd9xxx_request_irq(codec->control_data,
+ WCD9XXX_IRQ_MBHC_POTENTIAL,
+ tabla_dce_handler, "DC Estimation detect",
+ tabla);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TABLA_IRQ_MBHC_POTENTIAL);
+ WCD9XXX_IRQ_MBHC_POTENTIAL);
goto err_potential_irq;
}
- ret = wcd9xxx_request_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE,
- tabla_release_handler, "Button Release detect", tabla);
+ ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE,
+ tabla_release_handler,
+ "Button Release detect", tabla);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TABLA_IRQ_MBHC_RELEASE);
+ WCD9XXX_IRQ_MBHC_RELEASE);
goto err_release_irq;
}
- ret = wcd9xxx_request_irq(codec->control_data, TABLA_IRQ_SLIMBUS,
- tabla_slimbus_irq, "SLIMBUS Slave", tabla);
+ ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS,
+ tabla_slimbus_irq, "SLIMBUS Slave", tabla);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TABLA_IRQ_SLIMBUS);
+ WCD9XXX_IRQ_SLIMBUS);
goto err_slimbus_irq;
}
@@ -8249,51 +8538,35 @@
TABLA_SLIM_PGD_PORT_INT_EN0 + i, 0xFF);
ret = wcd9xxx_request_irq(codec->control_data,
- TABLA_IRQ_HPH_PA_OCPL_FAULT, tabla_hphl_ocp_irq,
- "HPH_L OCP detect", tabla);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+ tabla_hphl_ocp_irq,
+ "HPH_L OCP detect", tabla);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TABLA_IRQ_HPH_PA_OCPL_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
goto err_hphl_ocp_irq;
}
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_HPH_PA_OCPL_FAULT);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
ret = wcd9xxx_request_irq(codec->control_data,
- TABLA_IRQ_HPH_PA_OCPR_FAULT, tabla_hphr_ocp_irq,
- "HPH_R OCP detect", tabla);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
+ tabla_hphr_ocp_irq,
+ "HPH_R OCP detect", tabla);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TABLA_IRQ_HPH_PA_OCPR_FAULT);
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
goto err_hphr_ocp_irq;
}
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_HPH_PA_OCPR_FAULT);
- for (i = 0; i < ARRAY_SIZE(tabla_dai); i++) {
- switch (tabla_dai[i].id) {
- case AIF1_PB:
- ch_cnt = tabla_dai[i].playback.channels_max;
- break;
- case AIF1_CAP:
- ch_cnt = tabla_dai[i].capture.channels_max;
- break;
- case AIF2_PB:
- ch_cnt = tabla_dai[i].playback.channels_max;
- break;
- case AIF2_CAP:
- ch_cnt = tabla_dai[i].capture.channels_max;
- break;
- case AIF3_PB:
- ch_cnt = tabla_dai[i].playback.channels_max;
- break;
- case AIF3_CAP:
- ch_cnt = tabla_dai[i].capture.channels_max;
- break;
- default:
- continue;
- }
- tabla->dai[i].ch_num = kzalloc((sizeof(unsigned int)*
- ch_cnt), GFP_KERNEL);
- init_waitqueue_head(&tabla->dai[i].dai_wait);
- }
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+
+ /*
+ * Register suspend lock and notifier to resend edge triggered
+ * gpio IRQs
+ */
+ wake_lock_init(&tabla->irq_resend_wlock, WAKE_LOCK_SUSPEND,
+ "tabla_gpio_irq_resend");
+ tabla->gpio_irq_resend = false;
+
#ifdef CONFIG_DEBUG_FS
if (ret == 0) {
@@ -8310,40 +8583,46 @@
err_hphr_ocp_irq:
wcd9xxx_free_irq(codec->control_data,
- TABLA_IRQ_HPH_PA_OCPL_FAULT, tabla);
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, tabla);
err_hphl_ocp_irq:
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_SLIMBUS, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, tabla);
err_slimbus_irq:
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, tabla);
err_release_irq:
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL,
+ tabla);
err_potential_irq:
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, tabla);
err_remove_irq:
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION,
+ tabla);
err_insert_irq:
err_pdata:
+ kfree(ptr);
+err_nomem_slimch:
mutex_destroy(&tabla->codec_resource_lock);
kfree(tabla);
return ret;
}
static int tabla_codec_remove(struct snd_soc_codec *codec)
{
- int i;
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_SLIMBUS, tabla);
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE, tabla);
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL, tabla);
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL, tabla);
- wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION, tabla);
+
+ wake_lock_destroy(&tabla->irq_resend_wlock);
+
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL,
+ tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, tabla);
+ wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION,
+ tabla);
TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
tabla_codec_disable_clock_block(codec);
TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
tabla_codec_enable_bandgap(codec, TABLA_BANDGAP_OFF);
if (tabla->mbhc_fw)
release_firmware(tabla->mbhc_fw);
- for (i = 0; i < ARRAY_SIZE(tabla_dai); i++)
- kfree(tabla->dai[i].ch_num);
mutex_destroy(&tabla->codec_resource_lock);
#ifdef CONFIG_DEBUG_FS
debugfs_remove(tabla->debugfs_poke);
@@ -8380,11 +8659,29 @@
static int tabla_resume(struct device *dev)
{
+ int irq;
struct platform_device *pdev = to_platform_device(dev);
struct tabla_priv *tabla = platform_get_drvdata(pdev);
+
dev_dbg(dev, "%s: system resume tabla %p\n", __func__, tabla);
- if (tabla)
+ if (tabla) {
+ TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
tabla->mbhc_last_resume = jiffies;
+ if (tabla->gpio_irq_resend) {
+ WARN_ON(!tabla->mbhc_cfg.gpio_irq);
+ tabla->gpio_irq_resend = false;
+
+ irq = tabla->mbhc_cfg.gpio_irq;
+ pr_debug("%s: Resending GPIO IRQ %d\n", __func__, irq);
+ irq_set_pending(irq);
+ check_irq_resend(irq_to_desc(irq), irq);
+
+ /* release suspend lock */
+ wake_unlock(&tabla->irq_resend_wlock);
+ }
+ TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
+ }
+
return 0;
}
diff --git a/sound/soc/codecs/wcd9310.h b/sound/soc/codecs/wcd9310.h
index 4c9f8b4..98c1835 100644
--- a/sound/soc/codecs/wcd9310.h
+++ b/sound/soc/codecs/wcd9310.h
@@ -252,3 +252,29 @@
sizeof(cfg_ptr->_alpha[0]))))
+/* Number of input and output Slimbus port */
+enum {
+ TABLA_RX1 = 0,
+ TABLA_RX2,
+ TABLA_RX3,
+ TABLA_RX4,
+ TABLA_RX5,
+ TABLA_RX6,
+ TABLA_RX7,
+ TABLA_RX_MAX,
+};
+
+enum {
+ TABLA_TX1 = 0,
+ TABLA_TX2,
+ TABLA_TX3,
+ TABLA_TX4,
+ TABLA_TX5,
+ TABLA_TX6,
+ TABLA_TX7,
+ TABLA_TX8,
+ TABLA_TX9,
+ TABLA_TX10,
+ TABLA_TX_MAX,
+};
+
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index e8bb652..886e4d3 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -33,81 +33,46 @@
#include <linux/kernel.h>
#include <linux/gpio.h>
#include "wcd9320.h"
+#include "wcd9xxx-resmgr.h"
#define WCD9320_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
-
#define NUM_DECIMATORS 10
#define NUM_INTERPOLATORS 7
#define BITS_PER_REG 8
-#define TAIKO_CFILT_FAST_MODE 0x00
-#define TAIKO_CFILT_SLOW_MODE 0x40
-#define MBHC_FW_READ_ATTEMPTS 15
-#define MBHC_FW_READ_TIMEOUT 2000000
-
-enum {
- MBHC_USE_HPHL_TRIGGER = 1,
- MBHC_USE_MB_TRIGGER = 2
-};
-
-#define MBHC_NUM_DCE_PLUG_DETECT 3
-#define NUM_ATTEMPTS_INSERT_DETECT 25
-#define NUM_ATTEMPTS_TO_REPORT 5
-
-#define TAIKO_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \
- SND_JACK_OC_HPHR | SND_JACK_UNSUPPORTED)
+#define TAIKO_TX_PORT_NUMBER 16
#define TAIKO_I2S_MASTER_MODE_MASK 0x08
-#define TAIKO_OCP_ATTEMPT 1
-
-#define AIF1_PB 1
-#define AIF1_CAP 2
-#define AIF2_PB 3
-#define AIF2_CAP 4
-#define AIF3_CAP 5
-#define AIF3_PB 6
-
-#define NUM_CODEC_DAIS 6
-#define TAIKO_COMP_DIGITAL_GAIN_OFFSET 3
-
-struct taiko_codec_dai_data {
- u32 rate;
- u32 *ch_num;
- u32 ch_act;
- u32 ch_tot;
+enum {
+ AIF1_PB = 0,
+ AIF1_CAP,
+ AIF2_PB,
+ AIF2_CAP,
+ AIF3_PB,
+ AIF3_CAP,
+ NUM_CODEC_DAIS,
};
-#define TAIKO_MCLK_RATE_12288KHZ 12288000
-#define TAIKO_MCLK_RATE_9600KHZ 9600000
+enum {
+ RX_MIX1_INP_SEL_ZERO = 0,
+ RX_MIX1_INP_SEL_SRC1,
+ RX_MIX1_INP_SEL_SRC2,
+ RX_MIX1_INP_SEL_IIR1,
+ RX_MIX1_INP_SEL_IIR2,
+ RX_MIX1_INP_SEL_RX1,
+ RX_MIX1_INP_SEL_RX2,
+ RX_MIX1_INP_SEL_RX3,
+ RX_MIX1_INP_SEL_RX4,
+ RX_MIX1_INP_SEL_RX5,
+ RX_MIX1_INP_SEL_RX6,
+ RX_MIX1_INP_SEL_RX7,
+ RX_MIX1_INP_SEL_AUXRX,
+};
-#define TAIKO_FAKE_INS_THRESHOLD_MS 2500
-#define TAIKO_FAKE_REMOVAL_MIN_PERIOD_MS 50
-
-#define TAIKO_MBHC_BUTTON_MIN 0x8000
-
-#define TAIKO_MBHC_FAKE_INSERT_LOW 10
-#define TAIKO_MBHC_FAKE_INSERT_HIGH 80
-#define TAIKO_MBHC_FAKE_INS_HIGH_NO_GPIO 150
-
-#define TAIKO_MBHC_STATUS_REL_DETECTION 0x0C
-
-#define TAIKO_MBHC_GPIO_REL_DEBOUNCE_TIME_MS 200
-
-#define TAIKO_MBHC_FAKE_INS_DELTA_MV 200
-#define TAIKO_MBHC_FAKE_INS_DELTA_SCALED_MV 300
-
-#define TAIKO_HS_DETECT_PLUG_TIME_MS (5 * 1000)
-#define TAIKO_HS_DETECT_PLUG_INERVAL_MS 100
-
-#define TAIKO_GPIO_IRQ_DEBOUNCE_TIME_US 5000
-
-#define TAIKO_MBHC_GND_MIC_SWAP_THRESHOLD 2
-
-#define TAIKO_ACQUIRE_LOCK(x) do { mutex_lock(&x); } while (0)
-#define TAIKO_RELEASE_LOCK(x) do { mutex_unlock(&x); } while (0)
+#define TAIKO_COMP_DIGITAL_GAIN_OFFSET 3
static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
@@ -115,21 +80,6 @@
static struct snd_soc_dai_driver taiko_dai[];
static const DECLARE_TLV_DB_SCALE(aux_pga_gain, 0, 2, 0);
-enum taiko_bandgap_type {
- TAIKO_BANDGAP_OFF = 0,
- TAIKO_BANDGAP_AUDIO_MODE,
- TAIKO_BANDGAP_MBHC_MODE,
-};
-
-struct mbhc_micbias_regs {
- u16 cfilt_val;
- u16 cfilt_ctl;
- u16 mbhc_reg;
- u16 int_rbias;
- u16 ctl_reg;
- u8 cfilt_sel;
-};
-
/* Codec supports 2 IIR filters */
enum {
IIR1 = 0,
@@ -162,72 +112,12 @@
COMPANDER_FS_MAX,
};
-/* Flags to track of PA and DAC state.
- * PA and DAC should be tracked separately as AUXPGA loopback requires
- * only PA to be turned on without DAC being on. */
-enum taiko_priv_ack_flags {
- TAIKO_HPHL_PA_OFF_ACK = 0,
- TAIKO_HPHR_PA_OFF_ACK,
- TAIKO_HPHL_DAC_OFF_ACK,
- TAIKO_HPHR_DAC_OFF_ACK
-};
-
-
struct comp_sample_dependent_params {
u32 peak_det_timeout;
u32 rms_meter_div_fact;
u32 rms_meter_resamp_fact;
};
-/* Data used by MBHC */
-struct mbhc_internal_cal_data {
- u16 dce_z;
- u16 dce_mb;
- u16 sta_z;
- u16 sta_mb;
- u32 t_sta_dce;
- u32 t_dce;
- u32 t_sta;
- u32 micb_mv;
- u16 v_ins_hu;
- u16 v_ins_h;
- u16 v_b1_hu;
- u16 v_b1_h;
- u16 v_b1_huc;
- u16 v_brh;
- u16 v_brl;
- u16 v_no_mic;
- u8 npoll;
- u8 nbounce_wait;
- s16 adj_v_hs_max;
- u16 adj_v_ins_hu;
- u16 adj_v_ins_h;
- s16 v_inval_ins_low;
- s16 v_inval_ins_high;
-};
-
-struct taiko_reg_address {
- u16 micb_4_ctl;
- u16 micb_4_int_rbias;
- u16 micb_4_mbhc;
-};
-
-enum taiko_mbhc_plug_type {
- PLUG_TYPE_INVALID = -1,
- PLUG_TYPE_NONE,
- PLUG_TYPE_HEADSET,
- PLUG_TYPE_HEADPHONE,
- PLUG_TYPE_HIGH_HPH,
- PLUG_TYPE_GND_MIC_SWAP,
-};
-
-enum taiko_mbhc_state {
- MBHC_STATE_NONE = -1,
- MBHC_STATE_POTENTIAL,
- MBHC_STATE_POTENTIAL_RECOVERY,
- MBHC_STATE_RELEASE,
-};
-
struct hpf_work {
struct taiko_priv *taiko;
u32 decimator;
@@ -237,62 +127,65 @@
static struct hpf_work tx_hpf_work[NUM_DECIMATORS];
+static const struct wcd9xxx_ch taiko_rx_chs[TAIKO_RX_MAX] = {
+ WCD9XXX_CH(16, 0),
+ WCD9XXX_CH(17, 1),
+ WCD9XXX_CH(18, 2),
+ WCD9XXX_CH(19, 3),
+ WCD9XXX_CH(20, 4),
+ WCD9XXX_CH(21, 5),
+ WCD9XXX_CH(22, 6),
+ WCD9XXX_CH(23, 7),
+ WCD9XXX_CH(24, 8),
+ WCD9XXX_CH(25, 9),
+ WCD9XXX_CH(26, 10),
+ WCD9XXX_CH(27, 11),
+ WCD9XXX_CH(28, 12),
+};
+
+static const struct wcd9xxx_ch taiko_tx_chs[TAIKO_TX_MAX] = {
+ WCD9XXX_CH(0, 0),
+ WCD9XXX_CH(1, 1),
+ WCD9XXX_CH(2, 2),
+ WCD9XXX_CH(3, 3),
+ WCD9XXX_CH(4, 4),
+ WCD9XXX_CH(5, 5),
+ WCD9XXX_CH(6, 6),
+ WCD9XXX_CH(7, 7),
+ WCD9XXX_CH(8, 8),
+ WCD9XXX_CH(9, 9),
+ WCD9XXX_CH(10, 10),
+ WCD9XXX_CH(11, 11),
+ WCD9XXX_CH(12, 12),
+ WCD9XXX_CH(13, 13),
+ WCD9XXX_CH(14, 14),
+ WCD9XXX_CH(15, 15),
+};
+
+static const u32 vport_check_table[NUM_CODEC_DAIS] = {
+ 0, /* AIF1_PB */
+ (1 << AIF2_CAP) | (1 << AIF3_CAP), /* AIF1_CAP */
+ 0, /* AIF2_PB */
+ (1 << AIF1_CAP) | (1 << AIF3_CAP), /* AIF2_CAP */
+ 0, /* AIF2_PB */
+ (1 << AIF1_CAP) | (1 << AIF2_CAP), /* AIF2_CAP */
+};
+
struct taiko_priv {
struct snd_soc_codec *codec;
- struct taiko_reg_address reg_addr;
u32 adc_count;
- u32 cfilt1_cnt;
- u32 cfilt2_cnt;
- u32 cfilt3_cnt;
u32 rx_bias_count;
s32 dmic_1_2_clk_cnt;
s32 dmic_3_4_clk_cnt;
s32 dmic_5_6_clk_cnt;
- enum taiko_bandgap_type bandgap_type;
- bool mclk_enabled;
- bool clock_active;
- bool config_mode_active;
- bool mbhc_polling_active;
- unsigned long mbhc_fake_ins_start;
- int buttons_pressed;
- enum taiko_mbhc_state mbhc_state;
- struct taiko_mbhc_config mbhc_cfg;
- struct mbhc_internal_cal_data mbhc_data;
-
- struct wcd9xxx_pdata *pdata;
u32 anc_slot;
- bool no_mic_headset_override;
- /* Delayed work to report long button press */
- struct delayed_work mbhc_btn_dwork;
-
- struct mbhc_micbias_regs mbhc_bias_regs;
- bool mbhc_micbias_switched;
-
- /* track PA/DAC state */
- unsigned long hph_pa_dac_state;
-
/*track taiko interface type*/
u8 intf_type;
- u32 hph_status; /* track headhpone status */
- /* define separate work for left and right headphone OCP to avoid
- * additional checking on which OCP event to report so no locking
- * to ensure synchronization is required
- */
- struct work_struct hphlocp_work; /* reporting left hph ocp off */
- struct work_struct hphrocp_work; /* reporting right hph ocp off */
-
- u8 hphlocp_cnt; /* headphone left ocp retry */
- u8 hphrocp_cnt; /* headphone right ocp retry */
-
- /* Work to perform MBHC Firmware Read */
- struct delayed_work mbhc_firmware_dwork;
- const struct firmware *mbhc_fw;
-
/* num of slim ports required */
- struct taiko_codec_dai_data dai[NUM_CODEC_DAIS];
+ struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS];
/*compander*/
int comp_enabled[COMPANDER_MAX];
@@ -303,29 +196,12 @@
u8 aux_l_gain;
u8 aux_r_gain;
- struct delayed_work mbhc_insert_dwork;
- unsigned long mbhc_last_resume; /* in jiffies */
-
- u8 current_plug;
- struct work_struct hs_correct_plug_work;
- bool hs_detect_work_stop;
- bool hs_polling_irq_prepared;
- bool lpi_enabled; /* low power insertion detection */
- bool in_gpio_handler;
- /* Currently, only used for mbhc purpose, to protect
- * concurrent execution of mbhc threaded irq handlers and
- * kill race between DAPM and MBHC.But can serve as a
- * general lock to protect codec resource
- */
- struct mutex codec_resource_lock;
-
-#ifdef CONFIG_DEBUG_FS
- struct dentry *debugfs_poke;
- struct dentry *debugfs_mbhc;
-#endif
+ /* resmgr module */
+ struct wcd9xxx_resmgr resmgr;
+ /* mbhc module */
+ struct wcd9xxx_mbhc mbhc;
};
-
static const u32 comp_shift[] = {
0,
2,
@@ -778,6 +654,9 @@
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
u32 rate = taiko->comp_fs[w->shift];
+ pr_debug("%s: %s event %d enabled = %d", __func__, w->name,
+ event, taiko->comp_enabled[w->shift]);
+
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
if (taiko->comp_enabled[w->shift] != 0) {
@@ -1601,6 +1480,240 @@
static const struct snd_kcontrol_new lineout4_ground_switch =
SOC_DAPM_SINGLE("Switch", TAIKO_A_RX_LINE_4_DAC_CTL, 6, 1, 0);
+static int slim_tx_vport_validation(u32 dai_id, u32 port_id,
+ struct taiko_priv *taiko_p)
+{
+ struct wcd9xxx_ch *ch;
+ int ret = 0;
+ int index = 0;
+ u32 vtable = vport_check_table[dai_id];
+ pr_debug("%s: dai_id %u vtable 0x%x port_id %u\n", __func__,
+ dai_id, vtable, port_id);
+ while (vtable) {
+ if (vtable & 1) {
+ list_for_each_entry(ch,
+ &taiko_p->dai[index].wcd9xxx_ch_list,
+ list) {
+ pr_debug("%s: index %u ch->port %u vtable 0x%x\n",
+ __func__, index, ch->port, vtable);
+ if (ch->port == port_id) {
+ pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n",
+ __func__, port_id + 1,
+ (index + 1)/2);
+ ret = -EINVAL;
+ break;
+ }
+ }
+ }
+ if (ret)
+ break;
+ index++;
+ vtable = vtable >> 1;
+ }
+ return ret;
+}
+
+/* virtual port entries */
+static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+ ucontrol->value.integer.value[0] = widget->value;
+ return 0;
+}
+
+static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
+ struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+ struct soc_multi_mixer_control *mixer =
+ ((struct soc_multi_mixer_control *)kcontrol->private_value);
+ u32 dai_id = widget->shift;
+ u32 port_id = mixer->shift;
+ u32 enable = ucontrol->value.integer.value[0];
+
+
+ pr_debug("%s: wname %s cname %s value %u shift %d item %ld\n", __func__,
+ widget->name, ucontrol->id.name, widget->value, widget->shift,
+ ucontrol->value.integer.value[0]);
+
+ mutex_lock(&codec->mutex);
+
+ if (taiko_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ if (dai_id != AIF1_CAP) {
+ dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+ __func__);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+ }
+ switch (dai_id) {
+ case AIF1_CAP:
+ case AIF2_CAP:
+ case AIF3_CAP:
+ /* only add to the list if value not set
+ */
+ if (enable && !(widget->value & 1 << port_id)) {
+ if (slim_tx_vport_validation(dai_id,
+ port_id, taiko_p)) {
+ pr_info("%s: TX%u is used by other virtual port\n",
+ __func__, port_id + 1);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+ widget->value |= 1 << port_id;
+ list_add_tail(&core->tx_chs[port_id].list,
+ &taiko_p->dai[dai_id].wcd9xxx_ch_list
+ );
+ } else if (!enable && (widget->value & 1 << port_id)) {
+ widget->value &= ~(1 << port_id);
+ list_del_init(&core->tx_chs[port_id].list);
+ } else {
+ if (enable)
+ pr_info("%s: TX%u port is used by this virtual port\n",
+ __func__, port_id + 1);
+ else
+ pr_info("%s: TX%u port is not used by this virtual port\n",
+ __func__, port_id + 1);
+ /* avoid update power function */
+ mutex_unlock(&codec->mutex);
+ return 0;
+ }
+ break;
+ default:
+ pr_err("Unknown AIF %d\n", dai_id);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+ pr_debug("%s: name %s sname %s updated value %u shift %d\n", __func__,
+ widget->name, widget->sname, widget->value, widget->shift);
+
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, enable);
+
+ mutex_unlock(&codec->mutex);
+ return 0;
+}
+
+static int slim_rx_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+ ucontrol->value.enumerated.item[0] = widget->value;
+ return 0;
+}
+
+static const char *const slim_rx_mux_text[] = {
+ "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB"
+};
+
+static int slim_rx_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
+ struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ u32 port_id = widget->shift;
+
+ pr_debug("%s: wname %s cname %s value %u shift %d item %ld\n", __func__,
+ widget->name, ucontrol->id.name, widget->value, widget->shift,
+ ucontrol->value.integer.value[0]);
+
+ widget->value = ucontrol->value.enumerated.item[0];
+
+ mutex_lock(&codec->mutex);
+
+ if (taiko_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ if (widget->value > 1) {
+ dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+ __func__);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+ }
+ /* value need to match the Virtual port and AIF number
+ */
+ switch (widget->value) {
+ case 0:
+ list_del_init(&core->rx_chs[port_id].list);
+ break;
+ case 1:
+ list_add_tail(&core->rx_chs[port_id].list,
+ &taiko_p->dai[AIF1_PB].wcd9xxx_ch_list);
+ break;
+ case 2:
+ list_add_tail(&core->rx_chs[port_id].list,
+ &taiko_p->dai[AIF2_PB].wcd9xxx_ch_list);
+ break;
+ case 3:
+ list_add_tail(&core->rx_chs[port_id].list,
+ &taiko_p->dai[AIF3_PB].wcd9xxx_ch_list);
+ break;
+ default:
+ pr_err("Unknown AIF %d\n", widget->value);
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
+ }
+
+ snd_soc_dapm_mux_update_power(widget, kcontrol, 1, widget->value, e);
+
+ mutex_unlock(&codec->mutex);
+ return 0;
+}
+
+static const struct soc_enum slim_rx_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text);
+
+static const struct snd_kcontrol_new slim_rx_mux[TAIKO_RX_MAX] = {
+ SOC_DAPM_ENUM_EXT("SLIM RX1 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX2 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX3 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX4 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX5 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX6 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX7 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+};
+
+static const struct snd_kcontrol_new aif_cap_mixer[] = {
+ SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TAIKO_TX1, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TAIKO_TX2, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TAIKO_TX3, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TAIKO_TX4, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TAIKO_TX5, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TAIKO_TX6, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TAIKO_TX7, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TAIKO_TX8, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TAIKO_TX9, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TAIKO_TX10, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
static void taiko_codec_enable_adc_block(struct snd_soc_codec *codec,
int enable)
{
@@ -1667,173 +1780,6 @@
return 0;
}
-static void taiko_codec_enable_audio_mode_bandgap(struct snd_soc_codec *codec)
-{
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x80,
- 0x80);
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x04,
- 0x04);
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x01,
- 0x01);
- usleep_range(1000, 1000);
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x80,
- 0x00);
-}
-
-static void taiko_codec_enable_bandgap(struct snd_soc_codec *codec,
- enum taiko_bandgap_type choice)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- /* TODO lock resources accessed by audio streams and threaded
- * interrupt handlers
- */
-
- pr_debug("%s, choice is %d, current is %d\n", __func__, choice,
- taiko->bandgap_type);
-
- if (taiko->bandgap_type == choice)
- return;
-
- if ((taiko->bandgap_type == TAIKO_BANDGAP_OFF) &&
- (choice == TAIKO_BANDGAP_AUDIO_MODE)) {
- taiko_codec_enable_audio_mode_bandgap(codec);
- } else if (choice == TAIKO_BANDGAP_MBHC_MODE) {
- /* bandgap mode becomes fast,
- * mclk should be off or clk buff source souldn't be VBG
- * Let's turn off mclk always */
- WARN_ON(snd_soc_read(codec, TAIKO_A_CLK_BUFF_EN2) & (1 << 2));
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x2,
- 0x2);
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x80,
- 0x80);
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x4,
- 0x4);
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x01,
- 0x01);
- usleep_range(1000, 1000);
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x80,
- 0x00);
- } else if ((taiko->bandgap_type == TAIKO_BANDGAP_MBHC_MODE) &&
- (choice == TAIKO_BANDGAP_AUDIO_MODE)) {
- snd_soc_write(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x00);
- usleep_range(100, 100);
- taiko_codec_enable_audio_mode_bandgap(codec);
- } else if (choice == TAIKO_BANDGAP_OFF) {
- snd_soc_write(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x50);
- } else {
- pr_err("%s: Error, Invalid bandgap settings\n", __func__);
- }
- taiko->bandgap_type = choice;
-}
-
-static void taiko_codec_disable_clock_block(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- pr_debug("%s\n", __func__);
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN2, 0x04, 0x00);
- usleep_range(50, 50);
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN2, 0x02, 0x02);
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x01, 0x00);
- usleep_range(50, 50);
- taiko->clock_active = false;
-}
-
-static int taiko_codec_mclk_index(const struct taiko_priv *taiko)
-{
- if (taiko->mbhc_cfg.mclk_rate == TAIKO_MCLK_RATE_12288KHZ)
- return 0;
- else if (taiko->mbhc_cfg.mclk_rate == TAIKO_MCLK_RATE_9600KHZ)
- return 1;
- else {
- BUG_ON(1);
- return -EINVAL;
- }
-}
-
-static void taiko_enable_rx_bias(struct snd_soc_codec *codec, u32 enable)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- if (enable) {
- taiko->rx_bias_count++;
- if (taiko->rx_bias_count == 1)
- snd_soc_update_bits(codec, TAIKO_A_RX_COM_BIAS,
- 0x80, 0x80);
- } else {
- taiko->rx_bias_count--;
- if (!taiko->rx_bias_count)
- snd_soc_update_bits(codec, TAIKO_A_RX_COM_BIAS,
- 0x80, 0x00);
- }
-}
-
-static int taiko_codec_enable_config_mode(struct snd_soc_codec *codec,
- int enable)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- pr_debug("%s: enable = %d\n", __func__, enable);
- if (enable) {
-
- snd_soc_update_bits(codec, TAIKO_A_RC_OSC_FREQ, 0x10, 0);
- /* bandgap mode to fast */
- snd_soc_write(codec, TAIKO_A_BIAS_OSC_BG_CTL, 0x17);
- usleep_range(5, 5);
- snd_soc_update_bits(codec, TAIKO_A_RC_OSC_FREQ, 0x80,
- 0x80);
- snd_soc_update_bits(codec, TAIKO_A_RC_OSC_TEST, 0x80,
- 0x80);
- usleep_range(10, 10);
- snd_soc_update_bits(codec, TAIKO_A_RC_OSC_TEST, 0x80, 0);
- usleep_range(10000, 10000);
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x08, 0x08);
-
- } else {
- snd_soc_update_bits(codec, TAIKO_A_BIAS_OSC_BG_CTL, 0x1,
- 0);
- snd_soc_update_bits(codec, TAIKO_A_RC_OSC_FREQ, 0x80, 0);
- /* clk source to ext clk and clk buff ref to VBG */
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x0C, 0x04);
- }
- taiko->config_mode_active = enable ? true : false;
-
- return 0;
-}
-
-static int taiko_codec_enable_clock_block(struct snd_soc_codec *codec,
- int config_mode)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- pr_debug("%s: config_mode = %d\n", __func__, config_mode);
-
- /* transit to RCO requires mclk off */
- WARN_ON(snd_soc_read(codec, TAIKO_A_CLK_BUFF_EN2) & (1 << 2));
- if (config_mode) {
- /* enable RCO and switch to it */
- taiko_codec_enable_config_mode(codec, 1);
- snd_soc_write(codec, TAIKO_A_CLK_BUFF_EN2, 0x02);
- usleep_range(1000, 1000);
- } else {
- /* switch to MCLK */
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x08, 0x00);
-
- if (taiko->mbhc_polling_active)
- snd_soc_write(codec, TAIKO_A_CLK_BUFF_EN2, 0x02);
- taiko_codec_enable_config_mode(codec, 0);
- }
-
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x01, 0x01);
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN2, 0x02, 0x00);
- /* on MCLK */
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN2, 0x04, 0x04);
- snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_MCLK_CTL, 0x01, 0x01);
- usleep_range(50, 50);
- taiko->clock_active = true;
- return 0;
-}
-
static int taiko_codec_enable_aux_pga(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -1844,32 +1790,22 @@
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- taiko_codec_enable_bandgap(codec, TAIKO_BANDGAP_AUDIO_MODE);
- taiko_enable_rx_bias(codec, 1);
-
- if (taiko->aux_pga_cnt++ == 1
- && !taiko->mclk_enabled) {
- taiko_codec_enable_clock_block(codec, 1);
- pr_debug("AUX PGA enabled RC osc\n");
- }
+ WCD9XXX_BCL_LOCK(&taiko->resmgr);
+ wcd9xxx_resmgr_get_bandgap(&taiko->resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
+ /* AUX PGA requires RCO or MCLK */
+ wcd9xxx_resmgr_get_clk_block(&taiko->resmgr, WCD9XXX_CLK_RCO);
+ wcd9xxx_resmgr_enable_rx_bias(&taiko->resmgr, 1);
+ WCD9XXX_BCL_UNLOCK(&taiko->resmgr);
break;
case SND_SOC_DAPM_POST_PMD:
- taiko_enable_rx_bias(codec, 0);
-
- if (taiko->aux_pga_cnt-- == 0) {
- if (taiko->mbhc_polling_active)
- taiko_codec_enable_bandgap(codec,
- TAIKO_BANDGAP_MBHC_MODE);
- else
- taiko_codec_enable_bandgap(codec,
- TAIKO_BANDGAP_OFF);
-
- if (!taiko->mclk_enabled &&
- !taiko->mbhc_polling_active) {
- taiko_codec_enable_clock_block(codec, 0);
- }
- }
+ WCD9XXX_BCL_LOCK(&taiko->resmgr);
+ wcd9xxx_resmgr_enable_rx_bias(&taiko->resmgr, 0);
+ wcd9xxx_resmgr_put_bandgap(&taiko->resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
+ wcd9xxx_resmgr_put_clk_block(&taiko->resmgr, WCD9XXX_CLK_RCO);
+ WCD9XXX_BCL_UNLOCK(&taiko->resmgr);
break;
}
return 0;
@@ -2096,358 +2032,47 @@
return 0;
}
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_start_hs_polling(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- int mbhc_state = taiko->mbhc_state;
-
- pr_debug("%s: enter\n", __func__);
- if (!taiko->mbhc_polling_active) {
- pr_debug("Polling is not active, do not start polling\n");
- return;
- }
- snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x84);
-
- if (!taiko->no_mic_headset_override) {
- if (mbhc_state == MBHC_STATE_POTENTIAL) {
- pr_debug("%s recovering MBHC state macine\n", __func__);
- taiko->mbhc_state = MBHC_STATE_POTENTIAL_RECOVERY;
- /* set to max button press threshold */
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B2_CTL,
- 0x7F);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B1_CTL,
- 0xFF);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B4_CTL,
- 0x7F);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B3_CTL,
- 0xFF);
- /* set to max */
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B6_CTL,
- 0x7F);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B5_CTL,
- 0xFF);
- }
- }
-
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x1);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x1);
- pr_debug("%s: leave\n", __func__);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_pause_hs_polling(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- pr_debug("%s: enter\n", __func__);
- if (!taiko->mbhc_polling_active) {
- pr_debug("polling not active, nothing to pause\n");
- return;
- }
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
- pr_debug("%s: leave\n", __func__);
-}
-
-static void taiko_codec_switch_cfilt_mode(struct snd_soc_codec *codec, int mode)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- u8 reg_mode_val, cur_mode_val;
- bool mbhc_was_polling = false;
-
- if (mode)
- reg_mode_val = TAIKO_CFILT_FAST_MODE;
- else
- reg_mode_val = TAIKO_CFILT_SLOW_MODE;
-
- cur_mode_val = snd_soc_read(codec,
- taiko->mbhc_bias_regs.cfilt_ctl) & 0x40;
-
- if (cur_mode_val != reg_mode_val) {
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- if (taiko->mbhc_polling_active) {
- taiko_codec_pause_hs_polling(codec);
- mbhc_was_polling = true;
- }
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.cfilt_ctl, 0x40, reg_mode_val);
- if (mbhc_was_polling)
- taiko_codec_start_hs_polling(codec);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
- cur_mode_val, reg_mode_val);
- } else {
- pr_debug("%s: CFILT Value is already %x\n",
- __func__, cur_mode_val);
- }
-}
-
-static void taiko_codec_update_cfilt_usage(struct snd_soc_codec *codec,
- u8 cfilt_sel, int inc)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- u32 *cfilt_cnt_ptr = NULL;
- u16 micb_cfilt_reg;
-
- switch (cfilt_sel) {
- case TAIKO_CFILT1_SEL:
- cfilt_cnt_ptr = &taiko->cfilt1_cnt;
- micb_cfilt_reg = TAIKO_A_MICB_CFILT_1_CTL;
- break;
- case TAIKO_CFILT2_SEL:
- cfilt_cnt_ptr = &taiko->cfilt2_cnt;
- micb_cfilt_reg = TAIKO_A_MICB_CFILT_2_CTL;
- break;
- case TAIKO_CFILT3_SEL:
- cfilt_cnt_ptr = &taiko->cfilt3_cnt;
- micb_cfilt_reg = TAIKO_A_MICB_CFILT_3_CTL;
- break;
- default:
- return; /* should not happen */
- }
-
- if (inc) {
- if (!(*cfilt_cnt_ptr)++) {
- /* Switch CFILT to slow mode if MBHC CFILT being used */
- if (cfilt_sel == taiko->mbhc_bias_regs.cfilt_sel)
- taiko_codec_switch_cfilt_mode(codec, 0);
-
- snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0x80);
- }
- } else {
- /* check if count not zero, decrement
- * then check if zero, go ahead disable cfilter
- */
- if ((*cfilt_cnt_ptr) && !--(*cfilt_cnt_ptr)) {
- snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0);
-
- /* Switch CFILT to fast mode if MBHC CFILT being used */
- if (cfilt_sel == taiko->mbhc_bias_regs.cfilt_sel)
- taiko_codec_switch_cfilt_mode(codec, 1);
- }
- }
-}
-
-static int taiko_find_k_value(unsigned int ldoh_v, unsigned int cfilt_mv)
-{
- int rc = -EINVAL;
- unsigned min_mv, max_mv;
-
- switch (ldoh_v) {
- case TAIKO_LDOH_1P95_V:
- min_mv = 160;
- max_mv = 1800;
- break;
- case TAIKO_LDOH_2P35_V:
- min_mv = 200;
- max_mv = 2200;
- break;
- case TAIKO_LDOH_2P75_V:
- min_mv = 240;
- max_mv = 2600;
- break;
- case TAIKO_LDOH_2P85_V:
- min_mv = 250;
- max_mv = 2700;
- break;
- default:
- goto done;
- }
-
- if (cfilt_mv < min_mv || cfilt_mv > max_mv)
- goto done;
-
- for (rc = 4; rc <= 44; rc++) {
- min_mv = max_mv * (rc) / 44;
- if (min_mv >= cfilt_mv) {
- rc -= 4;
- break;
- }
- }
-done:
- return rc;
-}
-
-static bool taiko_is_hph_pa_on(struct snd_soc_codec *codec)
-{
- u8 hph_reg_val = 0;
- hph_reg_val = snd_soc_read(codec, TAIKO_A_RX_HPH_CNP_EN);
-
- return (hph_reg_val & 0x30) ? true : false;
-}
-
-static bool taiko_is_hph_dac_on(struct snd_soc_codec *codec, int left)
-{
- u8 hph_reg_val = 0;
- if (left)
- hph_reg_val = snd_soc_read(codec,
- TAIKO_A_RX_HPH_L_DAC_CTL);
- else
- hph_reg_val = snd_soc_read(codec,
- TAIKO_A_RX_HPH_R_DAC_CTL);
-
- return (hph_reg_val & 0xC0) ? true : false;
-}
-
-static void taiko_turn_onoff_override(struct snd_soc_codec *codec, bool on)
-{
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x04, on << 2);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_drive_v_to_micbias(struct snd_soc_codec *codec,
- int usec)
-{
- int cfilt_k_val;
- bool set = true;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- if (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
- taiko->mbhc_micbias_switched) {
- pr_debug("%s: set mic V to micbias V\n", __func__);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
- taiko_turn_onoff_override(codec, true);
- while (1) {
- cfilt_k_val = taiko_find_k_value(
- taiko->pdata->micbias.ldoh_v,
- set ? taiko->mbhc_data.micb_mv :
- VDDIO_MICBIAS_MV);
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.cfilt_val,
- 0xFC, (cfilt_k_val << 2));
- if (!set)
- break;
- usleep_range(usec, usec);
- set = false;
- }
- taiko_turn_onoff_override(codec, false);
- }
-}
-
-/* called under codec_resource_lock acquisition */
-static void __taiko_codec_switch_micbias(struct snd_soc_codec *codec,
- int vddio_switch, bool restartpolling,
- bool checkpolling)
-{
- int cfilt_k_val;
- bool override;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- if (vddio_switch && !taiko->mbhc_micbias_switched &&
- (!checkpolling || taiko->mbhc_polling_active)) {
- if (restartpolling)
- taiko_codec_pause_hs_polling(codec);
- override = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B1_CTL) & 0x04;
- if (!override)
- taiko_turn_onoff_override(codec, true);
- /* Adjust threshold if Mic Bias voltage changes */
- if (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
- cfilt_k_val = taiko_find_k_value(
- taiko->pdata->micbias.ldoh_v,
- VDDIO_MICBIAS_MV);
- usleep_range(10000, 10000);
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.cfilt_val,
- 0xFC, (cfilt_k_val << 2));
- usleep_range(10000, 10000);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B1_CTL,
- taiko->mbhc_data.adj_v_ins_hu & 0xFF);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B2_CTL,
- (taiko->mbhc_data.adj_v_ins_hu >> 8) &
- 0xFF);
- pr_debug("%s: Programmed MBHC thresholds to VDDIO\n",
- __func__);
- }
-
- /* enable MIC BIAS Switch to VDDIO */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
- 0x80, 0x80);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
- 0x10, 0x00);
- if (!override)
- taiko_turn_onoff_override(codec, false);
- if (restartpolling)
- taiko_codec_start_hs_polling(codec);
-
- taiko->mbhc_micbias_switched = true;
- pr_debug("%s: VDDIO switch enabled\n", __func__);
- } else if (!vddio_switch && taiko->mbhc_micbias_switched) {
- if ((!checkpolling || taiko->mbhc_polling_active) &&
- restartpolling)
- taiko_codec_pause_hs_polling(codec);
- /* Reprogram thresholds */
- if (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
- cfilt_k_val = taiko_find_k_value(
- taiko->pdata->micbias.ldoh_v,
- taiko->mbhc_data.micb_mv);
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.cfilt_val,
- 0xFC, (cfilt_k_val << 2));
- usleep_range(10000, 10000);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B1_CTL,
- taiko->mbhc_data.v_ins_hu & 0xFF);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B2_CTL,
- (taiko->mbhc_data.v_ins_hu >> 8) & 0xFF);
- pr_debug("%s: Programmed MBHC thresholds to MICBIAS\n",
- __func__);
- }
-
- /* Disable MIC BIAS Switch to VDDIO */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
- 0x80, 0x00);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
- 0x10, 0x00);
-
- if ((!checkpolling || taiko->mbhc_polling_active) &&
- restartpolling)
- taiko_codec_start_hs_polling(codec);
-
- taiko->mbhc_micbias_switched = false;
- pr_debug("%s: VDDIO switch disabled\n", __func__);
- }
-}
-
-static void taiko_codec_switch_micbias(struct snd_soc_codec *codec,
- int vddio_switch)
-{
- return __taiko_codec_switch_micbias(codec, vddio_switch, true, true);
-}
-
static int taiko_codec_enable_micbias(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
u16 micb_int_reg;
- int micb_line;
u8 cfilt_sel_val = 0;
char *internal1_text = "Internal1";
char *internal2_text = "Internal2";
char *internal3_text = "Internal3";
+ enum wcd9xxx_notify_event e_post_off, e_pre_on, e_post_on;
pr_debug("%s %d\n", __func__, event);
switch (w->reg) {
case TAIKO_A_MICB_1_CTL:
micb_int_reg = TAIKO_A_MICB_1_INT_RBIAS;
- cfilt_sel_val = taiko->pdata->micbias.bias1_cfilt_sel;
- micb_line = TAIKO_MICBIAS1;
+ cfilt_sel_val = taiko->resmgr.pdata->micbias.bias1_cfilt_sel;
+ e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_1_ON;
+ e_post_on = WCD9XXX_EVENT_POST_MICBIAS_1_ON;
+ e_post_off = WCD9XXX_EVENT_POST_MICBIAS_1_OFF;
break;
case TAIKO_A_MICB_2_CTL:
micb_int_reg = TAIKO_A_MICB_2_INT_RBIAS;
- cfilt_sel_val = taiko->pdata->micbias.bias2_cfilt_sel;
- micb_line = TAIKO_MICBIAS2;
+ cfilt_sel_val = taiko->resmgr.pdata->micbias.bias2_cfilt_sel;
+ e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_2_ON;
+ e_post_on = WCD9XXX_EVENT_POST_MICBIAS_2_ON;
+ e_post_off = WCD9XXX_EVENT_POST_MICBIAS_2_OFF;
break;
case TAIKO_A_MICB_3_CTL:
micb_int_reg = TAIKO_A_MICB_3_INT_RBIAS;
- cfilt_sel_val = taiko->pdata->micbias.bias3_cfilt_sel;
- micb_line = TAIKO_MICBIAS3;
+ cfilt_sel_val = taiko->resmgr.pdata->micbias.bias3_cfilt_sel;
+ e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_3_ON;
+ e_post_on = WCD9XXX_EVENT_POST_MICBIAS_3_ON;
+ e_post_off = WCD9XXX_EVENT_POST_MICBIAS_3_OFF;
break;
case TAIKO_A_MICB_4_CTL:
- micb_int_reg = taiko->reg_addr.micb_4_int_rbias;
- cfilt_sel_val = taiko->pdata->micbias.bias4_cfilt_sel;
- micb_line = TAIKO_MICBIAS4;
+ micb_int_reg = taiko->resmgr.reg_addr->micb_4_int_rbias;
+ cfilt_sel_val = taiko->resmgr.pdata->micbias.bias4_cfilt_sel;
+ e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_4_ON;
+ e_post_on = WCD9XXX_EVENT_POST_MICBIAS_4_ON;
+ e_post_off = WCD9XXX_EVENT_POST_MICBIAS_4_OFF;
break;
default:
pr_err("%s: Error, invalid micbias register\n", __func__);
@@ -2456,15 +2081,12 @@
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- /* Decide whether to switch the micbias for MBHC */
- if (w->reg == taiko->mbhc_bias_regs.ctl_reg) {
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- taiko_codec_switch_micbias(codec, 0);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- }
+ /* Let MBHC module know so micbias switch to be off */
+ wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_pre_on);
snd_soc_update_bits(codec, w->reg, 0x0E, 0x0A);
- taiko_codec_update_cfilt_usage(codec, cfilt_sel_val, 1);
+ /* Get cfilt */
+ wcd9xxx_resmgr_cfilt_get(&taiko->resmgr, cfilt_sel_val);
if (strnstr(w->name, internal1_text, 30))
snd_soc_update_bits(codec, micb_int_reg, 0xE0, 0xE0);
@@ -2475,25 +2097,13 @@
break;
case SND_SOC_DAPM_POST_PMU:
-
usleep_range(20000, 20000);
-
- if (taiko->mbhc_polling_active &&
- taiko->mbhc_cfg.micbias == micb_line) {
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- taiko_codec_pause_hs_polling(codec);
- taiko_codec_start_hs_polling(codec);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- }
+ /* Let MBHC module know so micbias is on */
+ wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_post_on);
break;
-
case SND_SOC_DAPM_POST_PMD:
- if ((w->reg == taiko->mbhc_bias_regs.ctl_reg) &&
- taiko_is_hph_pa_on(codec)) {
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- taiko_codec_switch_micbias(codec, 1);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- }
+ /* Let MBHC module know so micbias switch to be off */
+ wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_post_off);
if (strnstr(w->name, internal1_text, 30))
snd_soc_update_bits(codec, micb_int_reg, 0x80, 0x00);
@@ -2502,7 +2112,8 @@
else if (strnstr(w->name, internal3_text, 30))
snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x0);
- taiko_codec_update_cfilt_usage(codec, cfilt_sel_val, 0);
+ /* Put cfilt */
+ wcd9xxx_resmgr_cfilt_put(&taiko->resmgr, cfilt_sel_val);
break;
}
@@ -2704,15 +2315,16 @@
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
+ struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
pr_debug("%s %d\n", __func__, event);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- taiko_enable_rx_bias(codec, 1);
+ wcd9xxx_resmgr_enable_rx_bias(&taiko->resmgr, 1);
break;
case SND_SOC_DAPM_POST_PMD:
- taiko_enable_rx_bias(codec, 0);
+ wcd9xxx_resmgr_enable_rx_bias(&taiko->resmgr, 0);
break;
}
return 0;
@@ -2735,81 +2347,32 @@
return 0;
}
-static void taiko_snd_soc_jack_report(struct taiko_priv *taiko,
- struct snd_soc_jack *jack, int status,
- int mask)
-{
- /* XXX: wake_lock_timeout()? */
- snd_soc_jack_report_no_dapm(jack, status, mask);
-}
-
-static void hphocp_off_report(struct taiko_priv *taiko,
- u32 jack_status, int irq)
-{
- struct snd_soc_codec *codec;
- if (!taiko) {
- pr_err("%s: Bad taiko private data\n", __func__);
- return;
- }
-
- pr_debug("%s: clear ocp status %x\n", __func__, jack_status);
- codec = taiko->codec;
- if (taiko->hph_status & jack_status) {
- taiko->hph_status &= ~jack_status;
- if (taiko->mbhc_cfg.headset_jack)
- taiko_snd_soc_jack_report(taiko,
- taiko->mbhc_cfg.headset_jack,
- taiko->hph_status,
- TAIKO_JACK_MASK);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10, 0x10);
- /* reset retry counter as PA is turned off signifying
- * start of new OCP detection session
- */
- if (TAIKO_IRQ_HPH_PA_OCPL_FAULT)
- taiko->hphlocp_cnt = 0;
- else
- taiko->hphrocp_cnt = 0;
- wcd9xxx_enable_irq(codec->control_data, irq);
- }
-}
-
-static void hphlocp_off_report(struct work_struct *work)
-{
- struct taiko_priv *taiko = container_of(work, struct taiko_priv,
- hphlocp_work);
- hphocp_off_report(taiko, SND_JACK_OC_HPHL, TAIKO_IRQ_HPH_PA_OCPL_FAULT);
-}
-
-static void hphrocp_off_report(struct work_struct *work)
-{
- struct taiko_priv *taiko = container_of(work, struct taiko_priv,
- hphrocp_work);
- hphocp_off_report(taiko, SND_JACK_OC_HPHR, TAIKO_IRQ_HPH_PA_OCPR_FAULT);
-}
-
static int taiko_hph_pa_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- u8 mbhc_micb_ctl_val;
+ enum wcd9xxx_notify_event e_pre_on, e_post_off;
+
pr_debug("%s: %s event = %d\n", __func__, w->name, event);
+ if (w->shift == 5) {
+ e_pre_on = WCD9XXX_EVENT_PRE_HPHR_PA_ON;
+ e_post_off = WCD9XXX_EVENT_POST_HPHR_PA_OFF;
+ } else if (w->shift == 4) {
+ e_pre_on = WCD9XXX_EVENT_PRE_HPHL_PA_ON;
+ e_post_off = WCD9XXX_EVENT_POST_HPHL_PA_OFF;
+ } else {
+ pr_err("%s: Invalid w->shift %d\n", __func__, w->shift);
+ return -EINVAL;
+ }
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- mbhc_micb_ctl_val = snd_soc_read(codec,
- taiko->mbhc_bias_regs.ctl_reg);
-
- if (!(mbhc_micb_ctl_val & 0x80)) {
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- taiko_codec_switch_micbias(codec, 1);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- }
+ /* Let MBHC module know PA is turning on */
+ wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_pre_on);
break;
case SND_SOC_DAPM_POST_PMU:
-
usleep_range(10000, 10000);
snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_5, 0x02, 0x00);
@@ -2818,100 +2381,26 @@
snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_3, 0x08, 0x00);
usleep_range(10, 10);
-
break;
case SND_SOC_DAPM_POST_PMD:
- /* schedule work is required because at the time HPH PA DAPM
+ /* Let MBHC module know PA turned off */
+ wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_post_off);
+
+ /*
+ * schedule work is required because at the time HPH PA DAPM
* event callback is called by DAPM framework, CODEC dapm mutex
* would have been locked while snd_soc_jack_report also
* attempts to acquire same lock.
*/
- if (w->shift == 5) {
- clear_bit(TAIKO_HPHL_PA_OFF_ACK,
- &taiko->hph_pa_dac_state);
- clear_bit(TAIKO_HPHL_DAC_OFF_ACK,
- &taiko->hph_pa_dac_state);
- if (taiko->hph_status & SND_JACK_OC_HPHL)
- schedule_work(&taiko->hphlocp_work);
- } else if (w->shift == 4) {
- clear_bit(TAIKO_HPHR_PA_OFF_ACK,
- &taiko->hph_pa_dac_state);
- clear_bit(TAIKO_HPHR_DAC_OFF_ACK,
- &taiko->hph_pa_dac_state);
- if (taiko->hph_status & SND_JACK_OC_HPHR)
- schedule_work(&taiko->hphrocp_work);
- }
-
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- taiko_codec_switch_micbias(codec, 0);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-
pr_debug("%s: sleep 10 ms after %s PA disable.\n", __func__,
- w->name);
+ w->name);
usleep_range(10000, 10000);
break;
}
return 0;
}
-static void taiko_get_mbhc_micbias_regs(struct snd_soc_codec *codec,
- struct mbhc_micbias_regs *micbias_regs)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- unsigned int cfilt;
-
- switch (taiko->mbhc_cfg.micbias) {
- case TAIKO_MICBIAS1:
- cfilt = taiko->pdata->micbias.bias1_cfilt_sel;
- micbias_regs->mbhc_reg = TAIKO_A_MICB_1_MBHC;
- micbias_regs->int_rbias = TAIKO_A_MICB_1_INT_RBIAS;
- micbias_regs->ctl_reg = TAIKO_A_MICB_1_CTL;
- break;
- case TAIKO_MICBIAS2:
- cfilt = taiko->pdata->micbias.bias2_cfilt_sel;
- micbias_regs->mbhc_reg = TAIKO_A_MICB_2_MBHC;
- micbias_regs->int_rbias = TAIKO_A_MICB_2_INT_RBIAS;
- micbias_regs->ctl_reg = TAIKO_A_MICB_2_CTL;
- break;
- case TAIKO_MICBIAS3:
- cfilt = taiko->pdata->micbias.bias3_cfilt_sel;
- micbias_regs->mbhc_reg = TAIKO_A_MICB_3_MBHC;
- micbias_regs->int_rbias = TAIKO_A_MICB_3_INT_RBIAS;
- micbias_regs->ctl_reg = TAIKO_A_MICB_3_CTL;
- break;
- case TAIKO_MICBIAS4:
- cfilt = taiko->pdata->micbias.bias4_cfilt_sel;
- micbias_regs->mbhc_reg = taiko->reg_addr.micb_4_mbhc;
- micbias_regs->int_rbias = taiko->reg_addr.micb_4_int_rbias;
- micbias_regs->ctl_reg = taiko->reg_addr.micb_4_ctl;
- break;
- default:
- /* Should never reach here */
- pr_err("%s: Invalid MIC BIAS for MBHC\n", __func__);
- return;
- }
-
- micbias_regs->cfilt_sel = cfilt;
-
- switch (cfilt) {
- case TAIKO_CFILT1_SEL:
- micbias_regs->cfilt_val = TAIKO_A_MICB_CFILT_1_VAL;
- micbias_regs->cfilt_ctl = TAIKO_A_MICB_CFILT_1_CTL;
- taiko->mbhc_data.micb_mv = taiko->pdata->micbias.cfilt1_mv;
- break;
- case TAIKO_CFILT2_SEL:
- micbias_regs->cfilt_val = TAIKO_A_MICB_CFILT_2_VAL;
- micbias_regs->cfilt_ctl = TAIKO_A_MICB_CFILT_2_CTL;
- taiko->mbhc_data.micb_mv = taiko->pdata->micbias.cfilt2_mv;
- break;
- case TAIKO_CFILT3_SEL:
- micbias_regs->cfilt_val = TAIKO_A_MICB_CFILT_3_VAL;
- micbias_regs->cfilt_ctl = TAIKO_A_MICB_CFILT_3_CTL;
- taiko->mbhc_data.micb_mv = taiko->pdata->micbias.cfilt3_mv;
- break;
- }
-}
static const struct snd_soc_dapm_widget taiko_dapm_i2s_widgets[] = {
SND_SOC_DAPM_SUPPLY("RX_I2S_CLK", TAIKO_A_CDC_CLK_RX_I2S_CTL,
4, 0, NULL, 0),
@@ -2960,14 +2449,48 @@
static const struct snd_soc_dapm_route audio_map[] = {
/* SLIMBUS Connections */
+ {"AIF1 CAP", NULL, "AIF1_CAP Mixer"},
+ {"AIF2 CAP", NULL, "AIF2_CAP Mixer"},
+ {"AIF3 CAP", NULL, "AIF3_CAP Mixer"},
- {"SLIM TX1", NULL, "SLIM TX1 MUX"},
+ /* SLIM_MIXER("AIF1_CAP Mixer"),*/
+ {"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+ /* SLIM_MIXER("AIF2_CAP Mixer"),*/
+ {"AIF2_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+ /* SLIM_MIXER("AIF3_CAP Mixer"),*/
+ {"AIF3_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+
{"SLIM TX1 MUX", "DEC1", "DEC1 MUX"},
- {"SLIM TX2", NULL, "SLIM TX2 MUX"},
{"SLIM TX2 MUX", "DEC2", "DEC2 MUX"},
- {"SLIM TX3", NULL, "SLIM TX3 MUX"},
{"SLIM TX3 MUX", "DEC3", "DEC3 MUX"},
{"SLIM TX3 MUX", "RMIX1", "RX1 MIX1"},
{"SLIM TX3 MUX", "RMIX2", "RX2 MIX1"},
@@ -2977,10 +2500,8 @@
{"SLIM TX3 MUX", "RMIX6", "RX6 MIX1"},
{"SLIM TX3 MUX", "RMIX7", "RX7 MIX1"},
- {"SLIM TX4", NULL, "SLIM TX4 MUX"},
{"SLIM TX4 MUX", "DEC4", "DEC4 MUX"},
- {"SLIM TX5", NULL, "SLIM TX5 MUX"},
{"SLIM TX5 MUX", "DEC5", "DEC5 MUX"},
{"SLIM TX5 MUX", "RMIX1", "RX1 MIX1"},
{"SLIM TX5 MUX", "RMIX2", "RX2 MIX1"},
@@ -2990,10 +2511,8 @@
{"SLIM TX5 MUX", "RMIX6", "RX6 MIX1"},
{"SLIM TX5 MUX", "RMIX7", "RX7 MIX1"},
- {"SLIM TX6", NULL, "SLIM TX6 MUX"},
{"SLIM TX6 MUX", "DEC6", "DEC6 MUX"},
- {"SLIM TX7", NULL, "SLIM TX7 MUX"},
{"SLIM TX7 MUX", "DEC1", "DEC1 MUX"},
{"SLIM TX7 MUX", "DEC2", "DEC2 MUX"},
{"SLIM TX7 MUX", "DEC3", "DEC3 MUX"},
@@ -3012,7 +2531,6 @@
{"SLIM TX7 MUX", "RMIX6", "RX6 MIX1"},
{"SLIM TX7 MUX", "RMIX7", "RX7 MIX1"},
- {"SLIM TX8", NULL, "SLIM TX8 MUX"},
{"SLIM TX8 MUX", "DEC1", "DEC1 MUX"},
{"SLIM TX8 MUX", "DEC2", "DEC2 MUX"},
{"SLIM TX8 MUX", "DEC3", "DEC3 MUX"},
@@ -3024,7 +2542,6 @@
{"SLIM TX8 MUX", "DEC9", "DEC9 MUX"},
{"SLIM TX8 MUX", "DEC10", "DEC10 MUX"},
- {"SLIM TX9", NULL, "SLIM TX9 MUX"},
{"SLIM TX9 MUX", "DEC1", "DEC1 MUX"},
{"SLIM TX9 MUX", "DEC2", "DEC2 MUX"},
{"SLIM TX9 MUX", "DEC3", "DEC3 MUX"},
@@ -3036,7 +2553,6 @@
{"SLIM TX9 MUX", "DEC9", "DEC9 MUX"},
{"SLIM TX9 MUX", "DEC10", "DEC10 MUX"},
- {"SLIM TX10", NULL, "SLIM TX10 MUX"},
{"SLIM TX10 MUX", "DEC1", "DEC1 MUX"},
{"SLIM TX10 MUX", "DEC2", "DEC2 MUX"},
{"SLIM TX10 MUX", "DEC3", "DEC3 MUX"},
@@ -3123,7 +2639,7 @@
{"LINEOUT4 DAC", NULL, "RX6 DSM MUX"},
{"SPK PA", NULL, "SPK DAC"},
- {"SPK DAC", NULL, "RX7 MIX1"},
+ {"SPK DAC", NULL, "RX7 MIX2"},
{"RX1 CHAIN", NULL, "RX1 MIX2"},
{"RX2 CHAIN", NULL, "RX2 MIX2"},
@@ -3167,6 +2683,39 @@
{"RX7 MIX2", NULL, "RX7 MIX2 INP1"},
{"RX7 MIX2", NULL, "RX7 MIX2 INP2"},
+ /* SLIM_MUX("AIF1_PB", "AIF1 PB"),*/
+ {"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX5 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX6 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX7 MUX", "AIF1_PB", "AIF1 PB"},
+ /* SLIM_MUX("AIF2_PB", "AIF2 PB"),*/
+ {"SLIM RX1 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX2 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX3 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX4 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX5 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX6 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX7 MUX", "AIF2_PB", "AIF2 PB"},
+ /* SLIM_MUX("AIF3_PB", "AIF3 PB"),*/
+ {"SLIM RX1 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX2 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX3 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX4 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX5 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX6 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX7 MUX", "AIF3_PB", "AIF3 PB"},
+
+ {"SLIM RX1", NULL, "SLIM RX1 MUX"},
+ {"SLIM RX2", NULL, "SLIM RX2 MUX"},
+ {"SLIM RX3", NULL, "SLIM RX3 MUX"},
+ {"SLIM RX4", NULL, "SLIM RX4 MUX"},
+ {"SLIM RX5", NULL, "SLIM RX5 MUX"},
+ {"SLIM RX6", NULL, "SLIM RX6 MUX"},
+ {"SLIM RX7", NULL, "SLIM RX7 MUX"},
+
{"RX1 MIX1 INP1", "RX1", "SLIM RX1"},
{"RX1 MIX1 INP1", "RX2", "SLIM RX2"},
{"RX1 MIX1 INP1", "RX3", "SLIM RX3"},
@@ -3435,6 +2984,9 @@
if (reg == TAIKO_A_RX_HPH_L_STATUS || reg == TAIKO_A_RX_HPH_R_STATUS)
return 1;
+ if (reg == TAIKO_A_MBHC_INSERT_DET_STATUS)
+ return 1;
+
return 0;
}
@@ -3443,6 +2995,10 @@
unsigned int value)
{
int ret;
+
+ if (reg == SND_SOC_NOPM)
+ return 0;
+
BUG_ON(reg > TAIKO_MAX_REGISTER);
if (!taiko_volatile(codec, reg)) {
@@ -3460,6 +3016,9 @@
unsigned int val;
int ret;
+ if (reg == SND_SOC_NOPM)
+ return 0;
+
BUG_ON(reg > TAIKO_MAX_REGISTER);
if (!taiko_volatile(codec, reg) && taiko_readable(codec, reg) &&
@@ -3476,79 +3035,6 @@
return val;
}
-static s16 taiko_get_current_v_ins(struct taiko_priv *taiko, bool hu)
-{
- s16 v_ins;
- if ((taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
- taiko->mbhc_micbias_switched)
- v_ins = hu ? (s16)taiko->mbhc_data.adj_v_ins_hu :
- (s16)taiko->mbhc_data.adj_v_ins_h;
- else
- v_ins = hu ? (s16)taiko->mbhc_data.v_ins_hu :
- (s16)taiko->mbhc_data.v_ins_h;
- return v_ins;
-}
-
-static s16 taiko_get_current_v_hs_max(struct taiko_priv *taiko)
-{
- s16 v_hs_max;
- struct taiko_mbhc_plug_type_cfg *plug_type;
-
- plug_type = TAIKO_MBHC_CAL_PLUG_TYPE_PTR(taiko->mbhc_cfg.calibration);
- if ((taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
- taiko->mbhc_micbias_switched)
- v_hs_max = taiko->mbhc_data.adj_v_hs_max;
- else
- v_hs_max = plug_type->v_hs_max;
- return v_hs_max;
-}
-
-static void taiko_codec_calibrate_hs_polling(struct snd_soc_codec *codec)
-{
- u8 *n_ready, *n_cic;
- struct taiko_mbhc_btn_detect_cfg *btn_det;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- const s16 v_ins_hu = taiko_get_current_v_ins(taiko, true);
-
- btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(taiko->mbhc_cfg.calibration);
-
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B1_CTL,
- v_ins_hu & 0xFF);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B2_CTL,
- (v_ins_hu >> 8) & 0xFF);
-
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B3_CTL,
- taiko->mbhc_data.v_b1_hu & 0xFF);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B4_CTL,
- (taiko->mbhc_data.v_b1_hu >> 8) & 0xFF);
-
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B5_CTL,
- taiko->mbhc_data.v_b1_h & 0xFF);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B6_CTL,
- (taiko->mbhc_data.v_b1_h >> 8) & 0xFF);
-
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B9_CTL,
- taiko->mbhc_data.v_brh & 0xFF);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B10_CTL,
- (taiko->mbhc_data.v_brh >> 8) & 0xFF);
-
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B11_CTL,
- taiko->mbhc_data.v_brl & 0xFF);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B12_CTL,
- (taiko->mbhc_data.v_brl >> 8) & 0xFF);
-
- n_ready = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_READY);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B1_CTL,
- n_ready[taiko_codec_mclk_index(taiko)]);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B2_CTL,
- taiko->mbhc_data.npoll);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B3_CTL,
- taiko->mbhc_data.nbounce_wait);
- n_cic = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_CIC);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B6_CTL,
- n_cic[taiko_codec_mclk_index(taiko)]);
-}
-
static int taiko_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -3583,54 +3069,20 @@
pr_debug("%s: mclk_enable = %u, dapm = %d\n", __func__, mclk_enable,
dapm);
- if (dapm)
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
+
+ WCD9XXX_BCL_LOCK(&taiko->resmgr);
if (mclk_enable) {
- taiko->mclk_enabled = true;
-
- if (taiko->mbhc_polling_active) {
- taiko_codec_pause_hs_polling(codec);
- taiko_codec_disable_clock_block(codec);
- taiko_codec_enable_bandgap(codec,
- TAIKO_BANDGAP_AUDIO_MODE);
- taiko_codec_enable_clock_block(codec, 0);
- taiko_codec_calibrate_hs_polling(codec);
- taiko_codec_start_hs_polling(codec);
- } else {
- taiko_codec_disable_clock_block(codec);
- taiko_codec_enable_bandgap(codec,
- TAIKO_BANDGAP_AUDIO_MODE);
- taiko_codec_enable_clock_block(codec, 0);
- }
+ wcd9xxx_resmgr_get_bandgap(&taiko->resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
+ wcd9xxx_resmgr_get_clk_block(&taiko->resmgr, WCD9XXX_CLK_MCLK);
} else {
-
- if (!taiko->mclk_enabled) {
- if (dapm)
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- pr_err("Error, MCLK already diabled\n");
- return -EINVAL;
- }
- taiko->mclk_enabled = false;
-
- if (taiko->mbhc_polling_active) {
- taiko_codec_pause_hs_polling(codec);
- taiko_codec_disable_clock_block(codec);
- taiko_codec_enable_bandgap(codec,
- TAIKO_BANDGAP_MBHC_MODE);
- taiko_enable_rx_bias(codec, 1);
- taiko_codec_enable_clock_block(codec, 1);
- taiko_codec_calibrate_hs_polling(codec);
- taiko_codec_start_hs_polling(codec);
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1,
- 0x05, 0x01);
- } else {
- taiko_codec_disable_clock_block(codec);
- taiko_codec_enable_bandgap(codec,
- TAIKO_BANDGAP_OFF);
- }
+ /* Put clock and BG */
+ wcd9xxx_resmgr_put_clk_block(&taiko->resmgr, WCD9XXX_CLK_MCLK);
+ wcd9xxx_resmgr_put_bandgap(&taiko->resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
}
- if (dapm)
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
+ WCD9XXX_BCL_UNLOCK(&taiko->resmgr);
+
return 0;
}
@@ -3685,90 +3137,221 @@
{
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(dai->codec);
- u32 i = 0;
+ struct wcd9xxx *core = dev_get_drvdata(dai->codec->dev->parent);
if (!tx_slot && !rx_slot) {
pr_err("%s: Invalid\n", __func__);
return -EINVAL;
}
- pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n",
- __func__, dai->name, dai->id, tx_num, rx_num);
+ pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n"
+ "taiko->intf_type %d\n",
+ __func__, dai->name, dai->id, tx_num, rx_num,
+ taiko->intf_type);
- if (dai->id == AIF1_PB || dai->id == AIF2_PB || dai->id == AIF3_PB) {
- for (i = 0; i < rx_num; i++) {
- taiko->dai[dai->id - 1].ch_num[i] = rx_slot[i];
- taiko->dai[dai->id - 1].ch_act = 0;
- taiko->dai[dai->id - 1].ch_tot = rx_num;
+ if (taiko->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+ wcd9xxx_init_slimslave(core, core->slim->laddr,
+ tx_num, tx_slot, rx_num, rx_slot);
+ return 0;
+}
+
+static int taiko_get_channel_map(struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+
+{
+ struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(dai->codec);
+ u32 i = 0;
+ struct wcd9xxx_ch *ch;
+
+ switch (dai->id) {
+ case AIF1_PB:
+ case AIF2_PB:
+ case AIF3_PB:
+ if (!rx_slot || !rx_num) {
+ pr_err("%s: Invalid rx_slot %d or rx_num %d\n",
+ __func__, (u32) rx_slot, (u32) rx_num);
+ return -EINVAL;
}
- } else if (dai->id == AIF1_CAP || dai->id == AIF2_CAP ||
- dai->id == AIF3_CAP) {
- for (i = 0; i < tx_num; i++) {
- taiko->dai[dai->id - 1].ch_num[i] = tx_slot[i];
- taiko->dai[dai->id - 1].ch_act = 0;
- taiko->dai[dai->id - 1].ch_tot = tx_num;
+ list_for_each_entry(ch, &taiko_p->dai[dai->id].wcd9xxx_ch_list,
+ list) {
+ pr_debug("%s: tx_slot[%d] %d, ch->ch_num %d\n",
+ __func__, i, tx_slot[i], ch->ch_num);
+ rx_slot[i++] = ch->ch_num;
+ }
+ pr_debug("%s: rx_num %d\n", __func__, i);
+ *rx_num = i;
+ break;
+ case AIF1_CAP:
+ case AIF2_CAP:
+ case AIF3_CAP:
+ if (!tx_slot || !tx_num) {
+ pr_err("%s: Invalid tx_slot %d or tx_num %d\n",
+ __func__, (u32) tx_slot, (u32) tx_num);
+ return -EINVAL;
+ }
+ list_for_each_entry(ch, &taiko_p->dai[dai->id].wcd9xxx_ch_list,
+ list) {
+ pr_debug("%s: tx_slot[%d] %d, ch->ch_num %d\n",
+ __func__, i, tx_slot[i], ch->ch_num);
+ tx_slot[i++] = ch->ch_num;
+ }
+ pr_debug("%s: tx_num %d\n", __func__, i);
+ *tx_num = i;
+ break;
+
+ default:
+ pr_err("%s: Invalid DAI ID %x\n", __func__, dai->id);
+ break;
+ }
+
+ return 0;
+}
+
+static int taiko_set_interpolator_rate(struct snd_soc_dai *dai,
+ u8 rx_fs_rate_reg_val, u32 compander_fs, u32 sample_rate)
+{
+ u32 j;
+ u8 rx_mix1_inp;
+ u16 rx_mix_1_reg_1, rx_mix_1_reg_2;
+ u16 rx_fs_reg;
+ u8 rx_mix_1_reg_1_val, rx_mix_1_reg_2_val;
+ struct snd_soc_codec *codec = dai->codec;
+ struct wcd9xxx_ch *ch;
+ struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
+
+ list_for_each_entry(ch, &taiko->dai[dai->id].wcd9xxx_ch_list, list) {
+ /* for RX port starting from 16 instead of 10 like tabla */
+ rx_mix1_inp = ch->port + RX_MIX1_INP_SEL_RX1 -
+ TAIKO_TX_PORT_NUMBER;
+ if ((rx_mix1_inp < RX_MIX1_INP_SEL_RX1) ||
+ (rx_mix1_inp > RX_MIX1_INP_SEL_RX7)) {
+ pr_err("%s: Invalid TAIKO_RX%u port. Dai ID is %d\n",
+ __func__, rx_mix1_inp - 5 , dai->id);
+ return -EINVAL;
+ }
+
+ rx_mix_1_reg_1 = TAIKO_A_CDC_CONN_RX1_B1_CTL;
+
+ for (j = 0; j < NUM_INTERPOLATORS; j++) {
+ rx_mix_1_reg_2 = rx_mix_1_reg_1 + 1;
+
+ rx_mix_1_reg_1_val = snd_soc_read(codec,
+ rx_mix_1_reg_1);
+ rx_mix_1_reg_2_val = snd_soc_read(codec,
+ rx_mix_1_reg_2);
+
+ if (((rx_mix_1_reg_1_val & 0x0F) == rx_mix1_inp) ||
+ (((rx_mix_1_reg_1_val >> 4) & 0x0F)
+ == rx_mix1_inp) ||
+ ((rx_mix_1_reg_2_val & 0x0F) == rx_mix1_inp)) {
+
+ rx_fs_reg = TAIKO_A_CDC_RX1_B5_CTL + 8 * j;
+
+ pr_debug("%s: AIF_PB DAI(%d) connected to RX%u\n",
+ __func__, dai->id, j + 1);
+
+ pr_debug("%s: set RX%u sample rate to %u\n",
+ __func__, j + 1, sample_rate);
+
+ snd_soc_update_bits(codec, rx_fs_reg,
+ 0xE0, rx_fs_rate_reg_val);
+
+ if (comp_rx_path[j] < COMPANDER_MAX)
+ taiko->comp_fs[comp_rx_path[j]]
+ = compander_fs;
+ }
+ if (j <= 2)
+ rx_mix_1_reg_1 += 3;
+ else
+ rx_mix_1_reg_1 += 2;
}
}
return 0;
}
-static int taiko_get_channel_map(struct snd_soc_dai *dai,
- unsigned int *tx_num, unsigned int *tx_slot,
- unsigned int *rx_num, unsigned int *rx_slot)
-
+static int taiko_set_decimator_rate(struct snd_soc_dai *dai,
+ u8 tx_fs_rate_reg_val, u32 sample_rate)
{
- struct wcd9xxx *taiko = dev_get_drvdata(dai->codec->control_data);
+ struct snd_soc_codec *codec = dai->codec;
+ struct wcd9xxx_ch *ch;
+ struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
+ u32 tx_port;
+ u16 tx_port_reg, tx_fs_reg;
+ u8 tx_port_reg_val;
+ s8 decimator;
- u32 cnt = 0;
- u32 tx_ch[SLIM_MAX_TX_PORTS];
- u32 rx_ch[SLIM_MAX_RX_PORTS];
+ list_for_each_entry(ch, &taiko->dai[dai->id].wcd9xxx_ch_list, list) {
- if (!rx_slot && !tx_slot) {
- pr_err("%s: Invalid\n", __func__);
- return -EINVAL;
+ tx_port = ch->port + 1;
+ pr_debug("%s: dai->id = %d, tx_port = %d",
+ __func__, dai->id, tx_port);
+
+ if ((tx_port < 1) || (tx_port > NUM_DECIMATORS)) {
+ pr_err("%s: Invalid SLIM TX%u port. DAI ID is %d\n",
+ __func__, tx_port, dai->id);
+ return -EINVAL;
+ }
+
+ tx_port_reg = TAIKO_A_CDC_CONN_TX_SB_B1_CTL + (tx_port - 1);
+ tx_port_reg_val = snd_soc_read(codec, tx_port_reg);
+
+ decimator = 0;
+
+ if ((tx_port >= 1) && (tx_port <= 6)) {
+
+ tx_port_reg_val = tx_port_reg_val & 0x0F;
+ if (tx_port_reg_val == 0x8)
+ decimator = tx_port;
+
+ } else if ((tx_port >= 7) && (tx_port <= NUM_DECIMATORS)) {
+
+ tx_port_reg_val = tx_port_reg_val & 0x1F;
+
+ if ((tx_port_reg_val >= 0x8) &&
+ (tx_port_reg_val <= 0x11)) {
+
+ decimator = (tx_port_reg_val - 0x8) + 1;
+ }
+ }
+
+ if (decimator) { /* SLIM_TX port has a DEC as input */
+
+ tx_fs_reg = TAIKO_A_CDC_TX1_CLK_FS_CTL +
+ 8 * (decimator - 1);
+
+ pr_debug("%s: set DEC%u (-> SLIM_TX%u) rate to %u\n",
+ __func__, decimator, tx_port, sample_rate);
+
+ snd_soc_update_bits(codec, tx_fs_reg, 0x07,
+ tx_fs_rate_reg_val);
+
+ } else {
+ if ((tx_port_reg_val >= 0x1) &&
+ (tx_port_reg_val <= 0x7)) {
+
+ pr_debug("%s: RMIX%u going to SLIM TX%u\n",
+ __func__, tx_port_reg_val, tx_port);
+
+ } else if ((tx_port_reg_val >= 0x8) &&
+ (tx_port_reg_val <= 0x11)) {
+
+ pr_err("%s: ERROR: Should not be here\n",
+ __func__);
+ pr_err("%s: ERROR: DEC connected to SLIM TX%u\n",
+ __func__, tx_port);
+ return -EINVAL;
+
+ } else if (tx_port_reg_val == 0) {
+ pr_debug("%s: no signal to SLIM TX%u\n",
+ __func__, tx_port);
+ } else {
+ pr_err("%s: ERROR: wrong signal to SLIM TX%u\n",
+ __func__, tx_port);
+ pr_err("%s: ERROR: wrong signal = %u\n",
+ __func__, tx_port_reg_val);
+ return -EINVAL;
+ }
+ }
}
-
- /* for virtual port, codec driver needs to do
- * housekeeping, for now should be ok
- */
- wcd9xxx_get_channel(taiko, rx_ch, tx_ch);
- if (dai->id == AIF1_PB) {
- *rx_num = taiko_dai[dai->id - 1].playback.channels_max;
- while (cnt < *rx_num) {
- rx_slot[cnt] = rx_ch[cnt];
- cnt++;
- }
- } else if (dai->id == AIF1_CAP) {
- *tx_num = taiko_dai[dai->id - 1].capture.channels_max;
- while (cnt < *tx_num) {
- tx_slot[cnt] = tx_ch[6 + cnt];
- cnt++;
- }
- } else if (dai->id == AIF2_PB) {
- *rx_num = taiko_dai[dai->id - 1].playback.channels_max;
- while (cnt < *rx_num) {
- rx_slot[cnt] = rx_ch[5 + cnt];
- cnt++;
- }
- } else if (dai->id == AIF2_CAP) {
- *tx_num = taiko_dai[dai->id - 1].capture.channels_max;
- tx_slot[0] = tx_ch[cnt];
- tx_slot[1] = tx_ch[1 + cnt];
- tx_slot[2] = tx_ch[5 + cnt];
- tx_slot[3] = tx_ch[3 + cnt];
-
- } else if (dai->id == AIF3_PB) {
- *rx_num = taiko_dai[dai->id - 1].playback.channels_max;
- rx_slot[0] = rx_ch[3];
- rx_slot[1] = rx_ch[4];
-
- } else if (dai->id == AIF3_CAP) {
- *tx_num = taiko_dai[dai->id - 1].capture.channels_max;
- tx_slot[cnt] = tx_ch[2 + cnt];
- tx_slot[cnt + 1] = tx_ch[4 + cnt];
- }
- pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n",
- __func__, dai->name, dai->id, *tx_num, *rx_num);
-
-
return 0;
}
@@ -3778,10 +3361,9 @@
{
struct snd_soc_codec *codec = dai->codec;
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(dai->codec);
- u8 path, shift;
- u16 tx_fs_reg, rx_fs_reg;
- u8 tx_fs_rate, rx_fs_rate, rx_state, tx_state;
+ u8 tx_fs_rate, rx_fs_rate;
u32 compander_fs;
+ int ret;
pr_debug("%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__,
dai->name, dai->id, params_rate(params),
@@ -3820,37 +3402,20 @@
break;
default:
pr_err("%s: Invalid sampling rate %d\n", __func__,
- params_rate(params));
+ params_rate(params));
return -EINVAL;
}
-
- /**
- * If current dai is a tx dai, set sample rate to
- * all the txfe paths that are currently not active
- */
- if ((dai->id == AIF1_CAP) || (dai->id == AIF2_CAP) ||
- (dai->id == AIF3_CAP)) {
-
- tx_state = snd_soc_read(codec,
- TAIKO_A_CDC_CLK_TX_CLK_EN_B1_CTL);
-
- for (path = 1, shift = 0;
- path <= NUM_DECIMATORS; path++, shift++) {
-
- if (path == BITS_PER_REG + 1) {
- shift = 0;
- tx_state = snd_soc_read(codec,
- TAIKO_A_CDC_CLK_TX_CLK_EN_B2_CTL);
- }
-
- if (!(tx_state & (1 << shift))) {
- tx_fs_reg = TAIKO_A_CDC_TX1_CLK_FS_CTL
- + (BITS_PER_REG*(path-1));
- snd_soc_update_bits(codec, tx_fs_reg,
- 0x07, tx_fs_rate);
- }
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_CAPTURE:
+ ret = taiko_set_decimator_rate(dai, tx_fs_rate,
+ params_rate(params));
+ if (ret < 0) {
+ pr_err("%s: set decimator rate failed %d\n", __func__,
+ ret);
+ return ret;
}
+
if (taiko->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
@@ -3868,37 +3433,20 @@
break;
}
snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_TX_I2S_CTL,
- 0x07, tx_fs_rate);
+ 0x07, tx_fs_rate);
} else {
- taiko->dai[dai->id - 1].rate = params_rate(params);
+ taiko->dai[dai->id].rate = params_rate(params);
}
- }
- /**
- * TODO: Need to handle case where same RX chain takes 2 or more inputs
- * with varying sample rates
- */
+ break;
- /**
- * If current dai is a rx dai, set sample rate to
- * all the rx paths that are currently not active
- */
- if (dai->id == AIF1_PB || dai->id == AIF2_PB || dai->id == AIF3_PB) {
-
- rx_state = snd_soc_read(codec,
- TAIKO_A_CDC_CLK_RX_B1_CTL);
-
- for (path = 1, shift = 0;
- path <= NUM_INTERPOLATORS; path++, shift++) {
-
- if (!(rx_state & (1 << shift))) {
- rx_fs_reg = TAIKO_A_CDC_RX1_B5_CTL
- + (BITS_PER_REG*(path-1));
- snd_soc_update_bits(codec, rx_fs_reg,
- 0xE0, rx_fs_rate);
- if (comp_rx_path[shift] < COMPANDER_MAX)
- taiko->comp_fs[comp_rx_path[shift]]
- = compander_fs;
- }
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ ret = taiko_set_interpolator_rate(dai, rx_fs_rate,
+ compander_fs,
+ params_rate(params));
+ if (ret < 0) {
+ pr_err("%s: set decimator rate failed %d\n", __func__,
+ ret);
+ return ret;
}
if (taiko->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
switch (params_format(params)) {
@@ -3917,10 +3465,15 @@
break;
}
snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_RX_I2S_CTL,
- 0x03, (rx_fs_rate >> 0x05));
+ 0x03, (rx_fs_rate >> 0x05));
} else {
- taiko->dai[dai->id - 1].rate = params_rate(params);
+ taiko->dai[dai->id].rate = params_rate(params);
}
+ break;
+ default:
+ pr_err("%s: Invalid stream type %d\n", __func__,
+ substream->stream);
+ return -EINVAL;
}
return 0;
@@ -4026,7 +3579,7 @@
static struct snd_soc_dai_driver taiko_i2s_dai[] = {
{
.name = "taiko_i2s_rx1",
- .id = 1,
+ .id = AIF1_PB,
.playback = {
.stream_name = "AIF1 Playback",
.rates = WCD9320_RATES,
@@ -4040,7 +3593,7 @@
},
{
.name = "taiko_i2s_tx1",
- .id = 2,
+ .id = AIF1_CAP,
.capture = {
.stream_name = "AIF1 Capture",
.rates = WCD9320_RATES,
@@ -4055,125 +3608,77 @@
};
static int taiko_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol,
+ int event)
{
- struct wcd9xxx *taiko;
+ struct wcd9xxx *core;
struct snd_soc_codec *codec = w->codec;
struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(codec);
- u32 j = 0;
u32 ret = 0;
- codec->control_data = dev_get_drvdata(codec->dev->parent);
- taiko = codec->control_data;
+ struct wcd9xxx_codec_dai_data *dai;
+
+ core = dev_get_drvdata(codec->dev->parent);
+
+ pr_debug("%s: event called! codec name %s num_dai %d\n"
+ "stream name %s event %d\n",
+ __func__, w->codec->name, w->codec->num_dai, w->sname, event);
+
/* Execute the callback only if interface type is slimbus */
if (taiko_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS)
return 0;
- pr_debug("%s: %s %d\n", __func__, w->name, event);
+ dai = &taiko_p->dai[w->shift];
+ pr_debug("%s: w->name %s w->shift %d event %d\n",
+ __func__, w->name, w->shift, event);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- for (j = 0; j < ARRAY_SIZE(taiko_dai); j++) {
- if ((taiko_dai[j].id == AIF1_CAP) ||
- (taiko_dai[j].id == AIF2_CAP) ||
- (taiko_dai[j].id == AIF3_CAP))
- continue;
- if (!strncmp(w->sname,
- taiko_dai[j].playback.stream_name, 13)) {
- ++taiko_p->dai[j].ch_act;
- break;
- }
- }
- if (taiko_p->dai[j].ch_act == taiko_p->dai[j].ch_tot)
- ret = wcd9xxx_cfg_slim_sch_rx(taiko,
- taiko_p->dai[j].ch_num,
- taiko_p->dai[j].ch_tot,
- taiko_p->dai[j].rate);
+ ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+ dai->rate, dai->bit_width,
+ &dai->grph);
break;
case SND_SOC_DAPM_POST_PMD:
- for (j = 0; j < ARRAY_SIZE(taiko_dai); j++) {
- if ((taiko_dai[j].id == AIF1_CAP) ||
- (taiko_dai[j].id == AIF2_CAP) ||
- (taiko_dai[j].id == AIF3_CAP))
- continue;
- if (!strncmp(w->sname,
- taiko_dai[j].playback.stream_name, 13)) {
- --taiko_p->dai[j].ch_act;
- break;
- }
- }
- if (!taiko_p->dai[j].ch_act) {
- ret = wcd9xxx_close_slim_sch_rx(taiko,
- taiko_p->dai[j].ch_num,
- taiko_p->dai[j].ch_tot);
- usleep_range(15000, 15000);
- taiko_p->dai[j].rate = 0;
- memset(taiko_p->dai[j].ch_num, 0, (sizeof(u32)*
- taiko_p->dai[j].ch_tot));
- taiko_p->dai[j].ch_tot = 0;
- }
+ ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+ dai->grph);
+ usleep_range(15000, 15000);
+ break;
}
return ret;
}
static int taiko_codec_enable_slimtx(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol,
+ int event)
{
- struct wcd9xxx *taiko;
+ struct wcd9xxx *core;
struct snd_soc_codec *codec = w->codec;
struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(codec);
- /* index to the DAI ID, for now hardcoding */
- u32 j = 0;
u32 ret = 0;
+ struct wcd9xxx_codec_dai_data *dai;
- codec->control_data = dev_get_drvdata(codec->dev->parent);
- taiko = codec->control_data;
+ core = dev_get_drvdata(codec->dev->parent);
+
+ pr_debug("%s: event called! codec name %s num_dai %d stream name %s\n",
+ __func__, w->codec->name, w->codec->num_dai, w->sname);
/* Execute the callback only if interface type is slimbus */
if (taiko_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS)
return 0;
- pr_debug("%s(): %s %d\n", __func__, w->name, event);
+ pr_debug("%s(): w->name %s event %d w->shift %d\n",
+ __func__, w->name, event, w->shift);
+ dai = &taiko_p->dai[w->shift];
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- for (j = 0; j < ARRAY_SIZE(taiko_dai); j++) {
- if (taiko_dai[j].id == AIF1_PB ||
- taiko_dai[j].id == AIF2_PB ||
- taiko_dai[j].id == AIF3_PB)
- continue;
- if (!strncmp(w->sname,
- taiko_dai[j].capture.stream_name, 13)) {
- ++taiko_p->dai[j].ch_act;
- break;
- }
- }
- if (taiko_p->dai[j].ch_act == taiko_p->dai[j].ch_tot)
- ret = wcd9xxx_cfg_slim_sch_tx(taiko,
- taiko_p->dai[j].ch_num,
- taiko_p->dai[j].ch_tot,
- taiko_p->dai[j].rate);
+ ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+ dai->rate, dai->bit_width,
+ &dai->grph);
break;
case SND_SOC_DAPM_POST_PMD:
- for (j = 0; j < ARRAY_SIZE(taiko_dai); j++) {
- if (taiko_dai[j].id == AIF1_PB ||
- taiko_dai[j].id == AIF2_PB ||
- taiko_dai[j].id == AIF3_PB)
- continue;
- if (!strncmp(w->sname,
- taiko_dai[j].capture.stream_name, 13)) {
- --taiko_p->dai[j].ch_act;
- break;
- }
- }
- if (!taiko_p->dai[j].ch_act) {
- ret = wcd9xxx_close_slim_sch_tx(taiko,
- taiko_p->dai[j].ch_num,
- taiko_p->dai[j].ch_tot);
- taiko_p->dai[j].rate = 0;
- memset(taiko_p->dai[j].ch_num, 0, (sizeof(u32)*
- taiko_p->dai[j].ch_tot));
- taiko_p->dai[j].ch_tot = 0;
- }
+ ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+ dai->grph);
+ break;
}
return ret;
}
@@ -4213,29 +3718,38 @@
SND_SOC_DAPM_MIXER("DAC1", TAIKO_A_RX_EAR_EN, 6, 0, dac1_switch,
ARRAY_SIZE(dac1_switch)),
- SND_SOC_DAPM_AIF_IN_E("SLIM RX1", "AIF1 Playback", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimrx,
+ SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
+ AIF1_PB, 0, taiko_codec_enable_slimrx,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_AIF_IN_E("SLIM RX2", "AIF1 Playback", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimrx,
+ SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM,
+ AIF2_PB, 0, taiko_codec_enable_slimrx,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_AIF_IN_E("SLIM RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimrx,
+ SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM,
+ AIF3_PB, 0, taiko_codec_enable_slimrx,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_AIF_IN_E("SLIM RX4", "AIF3 Playback", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_AIF_IN_E("SLIM RX5", "AIF3 Playback", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, TAIKO_RX1, 0,
+ &slim_rx_mux[TAIKO_RX1]),
+ SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, TAIKO_RX2, 0,
+ &slim_rx_mux[TAIKO_RX2]),
+ SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, TAIKO_RX3, 0,
+ &slim_rx_mux[TAIKO_RX3]),
+ SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, TAIKO_RX4, 0,
+ &slim_rx_mux[TAIKO_RX4]),
+ SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, TAIKO_RX5, 0,
+ &slim_rx_mux[TAIKO_RX5]),
+ SND_SOC_DAPM_MUX("SLIM RX6 MUX", SND_SOC_NOPM, TAIKO_RX6, 0,
+ &slim_rx_mux[TAIKO_RX6]),
+ SND_SOC_DAPM_MUX("SLIM RX7 MUX", SND_SOC_NOPM, TAIKO_RX7, 0,
+ &slim_rx_mux[TAIKO_RX7]),
- SND_SOC_DAPM_AIF_IN_E("SLIM RX6", "AIF2 Playback", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_AIF_IN_E("SLIM RX7", "AIF2 Playback", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0),
/* Headphone */
SND_SOC_DAPM_OUTPUT("HEADPHONE"),
@@ -4297,13 +3811,17 @@
taiko_spk_dac_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("RX1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX2 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX7 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+
SND_SOC_DAPM_MIXER_E("RX1 MIX2", TAIKO_A_CDC_CLK_RX_B1_CTL, 0, 0, NULL,
0, taiko_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_MIXER_E("RX2 MIX2", TAIKO_A_CDC_CLK_RX_B1_CTL, 1, 0, NULL,
0, taiko_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU),
- SND_SOC_DAPM_MIXER_E("RX7 MIX2", TAIKO_A_CDC_CLK_RX_B1_CTL, 2, 0, NULL,
+ SND_SOC_DAPM_MIXER_E("RX3 MIX1", TAIKO_A_CDC_CLK_RX_B1_CTL, 2, 0, NULL,
0, taiko_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_MIXER_E("RX4 MIX1", TAIKO_A_CDC_CLK_RX_B1_CTL, 3, 0, NULL,
@@ -4315,14 +3833,10 @@
SND_SOC_DAPM_MIXER_E("RX6 MIX1", TAIKO_A_CDC_CLK_RX_B1_CTL, 5, 0, NULL,
0, taiko_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU),
- SND_SOC_DAPM_MIXER_E("RX7 MIX1", TAIKO_A_CDC_CLK_RX_B1_CTL, 6, 0, NULL,
+ SND_SOC_DAPM_MIXER_E("RX7 MIX2", TAIKO_A_CDC_CLK_RX_B1_CTL, 6, 0, NULL,
0, taiko_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU),
- SND_SOC_DAPM_MIXER("RX1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_MIXER("RX2 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_MIXER("RX3 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
-
SND_SOC_DAPM_MUX_E("RX4 DSM MUX", TAIKO_A_CDC_CLK_RX_B1_CTL, 3, 0,
&rx4_dsm_mux, taiko_codec_enable_interpolator,
SND_SOC_DAPM_PRE_PMU),
@@ -4536,55 +4050,47 @@
taiko_codec_enable_adc, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, 0, 0, &sb_tx1_mux),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX1", "AIF2 Capture", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM,
+ AIF1_CAP, 0, taiko_codec_enable_slimtx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, 0, 0, &sb_tx2_mux),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX2", "AIF2 Capture", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM,
+ AIF2_CAP, 0, taiko_codec_enable_slimtx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, 0, 0, &sb_tx3_mux),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX3", "AIF3 Capture", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM,
+ AIF3_CAP, 0, taiko_codec_enable_slimtx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, 0, 0, &sb_tx4_mux),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX4", "AIF2 Capture", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0,
+ aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
- SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, 0, 0, &sb_tx5_mux),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX5", "AIF3 Capture", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0,
+ aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
- SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, 0, 0, &sb_tx6_mux),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX6", "AIF2 Capture", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0,
+ aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
- SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, 0, 0, &sb_tx7_mux),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX7", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, 0, 0, &sb_tx8_mux),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX8", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
- 0, taiko_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_MUX("SLIM TX9 MUX", SND_SOC_NOPM, 0, 0, &sb_tx9_mux),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX9", "AIF1 Capture", NULL, SND_SOC_NOPM,
- 0, 0, taiko_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_MUX("SLIM TX10 MUX", SND_SOC_NOPM, 0, 0, &sb_tx10_mux),
- SND_SOC_DAPM_AIF_OUT_E("SLIM TX10", "AIF1 Capture", NULL, SND_SOC_NOPM,
- 0, 0, taiko_codec_enable_slimtx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, TAIKO_TX1, 0,
+ &sb_tx1_mux),
+ SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, TAIKO_TX2, 0,
+ &sb_tx2_mux),
+ SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, TAIKO_TX3, 0,
+ &sb_tx3_mux),
+ SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, TAIKO_TX4, 0,
+ &sb_tx4_mux),
+ SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, TAIKO_TX5, 0,
+ &sb_tx5_mux),
+ SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, TAIKO_TX6, 0,
+ &sb_tx6_mux),
+ SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, TAIKO_TX7, 0,
+ &sb_tx7_mux),
+ SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, TAIKO_TX8, 0,
+ &sb_tx8_mux),
+ SND_SOC_DAPM_MUX("SLIM TX9 MUX", SND_SOC_NOPM, TAIKO_TX9, 0,
+ &sb_tx9_mux),
+ SND_SOC_DAPM_MUX("SLIM TX10 MUX", SND_SOC_NOPM, TAIKO_TX10, 0,
+ &sb_tx10_mux),
/* Digital Mic Inputs */
SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
@@ -4648,2200 +4154,6 @@
};
-static short taiko_codec_read_sta_result(struct snd_soc_codec *codec)
-{
- u8 bias_msb, bias_lsb;
- short bias_value;
-
- bias_msb = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B3_STATUS);
- bias_lsb = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B2_STATUS);
- bias_value = (bias_msb << 8) | bias_lsb;
- return bias_value;
-}
-
-static short taiko_codec_read_dce_result(struct snd_soc_codec *codec)
-{
- u8 bias_msb, bias_lsb;
- short bias_value;
-
- bias_msb = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B5_STATUS);
- bias_lsb = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B4_STATUS);
- bias_value = (bias_msb << 8) | bias_lsb;
- return bias_value;
-}
-
-static void taiko_turn_onoff_rel_detection(struct snd_soc_codec *codec, bool on)
-{
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x02, on << 1);
-}
-
-static short __taiko_codec_sta_dce(struct snd_soc_codec *codec, int dce,
- bool override_bypass, bool noreldetection)
-{
- short bias_value;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- wcd9xxx_disable_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL);
- if (noreldetection)
- taiko_turn_onoff_rel_detection(codec, false);
-
- /* Turn on the override */
- if (!override_bypass)
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
- if (dce) {
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x4);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
- usleep_range(taiko->mbhc_data.t_sta_dce,
- taiko->mbhc_data.t_sta_dce);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x4);
- usleep_range(taiko->mbhc_data.t_dce,
- taiko->mbhc_data.t_dce);
- bias_value = taiko_codec_read_dce_result(codec);
- } else {
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x2);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
- usleep_range(taiko->mbhc_data.t_sta_dce,
- taiko->mbhc_data.t_sta_dce);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x2);
- usleep_range(taiko->mbhc_data.t_sta,
- taiko->mbhc_data.t_sta);
- bias_value = taiko_codec_read_sta_result(codec);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x0);
- }
- /* Turn off the override after measuring mic voltage */
- if (!override_bypass)
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
-
- if (noreldetection)
- taiko_turn_onoff_rel_detection(codec, true);
- wcd9xxx_enable_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL);
-
- return bias_value;
-}
-
-static short taiko_codec_sta_dce(struct snd_soc_codec *codec, int dce,
- bool norel)
-{
- return __taiko_codec_sta_dce(codec, dce, false, norel);
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static short taiko_codec_setup_hs_polling(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- short bias_value;
- u8 cfilt_mode;
-
- pr_debug("%s: enter, mclk_enabled %d\n", __func__, taiko->mclk_enabled);
- if (!taiko->mbhc_cfg.calibration) {
- pr_err("Error, no taiko calibration\n");
- return -ENODEV;
- }
-
- if (!taiko->mclk_enabled) {
- taiko_codec_disable_clock_block(codec);
- taiko_codec_enable_bandgap(codec, TAIKO_BANDGAP_MBHC_MODE);
- taiko_enable_rx_bias(codec, 1);
- taiko_codec_enable_clock_block(codec, 1);
- }
-
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x05, 0x01);
-
- /* Make sure CFILT is in fast mode, save current mode */
- cfilt_mode = snd_soc_read(codec, taiko->mbhc_bias_regs.cfilt_ctl);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl, 0x70, 0x00);
-
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x1F, 0x16);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
- snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x84);
-
- snd_soc_update_bits(codec, TAIKO_A_TX_7_MBHC_EN, 0x80, 0x80);
- snd_soc_update_bits(codec, TAIKO_A_TX_7_MBHC_EN, 0x1F, 0x1C);
- snd_soc_update_bits(codec, TAIKO_A_TX_7_MBHC_TEST_CTL, 0x40, 0x40);
-
- snd_soc_update_bits(codec, TAIKO_A_TX_7_MBHC_EN, 0x80, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x00);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x2, 0x2);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
-
- taiko_codec_calibrate_hs_polling(codec);
-
- /* don't flip override */
- bias_value = __taiko_codec_sta_dce(codec, 1, true, true);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl, 0x40,
- cfilt_mode);
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x13, 0x00);
-
- return bias_value;
-}
-
-static int taiko_cancel_btn_work(struct taiko_priv *taiko)
-{
- int r = 0;
- struct wcd9xxx *core = dev_get_drvdata(taiko->codec->dev->parent);
-
- if (cancel_delayed_work_sync(&taiko->mbhc_btn_dwork)) {
- /* if scheduled mbhc_btn_dwork is canceled from here,
- * we have to unlock from here instead btn_work */
- wcd9xxx_unlock_sleep(core);
- r = 1;
- }
- return r;
-}
-
-/* called under codec_resource_lock acquisition */
-void taiko_set_and_turnoff_hph_padac(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- u8 wg_time;
-
- wg_time = snd_soc_read(codec, TAIKO_A_RX_HPH_CNP_WG_TIME) ;
- wg_time += 1;
-
- /* If headphone PA is on, check if userspace receives
- * removal event to sync-up PA's state */
- if (taiko_is_hph_pa_on(codec)) {
- pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
- set_bit(TAIKO_HPHL_PA_OFF_ACK, &taiko->hph_pa_dac_state);
- set_bit(TAIKO_HPHR_PA_OFF_ACK, &taiko->hph_pa_dac_state);
- } else {
- pr_debug("%s PA is off\n", __func__);
- }
-
- if (taiko_is_hph_dac_on(codec, 1))
- set_bit(TAIKO_HPHL_DAC_OFF_ACK, &taiko->hph_pa_dac_state);
- if (taiko_is_hph_dac_on(codec, 0))
- set_bit(TAIKO_HPHR_DAC_OFF_ACK, &taiko->hph_pa_dac_state);
-
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_CNP_EN, 0x30, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_L_DAC_CTL,
- 0xC0, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_R_DAC_CTL,
- 0xC0, 0x00);
- usleep_range(wg_time * 1000, wg_time * 1000);
-}
-
-static void taiko_clr_and_turnon_hph_padac(struct taiko_priv *taiko)
-{
- bool pa_turned_on = false;
- struct snd_soc_codec *codec = taiko->codec;
- u8 wg_time;
-
- wg_time = snd_soc_read(codec, TAIKO_A_RX_HPH_CNP_WG_TIME) ;
- wg_time += 1;
-
- if (test_and_clear_bit(TAIKO_HPHR_DAC_OFF_ACK,
- &taiko->hph_pa_dac_state)) {
- pr_debug("%s: HPHR clear flag and enable DAC\n", __func__);
- snd_soc_update_bits(taiko->codec, TAIKO_A_RX_HPH_R_DAC_CTL,
- 0xC0, 0xC0);
- }
- if (test_and_clear_bit(TAIKO_HPHL_DAC_OFF_ACK,
- &taiko->hph_pa_dac_state)) {
- pr_debug("%s: HPHL clear flag and enable DAC\n", __func__);
- snd_soc_update_bits(taiko->codec, TAIKO_A_RX_HPH_L_DAC_CTL,
- 0xC0, 0xC0);
- }
-
- if (test_and_clear_bit(TAIKO_HPHR_PA_OFF_ACK,
- &taiko->hph_pa_dac_state)) {
- pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
- snd_soc_update_bits(taiko->codec, TAIKO_A_RX_HPH_CNP_EN, 0x10,
- 1 << 4);
- pa_turned_on = true;
- }
- if (test_and_clear_bit(TAIKO_HPHL_PA_OFF_ACK,
- &taiko->hph_pa_dac_state)) {
- pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
- snd_soc_update_bits(taiko->codec, TAIKO_A_RX_HPH_CNP_EN, 0x20,
- 1 << 5);
- pa_turned_on = true;
- }
-
- if (pa_turned_on) {
- pr_debug("%s: PA was turned off by MBHC and not by DAPM\n",
- __func__);
- usleep_range(wg_time * 1000, wg_time * 1000);
- }
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_report_plug(struct snd_soc_codec *codec, int insertion,
- enum snd_jack_types jack_type)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- if (!insertion) {
- /* Report removal */
- taiko->hph_status &= ~jack_type;
- if (taiko->mbhc_cfg.headset_jack) {
- /* cancel possibly scheduled btn work and
- * report release if we reported button press */
- if (taiko_cancel_btn_work(taiko)) {
- pr_debug("%s: button press is canceled\n",
- __func__);
- } else if (taiko->buttons_pressed) {
- pr_debug("%s: release of button press%d\n",
- __func__, jack_type);
- taiko_snd_soc_jack_report(taiko,
- taiko->mbhc_cfg.button_jack, 0,
- taiko->buttons_pressed);
- taiko->buttons_pressed &=
- ~TAIKO_JACK_BUTTON_MASK;
- }
- pr_debug("%s: Reporting removal %d(%x)\n", __func__,
- jack_type, taiko->hph_status);
- taiko_snd_soc_jack_report(taiko,
- taiko->mbhc_cfg.headset_jack,
- taiko->hph_status,
- TAIKO_JACK_MASK);
- }
- taiko_set_and_turnoff_hph_padac(codec);
- hphocp_off_report(taiko, SND_JACK_OC_HPHR,
- TAIKO_IRQ_HPH_PA_OCPR_FAULT);
- hphocp_off_report(taiko, SND_JACK_OC_HPHL,
- TAIKO_IRQ_HPH_PA_OCPL_FAULT);
- taiko->current_plug = PLUG_TYPE_NONE;
- taiko->mbhc_polling_active = false;
- } else {
- /* Report insertion */
- taiko->hph_status |= jack_type;
-
- if (jack_type == SND_JACK_HEADPHONE)
- taiko->current_plug = PLUG_TYPE_HEADPHONE;
- else if (jack_type == SND_JACK_UNSUPPORTED)
- taiko->current_plug = PLUG_TYPE_GND_MIC_SWAP;
- else if (jack_type == SND_JACK_HEADSET) {
- taiko->mbhc_polling_active = true;
- taiko->current_plug = PLUG_TYPE_HEADSET;
- }
- if (taiko->mbhc_cfg.headset_jack) {
- pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
- jack_type, taiko->hph_status);
- taiko_snd_soc_jack_report(taiko,
- taiko->mbhc_cfg.headset_jack,
- taiko->hph_status,
- TAIKO_JACK_MASK);
- }
- taiko_clr_and_turnon_hph_padac(taiko);
- }
-}
-
-static int taiko_codec_enable_hs_detect(struct snd_soc_codec *codec,
- int insertion, int trigger,
- bool padac_off)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- int central_bias_enabled = 0;
- const struct taiko_mbhc_general_cfg *generic =
- TAIKO_MBHC_CAL_GENERAL_PTR(taiko->mbhc_cfg.calibration);
- const struct taiko_mbhc_plug_detect_cfg *plug_det =
- TAIKO_MBHC_CAL_PLUG_DET_PTR(taiko->mbhc_cfg.calibration);
-
- if (!taiko->mbhc_cfg.calibration) {
- pr_err("Error, no taiko calibration\n");
- return -EINVAL;
- }
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x1, 0);
-
- /* Make sure mic bias and Mic line schmitt trigger
- * are turned OFF
- */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
-
- if (insertion) {
- taiko_codec_switch_micbias(codec, 0);
-
- /* DAPM can manipulate PA/DAC bits concurrently */
- if (padac_off == true)
- taiko_set_and_turnoff_hph_padac(codec);
-
- if (trigger & MBHC_USE_HPHL_TRIGGER) {
- /* Enable HPH Schmitt Trigger */
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x11,
- 0x11);
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x0C,
- plug_det->hph_current << 2);
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x02,
- 0x02);
- }
- if (trigger & MBHC_USE_MB_TRIGGER) {
- /* enable the mic line schmitt trigger */
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.mbhc_reg,
- 0x60, plug_det->mic_current << 5);
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.mbhc_reg,
- 0x80, 0x80);
- usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.ctl_reg, 0x01,
- 0x00);
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.mbhc_reg,
- 0x10, 0x10);
- }
-
- /* setup for insetion detection */
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x2, 0);
- } else {
- pr_debug("setup for removal detection\n");
- /* Make sure the HPH schmitt trigger is OFF */
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x12, 0x00);
-
- /* enable the mic line schmitt trigger */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg,
- 0x01, 0x00);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg, 0x60,
- plug_det->mic_current << 5);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
- 0x80, 0x80);
- usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
- 0x10, 0x10);
-
- /* Setup for low power removal detection */
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x2, 0x2);
- }
-
- if (snd_soc_read(codec, TAIKO_A_CDC_MBHC_B1_CTL) & 0x4) {
- /* called called by interrupt */
- if (!(taiko->clock_active)) {
- taiko_codec_enable_config_mode(codec, 1);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL,
- 0x06, 0);
- usleep_range(generic->t_shutdown_plug_rem,
- generic->t_shutdown_plug_rem);
- taiko_codec_enable_config_mode(codec, 0);
- } else
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL,
- 0x06, 0);
- }
-
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.int_rbias, 0x80, 0);
-
- /* If central bandgap disabled */
- if (!(snd_soc_read(codec, TAIKO_A_PIN_CTL_OE1) & 1)) {
- snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE1, 0x3, 0x3);
- usleep_range(generic->t_bg_fast_settle,
- generic->t_bg_fast_settle);
- central_bias_enabled = 1;
- }
-
- /* If LDO_H disabled */
- if (snd_soc_read(codec, TAIKO_A_PIN_CTL_OE0) & 0x80) {
- snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE0, 0x10, 0);
- snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE0, 0x80, 0x80);
- usleep_range(generic->t_ldoh, generic->t_ldoh);
- snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE0, 0x80, 0);
-
- if (central_bias_enabled)
- snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE1, 0x1, 0);
- }
-
- snd_soc_update_bits(codec, taiko->reg_addr.micb_4_mbhc, 0x3,
- taiko->mbhc_cfg.micbias);
-
- wcd9xxx_enable_irq(codec->control_data, TAIKO_IRQ_MBHC_INSERTION);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
- return 0;
-}
-
-static u16 taiko_codec_v_sta_dce(struct snd_soc_codec *codec, bool dce,
- s16 vin_mv)
-{
- struct taiko_priv *taiko;
- s16 diff, zero;
- u32 mb_mv, in;
- u16 value;
-
- taiko = snd_soc_codec_get_drvdata(codec);
- mb_mv = taiko->mbhc_data.micb_mv;
-
- if (mb_mv == 0) {
- pr_err("%s: Mic Bias voltage is set to zero\n", __func__);
- return -EINVAL;
- }
-
- if (dce) {
- diff = (taiko->mbhc_data.dce_mb) - (taiko->mbhc_data.dce_z);
- zero = (taiko->mbhc_data.dce_z);
- } else {
- diff = (taiko->mbhc_data.sta_mb) - (taiko->mbhc_data.sta_z);
- zero = (taiko->mbhc_data.sta_z);
- }
- in = (u32) diff * vin_mv;
-
- value = (u16) (in / mb_mv) + zero;
- return value;
-}
-
-static s32 taiko_codec_sta_dce_v(struct snd_soc_codec *codec, s8 dce,
- u16 bias_value)
-{
- struct taiko_priv *taiko;
- s16 value, z, mb;
- s32 mv;
-
- taiko = snd_soc_codec_get_drvdata(codec);
- value = bias_value;
- if (dce) {
- z = (taiko->mbhc_data.dce_z);
- mb = (taiko->mbhc_data.dce_mb);
- mv = (value - z) * (s32)taiko->mbhc_data.micb_mv / (mb - z);
- } else {
- z = (taiko->mbhc_data.sta_z);
- mb = (taiko->mbhc_data.sta_mb);
- mv = (value - z) * (s32)taiko->mbhc_data.micb_mv / (mb - z);
- }
-
- return mv;
-}
-
-static void btn_lpress_fn(struct work_struct *work)
-{
- struct delayed_work *delayed_work;
- struct taiko_priv *taiko;
- short bias_value;
- int dce_mv, sta_mv;
- struct wcd9xxx *core;
-
- pr_debug("%s:\n", __func__);
-
- delayed_work = to_delayed_work(work);
- taiko = container_of(delayed_work, struct taiko_priv, mbhc_btn_dwork);
- core = dev_get_drvdata(taiko->codec->dev->parent);
-
- if (taiko) {
- if (taiko->mbhc_cfg.button_jack) {
- bias_value = taiko_codec_read_sta_result(taiko->codec);
- sta_mv = taiko_codec_sta_dce_v(taiko->codec, 0,
- bias_value);
- bias_value = taiko_codec_read_dce_result(taiko->codec);
- dce_mv = taiko_codec_sta_dce_v(taiko->codec, 1,
- bias_value);
- pr_debug("%s: Reporting long button press event\n",
- __func__);
- pr_debug("%s: STA: %d, DCE: %d\n", __func__, sta_mv,
- dce_mv);
- taiko_snd_soc_jack_report(taiko,
- taiko->mbhc_cfg.button_jack,
- taiko->buttons_pressed,
- taiko->buttons_pressed);
- }
- } else {
- pr_err("%s: Bad taiko private data\n", __func__);
- }
-
- pr_debug("%s: leave\n", __func__);
- wcd9xxx_unlock_sleep(core);
-}
-
-void taiko_mbhc_cal(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko;
- struct taiko_mbhc_btn_detect_cfg *btn_det;
- u8 cfilt_mode, bg_mode;
- u8 ncic, nmeas, navg;
- u32 mclk_rate;
- u32 dce_wait, sta_wait;
- u8 *n_cic;
- void *calibration;
-
- taiko = snd_soc_codec_get_drvdata(codec);
- calibration = taiko->mbhc_cfg.calibration;
-
- wcd9xxx_disable_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL);
- taiko_turn_onoff_rel_detection(codec, false);
-
- /* First compute the DCE / STA wait times
- * depending on tunable parameters.
- * The value is computed in microseconds
- */
- btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(calibration);
- n_cic = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_CIC);
- ncic = n_cic[taiko_codec_mclk_index(taiko)];
- nmeas = TAIKO_MBHC_CAL_BTN_DET_PTR(calibration)->n_meas;
- navg = TAIKO_MBHC_CAL_GENERAL_PTR(calibration)->mbhc_navg;
- mclk_rate = taiko->mbhc_cfg.mclk_rate;
- dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (mclk_rate / 1000);
- sta_wait = (1000 * 128 * (navg + 1)) / (mclk_rate / 1000);
-
- taiko->mbhc_data.t_dce = dce_wait;
- taiko->mbhc_data.t_sta = sta_wait;
-
- /* LDOH and CFILT are already configured during pdata handling.
- * Only need to make sure CFILT and bandgap are in Fast mode.
- * Need to restore defaults once calculation is done.
- */
- cfilt_mode = snd_soc_read(codec, taiko->mbhc_bias_regs.cfilt_ctl);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl, 0x40, 0x00);
- bg_mode = snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x02,
- 0x02);
-
- /* Micbias, CFILT, LDOH, MBHC MUX mode settings
- * to perform ADC calibration
- */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x60,
- taiko->mbhc_cfg.micbias << 5);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_LDO_H_MODE_1, 0x60, 0x60);
- snd_soc_write(codec, TAIKO_A_TX_7_MBHC_TEST_CTL, 0x78);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x04, 0x04);
-
- /* DCE measurement for 0 volts */
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x0A);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x04);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x02);
- snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x81);
- usleep_range(100, 100);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x04);
- usleep_range(taiko->mbhc_data.t_dce, taiko->mbhc_data.t_dce);
- taiko->mbhc_data.dce_z = taiko_codec_read_dce_result(codec);
-
- /* DCE measurment for MB voltage */
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x0A);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x02);
- snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x82);
- usleep_range(100, 100);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x04);
- usleep_range(taiko->mbhc_data.t_dce, taiko->mbhc_data.t_dce);
- taiko->mbhc_data.dce_mb = taiko_codec_read_dce_result(codec);
-
- /* Sta measuremnt for 0 volts */
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x0A);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x02);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x02);
- snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x81);
- usleep_range(100, 100);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x02);
- usleep_range(taiko->mbhc_data.t_sta, taiko->mbhc_data.t_sta);
- taiko->mbhc_data.sta_z = taiko_codec_read_sta_result(codec);
-
- /* STA Measurement for MB Voltage */
- snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x82);
- usleep_range(100, 100);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x02);
- usleep_range(taiko->mbhc_data.t_sta, taiko->mbhc_data.t_sta);
- taiko->mbhc_data.sta_mb = taiko_codec_read_sta_result(codec);
-
- /* Restore default settings. */
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl, 0x40,
- cfilt_mode);
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x02, bg_mode);
-
- snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x84);
- usleep_range(100, 100);
-
- wcd9xxx_enable_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL);
- taiko_turn_onoff_rel_detection(codec, true);
-}
-
-void *taiko_mbhc_cal_btn_det_mp(const struct taiko_mbhc_btn_detect_cfg *btn_det,
- const enum taiko_mbhc_btn_det_mem mem)
-{
- void *ret = &btn_det->_v_btn_low;
-
- switch (mem) {
- case TAIKO_BTN_DET_GAIN:
- ret += sizeof(btn_det->_n_cic);
- case TAIKO_BTN_DET_N_CIC:
- ret += sizeof(btn_det->_n_ready);
- case TAIKO_BTN_DET_N_READY:
- ret += sizeof(btn_det->_v_btn_high[0]) * btn_det->num_btn;
- case TAIKO_BTN_DET_V_BTN_HIGH:
- ret += sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn;
- case TAIKO_BTN_DET_V_BTN_LOW:
- /* do nothing */
- break;
- default:
- ret = NULL;
- }
-
- return ret;
-}
-
-static s16 taiko_scale_v_micb_vddio(struct taiko_priv *taiko, int v,
- bool tovddio)
-{
- int r;
- int vddio_k, mb_k;
- vddio_k = taiko_find_k_value(taiko->pdata->micbias.ldoh_v,
- VDDIO_MICBIAS_MV);
- mb_k = taiko_find_k_value(taiko->pdata->micbias.ldoh_v,
- taiko->mbhc_data.micb_mv);
- if (tovddio)
- r = v * vddio_k / mb_k;
- else
- r = v * mb_k / vddio_k;
- return r;
-}
-
-static void taiko_mbhc_calc_thres(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko;
- s16 btn_mv = 0, btn_delta_mv;
- struct taiko_mbhc_btn_detect_cfg *btn_det;
- struct taiko_mbhc_plug_type_cfg *plug_type;
- u16 *btn_high;
- u8 *n_ready;
- int i;
-
- taiko = snd_soc_codec_get_drvdata(codec);
- btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(taiko->mbhc_cfg.calibration);
- plug_type = TAIKO_MBHC_CAL_PLUG_TYPE_PTR(taiko->mbhc_cfg.calibration);
-
- n_ready = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_READY);
- if (taiko->mbhc_cfg.mclk_rate == TAIKO_MCLK_RATE_12288KHZ) {
- taiko->mbhc_data.npoll = 4;
- taiko->mbhc_data.nbounce_wait = 30;
- } else if (taiko->mbhc_cfg.mclk_rate == TAIKO_MCLK_RATE_9600KHZ) {
- taiko->mbhc_data.npoll = 7;
- taiko->mbhc_data.nbounce_wait = 23;
- }
-
- taiko->mbhc_data.t_sta_dce = ((1000 * 256) /
- (taiko->mbhc_cfg.mclk_rate / 1000) *
- n_ready[taiko_codec_mclk_index(taiko)]) +
- 10;
- taiko->mbhc_data.v_ins_hu =
- taiko_codec_v_sta_dce(codec, STA, plug_type->v_hs_max);
- taiko->mbhc_data.v_ins_h =
- taiko_codec_v_sta_dce(codec, DCE, plug_type->v_hs_max);
-
- taiko->mbhc_data.v_inval_ins_low = TAIKO_MBHC_FAKE_INSERT_LOW;
- if (taiko->mbhc_cfg.gpio)
- taiko->mbhc_data.v_inval_ins_high =
- TAIKO_MBHC_FAKE_INSERT_HIGH;
- else
- taiko->mbhc_data.v_inval_ins_high =
- TAIKO_MBHC_FAKE_INS_HIGH_NO_GPIO;
-
- if (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
- taiko->mbhc_data.adj_v_hs_max =
- taiko_scale_v_micb_vddio(taiko, plug_type->v_hs_max, true);
- taiko->mbhc_data.adj_v_ins_hu =
- taiko_codec_v_sta_dce(codec, STA,
- taiko->mbhc_data.adj_v_hs_max);
- taiko->mbhc_data.adj_v_ins_h =
- taiko_codec_v_sta_dce(codec, DCE,
- taiko->mbhc_data.adj_v_hs_max);
- taiko->mbhc_data.v_inval_ins_low =
- taiko_scale_v_micb_vddio(taiko,
- taiko->mbhc_data.v_inval_ins_low,
- false);
- taiko->mbhc_data.v_inval_ins_high =
- taiko_scale_v_micb_vddio(taiko,
- taiko->mbhc_data.v_inval_ins_high,
- false);
- }
-
- btn_high = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_V_BTN_HIGH);
- for (i = 0; i < btn_det->num_btn; i++)
- btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv;
-
- taiko->mbhc_data.v_b1_h = taiko_codec_v_sta_dce(codec, DCE, btn_mv);
- btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_sta;
- taiko->mbhc_data.v_b1_hu =
- taiko_codec_v_sta_dce(codec, STA, btn_delta_mv);
-
- btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_cic;
-
- taiko->mbhc_data.v_b1_huc =
- taiko_codec_v_sta_dce(codec, DCE, btn_delta_mv);
-
- taiko->mbhc_data.v_brh = taiko->mbhc_data.v_b1_h;
- taiko->mbhc_data.v_brl = TAIKO_MBHC_BUTTON_MIN;
-
- taiko->mbhc_data.v_no_mic =
- taiko_codec_v_sta_dce(codec, STA, plug_type->v_no_mic);
-}
-
-void taiko_mbhc_init(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko;
- struct taiko_mbhc_general_cfg *generic;
- struct taiko_mbhc_btn_detect_cfg *btn_det;
- int n;
- u8 *n_cic, *gain;
-
- taiko = snd_soc_codec_get_drvdata(codec);
- generic = TAIKO_MBHC_CAL_GENERAL_PTR(taiko->mbhc_cfg.calibration);
- btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(taiko->mbhc_cfg.calibration);
-
- for (n = 0; n < 8; n++) {
- snd_soc_update_bits(codec,
- TAIKO_A_CDC_MBHC_FIR_B1_CFG,
- 0x07, n);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_FIR_B2_CFG,
- btn_det->c[n]);
- }
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B2_CTL, 0x07,
- btn_det->nc);
-
- n_cic = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_CIC);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_TIMER_B6_CTL, 0xFF,
- n_cic[taiko_codec_mclk_index(taiko)]);
-
- gain = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_GAIN);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B2_CTL, 0x78,
- gain[taiko_codec_mclk_index(taiko)] << 3);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
- generic->mbhc_nsa << 4);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_TIMER_B4_CTL, 0x0F,
- btn_det->n_meas);
-
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B5_CTL, generic->mbhc_navg);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x80, 0x80);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x78,
- btn_det->mbhc_nsc << 3);
-
- snd_soc_update_bits(codec, taiko->reg_addr.micb_4_mbhc, 0x03,
- TAIKO_MICBIAS2);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
-
- snd_soc_update_bits(codec, TAIKO_A_MBHC_SCALING_MUX_2, 0xF0, 0xF0);
-}
-
-static bool taiko_mbhc_fw_validate(const struct firmware *fw)
-{
- u32 cfg_offset;
- struct taiko_mbhc_imped_detect_cfg *imped_cfg;
- struct taiko_mbhc_btn_detect_cfg *btn_cfg;
-
- if (fw->size < TAIKO_MBHC_CAL_MIN_SIZE)
- return false;
-
- /* previous check guarantees that there is enough fw data up
- * to num_btn
- */
- btn_cfg = TAIKO_MBHC_CAL_BTN_DET_PTR(fw->data);
- cfg_offset = (u32) ((void *) btn_cfg - (void *) fw->data);
- if (fw->size < (cfg_offset + TAIKO_MBHC_CAL_BTN_SZ(btn_cfg)))
- return false;
-
- /* previous check guarantees that there is enough fw data up
- * to start of impedance detection configuration
- */
- imped_cfg = TAIKO_MBHC_CAL_IMPED_DET_PTR(fw->data);
- cfg_offset = (u32) ((void *) imped_cfg - (void *) fw->data);
-
- if (fw->size < (cfg_offset + TAIKO_MBHC_CAL_IMPED_MIN_SZ))
- return false;
-
- if (fw->size < (cfg_offset + TAIKO_MBHC_CAL_IMPED_SZ(imped_cfg)))
- return false;
-
- return true;
-}
-
-/* called under codec_resource_lock acquisition */
-static int taiko_determine_button(const struct taiko_priv *priv,
- const s32 micmv)
-{
- s16 *v_btn_low, *v_btn_high;
- struct taiko_mbhc_btn_detect_cfg *btn_det;
- int i, btn = -1;
-
- btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(priv->mbhc_cfg.calibration);
- v_btn_low = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_V_BTN_LOW);
- v_btn_high = taiko_mbhc_cal_btn_det_mp(btn_det,
- TAIKO_BTN_DET_V_BTN_HIGH);
-
- for (i = 0; i < btn_det->num_btn; i++) {
- if ((v_btn_low[i] <= micmv) && (v_btn_high[i] >= micmv)) {
- btn = i;
- break;
- }
- }
-
- if (btn == -1)
- pr_debug("%s: couldn't find button number for mic mv %d\n",
- __func__, micmv);
-
- return btn;
-}
-
-static int taiko_get_button_mask(const int btn)
-{
- int mask = 0;
- switch (btn) {
- case 0:
- mask = SND_JACK_BTN_0;
- break;
- case 1:
- mask = SND_JACK_BTN_1;
- break;
- case 2:
- mask = SND_JACK_BTN_2;
- break;
- case 3:
- mask = SND_JACK_BTN_3;
- break;
- case 4:
- mask = SND_JACK_BTN_4;
- break;
- case 5:
- mask = SND_JACK_BTN_5;
- break;
- case 6:
- mask = SND_JACK_BTN_6;
- break;
- case 7:
- mask = SND_JACK_BTN_7;
- break;
- }
- return mask;
-}
-
-static irqreturn_t taiko_dce_handler(int irq, void *data)
-{
- int i, mask;
- short dce, sta;
- s32 mv, mv_s, stamv_s;
- bool vddio;
- int btn = -1, meas = 0;
- struct taiko_priv *priv = data;
- const struct taiko_mbhc_btn_detect_cfg *d =
- TAIKO_MBHC_CAL_BTN_DET_PTR(priv->mbhc_cfg.calibration);
- short btnmeas[d->n_btn_meas + 1];
- struct snd_soc_codec *codec = priv->codec;
- struct wcd9xxx *core = dev_get_drvdata(priv->codec->dev->parent);
- int n_btn_meas = d->n_btn_meas;
- u8 mbhc_status = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B1_STATUS) & 0x3E;
-
- pr_debug("%s: enter\n", __func__);
-
- TAIKO_ACQUIRE_LOCK(priv->codec_resource_lock);
- if (priv->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) {
- pr_debug("%s: mbhc is being recovered, skip button press\n",
- __func__);
- goto done;
- }
-
- priv->mbhc_state = MBHC_STATE_POTENTIAL;
-
- if (!priv->mbhc_polling_active) {
- pr_warn("%s: mbhc polling is not active, skip button press\n",
- __func__);
- goto done;
- }
-
- dce = taiko_codec_read_dce_result(codec);
- mv = taiko_codec_sta_dce_v(codec, 1, dce);
-
- /* If GPIO interrupt already kicked in, ignore button press */
- if (priv->in_gpio_handler) {
- pr_debug("%s: GPIO State Changed, ignore button press\n",
- __func__);
- btn = -1;
- goto done;
- }
-
- vddio = (priv->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
- priv->mbhc_micbias_switched);
- mv_s = vddio ? taiko_scale_v_micb_vddio(priv, mv, false) : mv;
-
- if (mbhc_status != TAIKO_MBHC_STATUS_REL_DETECTION) {
- if (priv->mbhc_last_resume &&
- !time_after(jiffies, priv->mbhc_last_resume + HZ)) {
- pr_debug("%s: Button is already released shortly after resume\n",
- __func__);
- n_btn_meas = 0;
- } else {
- pr_debug("%s: Button is already released without resume",
- __func__);
- sta = taiko_codec_read_sta_result(codec);
- stamv_s = taiko_codec_sta_dce_v(codec, 0, sta);
- if (vddio)
- stamv_s = taiko_scale_v_micb_vddio(priv,
- stamv_s,
- false);
- btn = taiko_determine_button(priv, mv_s);
- if (btn != taiko_determine_button(priv, stamv_s))
- btn = -1;
- goto done;
- }
- }
-
- /* determine pressed button */
- btnmeas[meas++] = taiko_determine_button(priv, mv_s);
- pr_debug("%s: meas %d - DCE %d,%d,%d button %d\n", __func__,
- meas - 1, dce, mv, mv_s, btnmeas[meas - 1]);
- if (n_btn_meas == 0)
- btn = btnmeas[0];
- for (; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1))); meas++) {
- dce = taiko_codec_sta_dce(codec, 1, false);
- mv = taiko_codec_sta_dce_v(codec, 1, dce);
- mv_s = vddio ? taiko_scale_v_micb_vddio(priv, mv, false) : mv;
-
- btnmeas[meas] = taiko_determine_button(priv, mv_s);
- pr_debug("%s: meas %d - DCE %d,%d,%d button %d\n",
- __func__, meas, dce, mv, mv_s, btnmeas[meas]);
- /* if large enough measurements are collected,
- * start to check if last all n_btn_con measurements were
- * in same button low/high range */
- if (meas + 1 >= d->n_btn_con) {
- for (i = 0; i < d->n_btn_con; i++)
- if ((btnmeas[meas] < 0) ||
- (btnmeas[meas] != btnmeas[meas - i]))
- break;
- if (i == d->n_btn_con) {
- /* button pressed */
- btn = btnmeas[meas];
- break;
- } else if ((n_btn_meas - meas) < (d->n_btn_con - 1)) {
- /* if left measurements are less than n_btn_con,
- * it's impossible to find button number */
- break;
- }
- }
- }
-
- if (btn >= 0) {
- if (priv->in_gpio_handler) {
- pr_debug(
- "%s: GPIO already triggered, ignore button press\n",
- __func__);
- goto done;
- }
- mask = taiko_get_button_mask(btn);
- priv->buttons_pressed |= mask;
- wcd9xxx_lock_sleep(core);
- if (schedule_delayed_work(&priv->mbhc_btn_dwork,
- msecs_to_jiffies(400)) == 0) {
- WARN(1, "Button pressed twice without release event\n");
- wcd9xxx_unlock_sleep(core);
- }
- } else {
- pr_debug("%s: bogus button press, too short press?\n",
- __func__);
- }
-
- done:
- pr_debug("%s: leave\n", __func__);
- TAIKO_RELEASE_LOCK(priv->codec_resource_lock);
- return IRQ_HANDLED;
-}
-
-static int taiko_is_fake_press(struct taiko_priv *priv)
-{
- int i;
- int r = 0;
- struct snd_soc_codec *codec = priv->codec;
- const int dces = MBHC_NUM_DCE_PLUG_DETECT;
- s16 mb_v, v_ins_hu, v_ins_h;
-
- v_ins_hu = taiko_get_current_v_ins(priv, true);
- v_ins_h = taiko_get_current_v_ins(priv, false);
-
- for (i = 0; i < dces; i++) {
- usleep_range(10000, 10000);
- if (i == 0) {
- mb_v = taiko_codec_sta_dce(codec, 0, true);
- pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
- taiko_codec_sta_dce_v(codec, 0, mb_v));
- if (mb_v < (s16)priv->mbhc_data.v_b1_hu ||
- mb_v > v_ins_hu) {
- r = 1;
- break;
- }
- } else {
- mb_v = taiko_codec_sta_dce(codec, 1, true);
- pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
- taiko_codec_sta_dce_v(codec, 1, mb_v));
- if (mb_v < (s16)priv->mbhc_data.v_b1_h ||
- mb_v > v_ins_h) {
- r = 1;
- break;
- }
- }
- }
-
- return r;
-}
-
-static irqreturn_t taiko_release_handler(int irq, void *data)
-{
- int ret;
- struct taiko_priv *priv = data;
- struct snd_soc_codec *codec = priv->codec;
-
- pr_debug("%s: enter\n", __func__);
-
- TAIKO_ACQUIRE_LOCK(priv->codec_resource_lock);
- priv->mbhc_state = MBHC_STATE_RELEASE;
-
- taiko_codec_drive_v_to_micbias(codec, 10000);
-
- if (priv->buttons_pressed & TAIKO_JACK_BUTTON_MASK) {
- ret = taiko_cancel_btn_work(priv);
- if (ret == 0) {
- pr_debug("%s: Reporting long button release event\n",
- __func__);
- if (priv->mbhc_cfg.button_jack)
- taiko_snd_soc_jack_report(priv,
- priv->mbhc_cfg.button_jack, 0,
- priv->buttons_pressed);
- } else {
- if (taiko_is_fake_press(priv)) {
- pr_debug("%s: Fake button press interrupt\n",
- __func__);
- } else if (priv->mbhc_cfg.button_jack) {
- if (priv->in_gpio_handler) {
- pr_debug("%s: GPIO kicked in, ignore\n",
- __func__);
- } else {
- pr_debug(
- "%s: Reporting short button press and release\n",
- __func__);
- taiko_snd_soc_jack_report(priv,
- priv->mbhc_cfg.button_jack,
- priv->buttons_pressed,
- priv->buttons_pressed);
- taiko_snd_soc_jack_report(priv,
- priv->mbhc_cfg.button_jack, 0,
- priv->buttons_pressed);
- }
- }
- }
-
- priv->buttons_pressed &= ~TAIKO_JACK_BUTTON_MASK;
- }
-
- taiko_codec_calibrate_hs_polling(codec);
-
- if (priv->mbhc_cfg.gpio)
- msleep(TAIKO_MBHC_GPIO_REL_DEBOUNCE_TIME_MS);
-
- taiko_codec_start_hs_polling(codec);
-
- pr_debug("%s: leave\n", __func__);
- TAIKO_RELEASE_LOCK(priv->codec_resource_lock);
- return IRQ_HANDLED;
-}
-
-static void taiko_codec_shutdown_hs_removal_detect(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- const struct taiko_mbhc_general_cfg *generic =
- TAIKO_MBHC_CAL_GENERAL_PTR(taiko->mbhc_cfg.calibration);
-
- if (!taiko->mclk_enabled && !taiko->mbhc_polling_active)
- taiko_codec_enable_config_mode(codec, 1);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
-
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg, 0x80, 0x00);
-
- usleep_range(generic->t_shutdown_plug_rem,
- generic->t_shutdown_plug_rem);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
- if (!taiko->mclk_enabled && !taiko->mbhc_polling_active)
- taiko_codec_enable_config_mode(codec, 0);
-
- snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x00);
-}
-
-static void taiko_codec_cleanup_hs_polling(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- taiko_codec_shutdown_hs_removal_detect(codec);
-
- if (!taiko->mclk_enabled) {
- taiko_codec_disable_clock_block(codec);
- taiko_codec_enable_bandgap(codec, TAIKO_BANDGAP_OFF);
- }
-
- taiko->mbhc_polling_active = false;
- taiko->mbhc_state = MBHC_STATE_NONE;
-}
-
-static irqreturn_t taiko_hphl_ocp_irq(int irq, void *data)
-{
- struct taiko_priv *taiko = data;
- struct snd_soc_codec *codec;
-
- pr_info("%s: received HPHL OCP irq\n", __func__);
-
- if (taiko) {
- codec = taiko->codec;
- if (taiko->hphlocp_cnt++ < TAIKO_OCP_ATTEMPT) {
- pr_info("%s: retry\n", __func__);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10,
- 0x00);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10,
- 0x10);
- } else {
- wcd9xxx_disable_irq(codec->control_data,
- TAIKO_IRQ_HPH_PA_OCPL_FAULT);
- taiko->hphlocp_cnt = 0;
- taiko->hph_status |= SND_JACK_OC_HPHL;
- if (taiko->mbhc_cfg.headset_jack)
- taiko_snd_soc_jack_report(taiko,
- taiko->mbhc_cfg.headset_jack,
- taiko->hph_status,
- TAIKO_JACK_MASK);
- }
- } else {
- pr_err("%s: Bad taiko private data\n", __func__);
- }
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t taiko_hphr_ocp_irq(int irq, void *data)
-{
- struct taiko_priv *taiko = data;
- struct snd_soc_codec *codec;
-
- pr_info("%s: received HPHR OCP irq\n", __func__);
-
- if (taiko) {
- codec = taiko->codec;
- if (taiko->hphrocp_cnt++ < TAIKO_OCP_ATTEMPT) {
- pr_info("%s: retry\n", __func__);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10,
- 0x00);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10,
- 0x10);
- } else {
- wcd9xxx_disable_irq(codec->control_data,
- TAIKO_IRQ_HPH_PA_OCPR_FAULT);
- taiko->hphrocp_cnt = 0;
- taiko->hph_status |= SND_JACK_OC_HPHR;
- if (taiko->mbhc_cfg.headset_jack)
- taiko_snd_soc_jack_report(taiko,
- taiko->mbhc_cfg.headset_jack,
- taiko->hph_status,
- TAIKO_JACK_MASK);
- }
- } else {
- pr_err("%s: Bad taiko private data\n", __func__);
- }
-
- return IRQ_HANDLED;
-}
-
-static bool taiko_is_inval_ins_range(struct snd_soc_codec *codec,
- s32 mic_volt, bool highhph, bool *highv)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- bool invalid = false;
- s16 v_hs_max;
-
- /* Perform this check only when the high voltage headphone
- * needs to be considered as invalid
- */
- v_hs_max = taiko_get_current_v_hs_max(taiko);
- *highv = mic_volt > v_hs_max;
- if (!highhph && *highv)
- invalid = true;
- else if (mic_volt < taiko->mbhc_data.v_inval_ins_high &&
- (mic_volt > taiko->mbhc_data.v_inval_ins_low))
- invalid = true;
-
- return invalid;
-}
-
-static bool taiko_is_inval_ins_delta(struct snd_soc_codec *codec,
- int mic_volt, int mic_volt_prev,
- int threshold)
-{
- return abs(mic_volt - mic_volt_prev) > threshold;
-}
-
-/* called under codec_resource_lock acquisition */
-void taiko_find_plug_and_report(struct snd_soc_codec *codec,
- enum taiko_mbhc_plug_type plug_type)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- if (plug_type == PLUG_TYPE_HEADPHONE &&
- taiko->current_plug == PLUG_TYPE_NONE) {
- /* Nothing was reported previously
- * report a headphone or unsupported
- */
- taiko_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
- taiko_codec_cleanup_hs_polling(codec);
- } else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
- if (taiko->current_plug == PLUG_TYPE_HEADSET)
- taiko_codec_report_plug(codec, 0, SND_JACK_HEADSET);
- else if (taiko->current_plug == PLUG_TYPE_HEADPHONE)
- taiko_codec_report_plug(codec, 0, SND_JACK_HEADPHONE);
-
- taiko_codec_report_plug(codec, 1, SND_JACK_UNSUPPORTED);
- taiko_codec_cleanup_hs_polling(codec);
- } else if (plug_type == PLUG_TYPE_HEADSET) {
- /* If Headphone was reported previously, this will
- * only report the mic line
- */
- taiko_codec_report_plug(codec, 1, SND_JACK_HEADSET);
- msleep(100);
- taiko_codec_start_hs_polling(codec);
- } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
- if (taiko->current_plug == PLUG_TYPE_NONE)
- taiko_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
- taiko_codec_cleanup_hs_polling(codec);
- pr_debug("setup mic trigger for further detection\n");
- taiko->lpi_enabled = true;
- taiko_codec_enable_hs_detect(codec, 1,
- MBHC_USE_MB_TRIGGER |
- MBHC_USE_HPHL_TRIGGER,
- false);
- } else {
- WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
- taiko->current_plug, plug_type);
- }
-}
-
-/* should be called under interrupt context that hold suspend */
-static void taiko_schedule_hs_detect_plug(struct taiko_priv *taiko)
-{
- pr_debug("%s: scheduling taiko_hs_correct_gpio_plug\n", __func__);
- taiko->hs_detect_work_stop = false;
- wcd9xxx_lock_sleep(taiko->codec->control_data);
- schedule_work(&taiko->hs_correct_plug_work);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_cancel_hs_detect_plug(struct taiko_priv *taiko)
-{
- pr_debug("%s: canceling hs_correct_plug_work\n", __func__);
- taiko->hs_detect_work_stop = true;
- wmb();
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- if (cancel_work_sync(&taiko->hs_correct_plug_work)) {
- pr_debug("%s: hs_correct_plug_work is canceled\n", __func__);
- wcd9xxx_unlock_sleep(taiko->codec->control_data);
- }
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-}
-
-static bool taiko_hs_gpio_level_remove(struct taiko_priv *taiko)
-{
- return (gpio_get_value_cansleep(taiko->mbhc_cfg.gpio) !=
- taiko->mbhc_cfg.gpio_level_insert);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_hphr_gnd_switch(struct snd_soc_codec *codec, bool on)
-{
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x01, on);
- if (on)
- usleep_range(5000, 5000);
-}
-
-/* called under codec_resource_lock acquisition and mbhc override = 1 */
-static enum taiko_mbhc_plug_type
-taiko_codec_get_plug_type(struct snd_soc_codec *codec, bool highhph)
-{
- int i;
- bool gndswitch, vddioswitch;
- int scaled;
- struct taiko_mbhc_plug_type_cfg *plug_type_ptr;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- const bool vddio = (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV);
- int num_det = (MBHC_NUM_DCE_PLUG_DETECT + vddio);
- enum taiko_mbhc_plug_type plug_type[num_det];
- s16 mb_v[num_det];
- s32 mic_mv[num_det];
- bool inval;
- bool highdelta;
- bool ahighv = false, highv;
-
- /* make sure override is on */
- WARN_ON(!(snd_soc_read(codec, TAIKO_A_CDC_MBHC_B1_CTL) & 0x04));
-
- /* GND and MIC swap detection requires at least 2 rounds of DCE */
- BUG_ON(num_det < 2);
-
- plug_type_ptr =
- TAIKO_MBHC_CAL_PLUG_TYPE_PTR(taiko->mbhc_cfg.calibration);
-
- plug_type[0] = PLUG_TYPE_INVALID;
-
- /* performs DCEs for N times
- * 1st: check if voltage is in invalid range
- * 2nd - N-2nd: check voltage range and delta
- * N-1st: check voltage range, delta with HPHR GND switch
- * Nth: check voltage range with VDDIO switch if micbias V != vddio V*/
- for (i = 0; i < num_det; i++) {
- gndswitch = (i == (num_det - 1 - vddio));
- vddioswitch = (vddio && ((i == num_det - 1) ||
- (i == num_det - 2)));
- if (i == 0) {
- mb_v[i] = taiko_codec_setup_hs_polling(codec);
- mic_mv[i] = taiko_codec_sta_dce_v(codec, 1 , mb_v[i]);
- inval = taiko_is_inval_ins_range(codec, mic_mv[i],
- highhph, &highv);
- ahighv |= highv;
- scaled = mic_mv[i];
- } else {
- if (vddioswitch)
- __taiko_codec_switch_micbias(taiko->codec, 1,
- false, false);
- if (gndswitch)
- taiko_codec_hphr_gnd_switch(codec, true);
- mb_v[i] = __taiko_codec_sta_dce(codec, 1, true, true);
- mic_mv[i] = taiko_codec_sta_dce_v(codec, 1 , mb_v[i]);
- if (vddioswitch)
- scaled = taiko_scale_v_micb_vddio(taiko,
- mic_mv[i],
- false);
- else
- scaled = mic_mv[i];
- /* !gndswitch & vddioswitch means the previous DCE
- * was done with gndswitch, don't compare with DCE
- * with gndswitch */
- highdelta = taiko_is_inval_ins_delta(codec, scaled,
- mic_mv[i - !gndswitch - vddioswitch],
- TAIKO_MBHC_FAKE_INS_DELTA_SCALED_MV);
- inval = (taiko_is_inval_ins_range(codec, mic_mv[i],
- highhph, &highv) ||
- highdelta);
- ahighv |= highv;
- if (gndswitch)
- taiko_codec_hphr_gnd_switch(codec, false);
- if (vddioswitch)
- __taiko_codec_switch_micbias(taiko->codec, 0,
- false, false);
- /* claim UNSUPPORTED plug insertion when
- * good headset is detected but HPHR GND switch makes
- * delta difference */
- if (i == (num_det - 2) && highdelta && !ahighv)
- plug_type[0] = PLUG_TYPE_GND_MIC_SWAP;
- else if (i == (num_det - 1) && inval)
- plug_type[0] = PLUG_TYPE_INVALID;
- }
- pr_debug("%s: DCE #%d, %04x, V %d, scaled V %d, GND %d, VDDIO %d, inval %d\n",
- __func__, i + 1, mb_v[i] & 0xffff, mic_mv[i], scaled,
- gndswitch, vddioswitch, inval);
- /* don't need to run further DCEs */
- if (ahighv && inval)
- break;
- mic_mv[i] = scaled;
- }
-
- for (i = 0; (plug_type[0] != PLUG_TYPE_GND_MIC_SWAP && !inval) &&
- i < num_det; i++) {
- /*
- * If we are here, means none of the all
- * measurements are fake, continue plug type detection.
- * If all three measurements do not produce same
- * plug type, restart insertion detection
- */
- if (mic_mv[i] < plug_type_ptr->v_no_mic) {
- plug_type[i] = PLUG_TYPE_HEADPHONE;
- pr_debug("%s: Detect attempt %d, detected Headphone\n",
- __func__, i);
- } else if (highhph && (mic_mv[i] > plug_type_ptr->v_hs_max)) {
- plug_type[i] = PLUG_TYPE_HIGH_HPH;
- pr_debug(
- "%s: Detect attempt %d, detected High Headphone\n",
- __func__, i);
- } else {
- plug_type[i] = PLUG_TYPE_HEADSET;
- pr_debug("%s: Detect attempt %d, detected Headset\n",
- __func__, i);
- }
-
- if (i > 0 && (plug_type[i - 1] != plug_type[i])) {
- pr_err("%s: Detect attempt %d and %d are not same",
- __func__, i - 1, i);
- plug_type[0] = PLUG_TYPE_INVALID;
- inval = true;
- break;
- }
- }
-
- pr_debug("%s: Detected plug type %d\n", __func__, plug_type[0]);
- return plug_type[0];
-}
-
-static void taiko_hs_correct_gpio_plug(struct work_struct *work)
-{
- struct taiko_priv *taiko;
- struct snd_soc_codec *codec;
- int retry = 0, pt_gnd_mic_swap_cnt = 0;
- bool correction = false;
- enum taiko_mbhc_plug_type plug_type;
- unsigned long timeout;
-
- taiko = container_of(work, struct taiko_priv, hs_correct_plug_work);
- codec = taiko->codec;
-
- pr_debug("%s: enter\n", __func__);
- taiko->mbhc_cfg.mclk_cb_fn(codec, 1, false);
-
- /* Keep override on during entire plug type correction work.
- *
- * This is okay under the assumption that any GPIO irqs which use
- * MBHC block cancel and sync this work so override is off again
- * prior to GPIO interrupt handler's MBHC block usage.
- * Also while this correction work is running, we can guarantee
- * DAPM doesn't use any MBHC block as this work only runs with
- * headphone detection.
- */
- taiko_turn_onoff_override(codec, true);
-
- timeout = jiffies + msecs_to_jiffies(TAIKO_HS_DETECT_PLUG_TIME_MS);
- while (!time_after(jiffies, timeout)) {
- ++retry;
- rmb();
- if (taiko->hs_detect_work_stop) {
- pr_debug("%s: stop requested\n", __func__);
- break;
- }
-
- msleep(TAIKO_HS_DETECT_PLUG_INERVAL_MS);
- if (taiko_hs_gpio_level_remove(taiko)) {
- pr_debug("%s: GPIO value is low\n", __func__);
- break;
- }
-
- /* can race with removal interrupt */
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- plug_type = taiko_codec_get_plug_type(codec, true);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-
- if (plug_type == PLUG_TYPE_INVALID) {
- pr_debug("Invalid plug in attempt # %d\n", retry);
- if (retry == NUM_ATTEMPTS_TO_REPORT &&
- taiko->current_plug == PLUG_TYPE_NONE) {
- taiko_codec_report_plug(codec, 1,
- SND_JACK_HEADPHONE);
- }
- } else if (plug_type == PLUG_TYPE_HEADPHONE) {
- pr_debug("Good headphone detected, continue polling mic\n");
- if (taiko->current_plug == PLUG_TYPE_NONE)
- taiko_codec_report_plug(codec, 1,
- SND_JACK_HEADPHONE);
- } else {
- if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
- pt_gnd_mic_swap_cnt++;
- if (pt_gnd_mic_swap_cnt <
- TAIKO_MBHC_GND_MIC_SWAP_THRESHOLD)
- continue;
- else if (pt_gnd_mic_swap_cnt >
- TAIKO_MBHC_GND_MIC_SWAP_THRESHOLD) {
- /* This is due to GND/MIC switch didn't
- * work, Report unsupported plug */
- } else if (taiko->mbhc_cfg.swap_gnd_mic) {
- /* if switch is toggled, check again,
- * otherwise report unsupported plug */
- if (taiko->mbhc_cfg.swap_gnd_mic(codec))
- continue;
- }
- } else
- pt_gnd_mic_swap_cnt = 0;
-
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- /* Turn off override */
- taiko_turn_onoff_override(codec, false);
- /* The valid plug also includes PLUG_TYPE_GND_MIC_SWAP
- */
- taiko_find_plug_and_report(codec, plug_type);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- pr_debug("Attempt %d found correct plug %d\n", retry,
- plug_type);
- correction = true;
- break;
- }
- }
-
- /* Turn off override */
- if (!correction)
- taiko_turn_onoff_override(codec, false);
-
- taiko->mbhc_cfg.mclk_cb_fn(codec, 0, false);
- pr_debug("%s: leave\n", __func__);
- /* unlock sleep */
- wcd9xxx_unlock_sleep(taiko->codec->control_data);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_decide_gpio_plug(struct snd_soc_codec *codec)
-{
- enum taiko_mbhc_plug_type plug_type;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- pr_debug("%s: enter\n", __func__);
-
- taiko_turn_onoff_override(codec, true);
- plug_type = taiko_codec_get_plug_type(codec, true);
- taiko_turn_onoff_override(codec, false);
-
- if (taiko_hs_gpio_level_remove(taiko)) {
- pr_debug("%s: GPIO value is low when determining plug\n",
- __func__);
- return;
- }
-
- if (plug_type == PLUG_TYPE_INVALID ||
- plug_type == PLUG_TYPE_GND_MIC_SWAP) {
- taiko_schedule_hs_detect_plug(taiko);
- } else if (plug_type == PLUG_TYPE_HEADPHONE) {
- taiko_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
-
- taiko_schedule_hs_detect_plug(taiko);
- } else {
- pr_debug("%s: Valid plug found, determine plug type %d\n",
- __func__, plug_type);
- taiko_find_plug_and_report(codec, plug_type);
- }
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_detect_plug_type(struct snd_soc_codec *codec)
-{
- enum taiko_mbhc_plug_type plug_type;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- const struct taiko_mbhc_plug_detect_cfg *plug_det =
- TAIKO_MBHC_CAL_PLUG_DET_PTR(taiko->mbhc_cfg.calibration);
-
- /* Turn on the override,
- * taiko_codec_setup_hs_polling requires override on */
- taiko_turn_onoff_override(codec, true);
-
- if (plug_det->t_ins_complete > 20)
- msleep(plug_det->t_ins_complete);
- else
- usleep_range(plug_det->t_ins_complete * 1000,
- plug_det->t_ins_complete * 1000);
-
- if (taiko->mbhc_cfg.gpio) {
- /* Turn off the override */
- taiko_turn_onoff_override(codec, false);
- if (taiko_hs_gpio_level_remove(taiko))
- pr_debug(
- "%s: GPIO value is low when determining plug\n",
- __func__);
- else
- taiko_codec_decide_gpio_plug(codec);
- return;
- }
-
- plug_type = taiko_codec_get_plug_type(codec, false);
- taiko_turn_onoff_override(codec, false);
-
- if (plug_type == PLUG_TYPE_INVALID) {
- pr_debug("%s: Invalid plug type detected\n", __func__);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
- taiko_codec_cleanup_hs_polling(codec);
- taiko_codec_enable_hs_detect(codec, 1,
- MBHC_USE_MB_TRIGGER |
- MBHC_USE_HPHL_TRIGGER, false);
- } else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
- pr_debug("%s: GND-MIC swapped plug type detected\n", __func__);
- taiko_codec_report_plug(codec, 1, SND_JACK_UNSUPPORTED);
- taiko_codec_cleanup_hs_polling(codec);
- taiko_codec_enable_hs_detect(codec, 0, 0, false);
- } else if (plug_type == PLUG_TYPE_HEADPHONE) {
- pr_debug("%s: Headphone Detected\n", __func__);
- taiko_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
- taiko_codec_cleanup_hs_polling(codec);
- taiko_codec_enable_hs_detect(codec, 0, 0, false);
- } else if (plug_type == PLUG_TYPE_HEADSET) {
- pr_debug("%s: Headset detected\n", __func__);
- taiko_codec_report_plug(codec, 1, SND_JACK_HEADSET);
-
- /* avoid false button press detect */
- msleep(50);
- taiko_codec_start_hs_polling(codec);
- }
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static void taiko_hs_insert_irq_gpio(struct taiko_priv *priv, bool is_removal)
-{
- struct snd_soc_codec *codec = priv->codec;
-
- if (!is_removal) {
- pr_debug("%s: MIC trigger insertion interrupt\n", __func__);
-
- rmb();
- if (priv->lpi_enabled)
- msleep(100);
-
- rmb();
- if (!priv->lpi_enabled) {
- pr_debug("%s: lpi is disabled\n", __func__);
- } else if (gpio_get_value_cansleep(priv->mbhc_cfg.gpio) ==
- priv->mbhc_cfg.gpio_level_insert) {
- pr_debug(
- "%s: Valid insertion, detect plug type\n", __func__);
- taiko_codec_decide_gpio_plug(codec);
- } else {
- pr_debug(
- "%s: Invalid insertion stop plug detection\n",
- __func__);
- }
- } else {
- pr_err("%s: GPIO used, invalid MBHC Removal\n", __func__);
- }
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static void taiko_hs_insert_irq_nogpio(struct taiko_priv *priv, bool is_removal,
- bool is_mb_trigger)
-{
- int ret;
- struct snd_soc_codec *codec = priv->codec;
- struct wcd9xxx *core = dev_get_drvdata(priv->codec->dev->parent);
-
- if (is_removal) {
- /* cancel possiblely running hs detect work */
- taiko_cancel_hs_detect_plug(priv);
-
- /*
- * If headphone is removed while playback is in progress,
- * it is possible that micbias will be switched to VDDIO.
- */
- taiko_codec_switch_micbias(codec, 0);
- if (priv->current_plug == PLUG_TYPE_HEADPHONE)
- taiko_codec_report_plug(codec, 0, SND_JACK_HEADPHONE);
- else if (priv->current_plug == PLUG_TYPE_GND_MIC_SWAP)
- taiko_codec_report_plug(codec, 0, SND_JACK_UNSUPPORTED);
- else
- WARN(1, "%s: Unexpected current plug type %d\n",
- __func__, priv->current_plug);
- taiko_codec_shutdown_hs_removal_detect(codec);
- taiko_codec_enable_hs_detect(codec, 1,
- MBHC_USE_MB_TRIGGER |
- MBHC_USE_HPHL_TRIGGER,
- true);
- } else if (is_mb_trigger && !is_removal) {
- pr_debug("%s: Waiting for Headphone left trigger\n",
- __func__);
- wcd9xxx_lock_sleep(core);
- if (schedule_delayed_work(&priv->mbhc_insert_dwork,
- usecs_to_jiffies(1000000)) == 0) {
- pr_err("%s: mbhc_insert_dwork is already scheduled\n",
- __func__);
- wcd9xxx_unlock_sleep(core);
- }
- taiko_codec_enable_hs_detect(codec, 1, MBHC_USE_HPHL_TRIGGER,
- false);
- } else {
- ret = cancel_delayed_work(&priv->mbhc_insert_dwork);
- if (ret != 0) {
- pr_debug(
- "%s: Complete plug insertion, Detecting plug type\n",
- __func__);
- taiko_codec_detect_plug_type(codec);
- wcd9xxx_unlock_sleep(core);
- } else {
- wcd9xxx_enable_irq(codec->control_data,
- TAIKO_IRQ_MBHC_INSERTION);
- pr_err("%s: Error detecting plug insertion\n",
- __func__);
- }
- }
-}
-
-static irqreturn_t taiko_hs_insert_irq(int irq, void *data)
-{
- bool is_mb_trigger, is_removal;
- struct taiko_priv *priv = data;
- struct snd_soc_codec *codec = priv->codec;
-
- pr_debug("%s: enter\n", __func__);
- TAIKO_ACQUIRE_LOCK(priv->codec_resource_lock);
- wcd9xxx_disable_irq(codec->control_data, TAIKO_IRQ_MBHC_INSERTION);
-
- is_mb_trigger = !!(snd_soc_read(codec, priv->mbhc_bias_regs.mbhc_reg) &
- 0x10);
- is_removal = !!(snd_soc_read(codec, TAIKO_A_CDC_MBHC_INT_CTL) & 0x02);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
-
- /* Turn off both HPH and MIC line schmitt triggers */
- snd_soc_update_bits(codec, priv->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x13, 0x00);
- snd_soc_update_bits(codec, priv->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
-
- if (priv->mbhc_cfg.gpio)
- taiko_hs_insert_irq_gpio(priv, is_removal);
- else
- taiko_hs_insert_irq_nogpio(priv, is_removal, is_mb_trigger);
-
- TAIKO_RELEASE_LOCK(priv->codec_resource_lock);
- return IRQ_HANDLED;
-}
-
-static bool is_valid_mic_voltage(struct snd_soc_codec *codec, s32 mic_mv)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- const struct taiko_mbhc_plug_type_cfg *plug_type =
- TAIKO_MBHC_CAL_PLUG_TYPE_PTR(taiko->mbhc_cfg.calibration);
- const s16 v_hs_max = taiko_get_current_v_hs_max(taiko);
-
- return (!(mic_mv > 10 && mic_mv < 80) && (mic_mv > plug_type->v_no_mic)
- && (mic_mv < v_hs_max)) ? true : false;
-}
-
-/* called under codec_resource_lock acquisition
- * returns true if mic voltage range is back to normal insertion
- * returns false either if timedout or removed */
-static bool taiko_hs_remove_settle(struct snd_soc_codec *codec)
-{
- int i;
- bool timedout, settled = false;
- s32 mic_mv[MBHC_NUM_DCE_PLUG_DETECT];
- short mb_v[MBHC_NUM_DCE_PLUG_DETECT];
- unsigned long retry = 0, timeout;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- const s16 v_hs_max = taiko_get_current_v_hs_max(taiko);
-
- timeout = jiffies + msecs_to_jiffies(TAIKO_HS_DETECT_PLUG_TIME_MS);
- while (!(timedout = time_after(jiffies, timeout))) {
- retry++;
- if (taiko->mbhc_cfg.gpio && taiko_hs_gpio_level_remove(taiko)) {
- pr_debug("%s: GPIO indicates removal\n", __func__);
- break;
- }
-
- if (taiko->mbhc_cfg.gpio) {
- if (retry > 1)
- msleep(250);
- else
- msleep(50);
- }
-
- if (taiko->mbhc_cfg.gpio && taiko_hs_gpio_level_remove(taiko)) {
- pr_debug("%s: GPIO indicates removal\n", __func__);
- break;
- }
-
- for (i = 0; i < MBHC_NUM_DCE_PLUG_DETECT; i++) {
- mb_v[i] = taiko_codec_sta_dce(codec, 1, true);
- mic_mv[i] = taiko_codec_sta_dce_v(codec, 1 , mb_v[i]);
- pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
- __func__, retry, mic_mv[i], mb_v[i]);
- }
-
- if (taiko->mbhc_cfg.gpio && taiko_hs_gpio_level_remove(taiko)) {
- pr_debug("%s: GPIO indicates removal\n", __func__);
- break;
- }
-
- if (taiko->current_plug == PLUG_TYPE_NONE) {
- pr_debug("%s : headset/headphone is removed\n",
- __func__);
- break;
- }
-
- for (i = 0; i < MBHC_NUM_DCE_PLUG_DETECT; i++)
- if (!is_valid_mic_voltage(codec, mic_mv[i]))
- break;
-
- if (i == MBHC_NUM_DCE_PLUG_DETECT) {
- pr_debug("%s: MIC voltage settled\n", __func__);
- settled = true;
- msleep(200);
- break;
- }
-
- /* only for non-GPIO remove irq */
- if (!taiko->mbhc_cfg.gpio) {
- for (i = 0; i < MBHC_NUM_DCE_PLUG_DETECT; i++)
- if (mic_mv[i] < v_hs_max)
- break;
- if (i == MBHC_NUM_DCE_PLUG_DETECT) {
- pr_debug("%s: Headset is removed\n", __func__);
- break;
- }
- }
- }
-
- if (timedout)
- pr_debug("%s: Microphone did not settle in %d seconds\n",
- __func__, TAIKO_HS_DETECT_PLUG_TIME_MS);
- return settled;
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static void taiko_hs_remove_irq_gpio(struct taiko_priv *priv)
-{
- struct snd_soc_codec *codec = priv->codec;
-
- if (taiko_hs_remove_settle(codec))
- taiko_codec_start_hs_polling(codec);
- pr_debug("%s: remove settle done\n", __func__);
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static void taiko_hs_remove_irq_nogpio(struct taiko_priv *priv)
-{
- short bias_value;
- bool removed = true;
- struct snd_soc_codec *codec = priv->codec;
- const struct taiko_mbhc_general_cfg *generic =
- TAIKO_MBHC_CAL_GENERAL_PTR(priv->mbhc_cfg.calibration);
- int min_us = TAIKO_FAKE_REMOVAL_MIN_PERIOD_MS * 1000;
-
- if (priv->current_plug != PLUG_TYPE_HEADSET) {
- pr_debug("%s(): Headset is not inserted, ignore removal\n",
- __func__);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL,
- 0x08, 0x08);
- return;
- }
-
- usleep_range(generic->t_shutdown_plug_rem,
- generic->t_shutdown_plug_rem);
-
- do {
- bias_value = taiko_codec_sta_dce(codec, 1, true);
- pr_debug("%s: DCE %d,%d, %d us left\n", __func__, bias_value,
- taiko_codec_sta_dce_v(codec, 1, bias_value), min_us);
- if (bias_value < taiko_get_current_v_ins(priv, false)) {
- pr_debug("%s: checking false removal\n", __func__);
- msleep(500);
- removed = !taiko_hs_remove_settle(codec);
- pr_debug("%s: headset %sactually removed\n", __func__,
- removed ? "" : "not ");
- break;
- }
- min_us -= priv->mbhc_data.t_dce;
- } while (min_us > 0);
-
- if (removed) {
- /* cancel possiblely running hs detect work */
- taiko_cancel_hs_detect_plug(priv);
- /*
- * If this removal is not false, first check the micbias
- * switch status and switch it to LDOH if it is already
- * switched to VDDIO.
- */
- taiko_codec_switch_micbias(codec, 0);
-
- taiko_codec_report_plug(codec, 0, SND_JACK_HEADSET);
- taiko_codec_cleanup_hs_polling(codec);
- taiko_codec_enable_hs_detect(codec, 1,
- MBHC_USE_MB_TRIGGER |
- MBHC_USE_HPHL_TRIGGER,
- true);
- } else {
- taiko_codec_start_hs_polling(codec);
- }
-}
-
-static irqreturn_t taiko_hs_remove_irq(int irq, void *data)
-{
- struct taiko_priv *priv = data;
- bool vddio;
- pr_debug("%s: enter, removal interrupt\n", __func__);
-
- TAIKO_ACQUIRE_LOCK(priv->codec_resource_lock);
- vddio = (priv->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
- priv->mbhc_micbias_switched);
- if (vddio)
- __taiko_codec_switch_micbias(priv->codec, 0, false, true);
-
- if (priv->mbhc_cfg.gpio)
- taiko_hs_remove_irq_gpio(priv);
- else
- taiko_hs_remove_irq_nogpio(priv);
-
- /* if driver turned off vddio switch and headset is not removed,
- * turn on the vddio switch back, if headset is removed then vddio
- * switch is off by time now and shouldn't be turn on again from here */
- if (vddio && priv->current_plug == PLUG_TYPE_HEADSET)
- __taiko_codec_switch_micbias(priv->codec, 1, true, true);
- TAIKO_RELEASE_LOCK(priv->codec_resource_lock);
-
- return IRQ_HANDLED;
-}
-
-static void taiko_mbhc_insert_work(struct work_struct *work)
-{
- struct delayed_work *dwork;
- struct taiko_priv *taiko;
- struct snd_soc_codec *codec;
- struct wcd9xxx *taiko_core;
-
- dwork = to_delayed_work(work);
- taiko = container_of(dwork, struct taiko_priv, mbhc_insert_dwork);
- codec = taiko->codec;
- taiko_core = dev_get_drvdata(codec->dev->parent);
-
- pr_debug("%s:\n", __func__);
-
- /* Turn off both HPH and MIC line schmitt triggers */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x13, 0x00);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
- wcd9xxx_disable_irq_sync(codec->control_data, TAIKO_IRQ_MBHC_INSERTION);
- taiko_codec_detect_plug_type(codec);
- wcd9xxx_unlock_sleep(taiko_core);
-}
-
-static void taiko_hs_gpio_handler(struct snd_soc_codec *codec)
-{
- bool insert;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- bool is_removed = false;
-
- pr_debug("%s: enter\n", __func__);
-
- taiko->in_gpio_handler = true;
- /* Wait here for debounce time */
- usleep_range(TAIKO_GPIO_IRQ_DEBOUNCE_TIME_US,
- TAIKO_GPIO_IRQ_DEBOUNCE_TIME_US);
-
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-
- /* cancel pending button press */
- if (taiko_cancel_btn_work(taiko))
- pr_debug("%s: button press is canceled\n", __func__);
-
- insert = (gpio_get_value_cansleep(taiko->mbhc_cfg.gpio) ==
- taiko->mbhc_cfg.gpio_level_insert);
- if ((taiko->current_plug == PLUG_TYPE_NONE) && insert) {
- taiko->lpi_enabled = false;
- wmb();
-
- /* cancel detect plug */
- taiko_cancel_hs_detect_plug(taiko);
-
- /* Disable Mic Bias pull down and HPH Switch to GND */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x01,
- 0x00);
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x01, 0x00);
- taiko_codec_detect_plug_type(codec);
- } else if ((taiko->current_plug != PLUG_TYPE_NONE) && !insert) {
- taiko->lpi_enabled = false;
- wmb();
-
- /* cancel detect plug */
- taiko_cancel_hs_detect_plug(taiko);
-
- if (taiko->current_plug == PLUG_TYPE_HEADPHONE) {
- taiko_codec_report_plug(codec, 0, SND_JACK_HEADPHONE);
- is_removed = true;
- } else if (taiko->current_plug == PLUG_TYPE_GND_MIC_SWAP) {
- taiko_codec_report_plug(codec, 0, SND_JACK_UNSUPPORTED);
- is_removed = true;
- } else if (taiko->current_plug == PLUG_TYPE_HEADSET) {
- taiko_codec_pause_hs_polling(codec);
- taiko_codec_cleanup_hs_polling(codec);
- taiko_codec_report_plug(codec, 0, SND_JACK_HEADSET);
- is_removed = true;
- }
-
- if (is_removed) {
- /* Enable Mic Bias pull down and HPH Switch to GND */
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.ctl_reg, 0x01,
- 0x01);
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x01,
- 0x01);
- /* Make sure mic trigger is turned off */
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.ctl_reg,
- 0x01, 0x01);
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.mbhc_reg,
- 0x90, 0x00);
- /* Reset MBHC State Machine */
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL,
- 0x08, 0x08);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL,
- 0x08, 0x00);
- /* Turn off override */
- taiko_turn_onoff_override(codec, false);
- }
- }
-
- taiko->in_gpio_handler = false;
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- pr_debug("%s: leave\n", __func__);
-}
-
-static irqreturn_t taiko_mechanical_plug_detect_irq(int irq, void *data)
-{
- int r = IRQ_HANDLED;
- struct snd_soc_codec *codec = data;
-
- if (unlikely(wcd9xxx_lock_sleep(codec->control_data) == false)) {
- pr_warn("%s: failed to hold suspend\n", __func__);
- r = IRQ_NONE;
- } else {
- taiko_hs_gpio_handler(codec);
- wcd9xxx_unlock_sleep(codec->control_data);
- }
-
- return r;
-}
-
-static int taiko_mbhc_init_and_calibrate(struct taiko_priv *taiko)
-{
- int ret = 0;
- struct snd_soc_codec *codec = taiko->codec;
-
- taiko->mbhc_cfg.mclk_cb_fn(codec, 1, false);
- taiko_mbhc_init(codec);
- taiko_mbhc_cal(codec);
- taiko_mbhc_calc_thres(codec);
- taiko->mbhc_cfg.mclk_cb_fn(codec, 0, false);
- taiko_codec_calibrate_hs_polling(codec);
- if (!taiko->mbhc_cfg.gpio) {
- ret = taiko_codec_enable_hs_detect(codec, 1,
- MBHC_USE_MB_TRIGGER |
- MBHC_USE_HPHL_TRIGGER,
- false);
-
- if (IS_ERR_VALUE(ret))
- pr_err("%s: Failed to setup MBHC detection\n",
- __func__);
- } else {
- /* Enable Mic Bias pull down and HPH Switch to GND */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg,
- 0x01, 0x01);
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x01, 0x01);
- INIT_WORK(&taiko->hs_correct_plug_work,
- taiko_hs_correct_gpio_plug);
- }
-
- if (!IS_ERR_VALUE(ret)) {
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10, 0x10);
- wcd9xxx_enable_irq(codec->control_data,
- TAIKO_IRQ_HPH_PA_OCPL_FAULT);
- wcd9xxx_enable_irq(codec->control_data,
- TAIKO_IRQ_HPH_PA_OCPR_FAULT);
-
- if (taiko->mbhc_cfg.gpio) {
- ret = request_threaded_irq(taiko->mbhc_cfg.gpio_irq,
- NULL,
- taiko_mechanical_plug_detect_irq,
- (IRQF_TRIGGER_RISING |
- IRQF_TRIGGER_FALLING),
- "taiko-gpio", codec);
- if (!IS_ERR_VALUE(ret)) {
- ret = enable_irq_wake(taiko->mbhc_cfg.gpio_irq);
- /* Bootup time detection */
- taiko_hs_gpio_handler(codec);
- }
- }
- }
-
- return ret;
-}
-
-static void mbhc_fw_read(struct work_struct *work)
-{
- struct delayed_work *dwork;
- struct taiko_priv *taiko;
- struct snd_soc_codec *codec;
- const struct firmware *fw;
- int ret = -1, retry = 0;
-
- dwork = to_delayed_work(work);
- taiko = container_of(dwork, struct taiko_priv, mbhc_firmware_dwork);
- codec = taiko->codec;
-
- while (retry < MBHC_FW_READ_ATTEMPTS) {
- retry++;
- pr_info("%s:Attempt %d to request MBHC firmware\n",
- __func__, retry);
- ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
- codec->dev);
-
- if (ret != 0) {
- usleep_range(MBHC_FW_READ_TIMEOUT,
- MBHC_FW_READ_TIMEOUT);
- } else {
- pr_info("%s: MBHC Firmware read succesful\n", __func__);
- break;
- }
- }
-
- if (ret != 0) {
- pr_err("%s: Cannot load MBHC firmware use default cal\n",
- __func__);
- } else if (taiko_mbhc_fw_validate(fw) == false) {
- pr_err("%s: Invalid MBHC cal data size use default cal\n",
- __func__);
- release_firmware(fw);
- } else {
- taiko->mbhc_cfg.calibration = (void *)fw->data;
- taiko->mbhc_fw = fw;
- }
-
- (void) taiko_mbhc_init_and_calibrate(taiko);
-}
-
-int taiko_hs_detect(struct snd_soc_codec *codec,
- const struct taiko_mbhc_config *cfg)
-{
- struct taiko_priv *taiko;
- int rc = 0;
-
- if (!codec) {
- pr_err("%s: no codec\n", __func__);
- return -EINVAL;
- }
-
- if (!cfg->calibration) {
- pr_warn("%s: mbhc is not configured\n", __func__);
- return 0;
- }
-
- if (cfg->mclk_rate != TAIKO_MCLK_RATE_12288KHZ) {
- if (cfg->mclk_rate == TAIKO_MCLK_RATE_9600KHZ)
- pr_err("Error: clock rate %dHz is not yet supported\n",
- cfg->mclk_rate);
- else
- pr_err("Error: unsupported clock rate %d\n",
- cfg->mclk_rate);
- return -EINVAL;
- }
-
- taiko = snd_soc_codec_get_drvdata(codec);
- taiko->mbhc_cfg = *cfg;
- taiko->in_gpio_handler = false;
- taiko->current_plug = PLUG_TYPE_NONE;
- taiko->lpi_enabled = false;
- taiko_get_mbhc_micbias_regs(codec, &taiko->mbhc_bias_regs);
-
- /* Put CFILT in fast mode by default */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl,
- 0x40, TAIKO_CFILT_FAST_MODE);
- INIT_DELAYED_WORK(&taiko->mbhc_firmware_dwork, mbhc_fw_read);
- INIT_DELAYED_WORK(&taiko->mbhc_btn_dwork, btn_lpress_fn);
- INIT_WORK(&taiko->hphlocp_work, hphlocp_off_report);
- INIT_WORK(&taiko->hphrocp_work, hphrocp_off_report);
- INIT_DELAYED_WORK(&taiko->mbhc_insert_dwork, taiko_mbhc_insert_work);
-
- if (!taiko->mbhc_cfg.read_fw_bin)
- rc = taiko_mbhc_init_and_calibrate(taiko);
- else
- schedule_delayed_work(&taiko->mbhc_firmware_dwork,
- usecs_to_jiffies(MBHC_FW_READ_TIMEOUT));
-
- return rc;
-}
-EXPORT_SYMBOL_GPL(taiko_hs_detect);
-
static unsigned long slimbus_value;
static irqreturn_t taiko_slimbus_irq(int irq, void *data)
@@ -6868,8 +4180,8 @@
}
wcd9xxx_interface_reg_write(codec->control_data,
TAIKO_SLIM_PGD_PORT_INT_CLR0 + i, 0xFF);
- }
+ }
return IRQ_HANDLED;
}
@@ -6900,7 +4212,6 @@
/** end of Ear PA load 32 */
};
-
static const struct taiko_reg_mask_val taiko_1_0_class_h_hph[] = {
/* CLASS-H HPH IDLE_THRESHOLD Table */
@@ -6955,7 +4266,7 @@
static int taiko_handle_pdata(struct taiko_priv *taiko)
{
struct snd_soc_codec *codec = taiko->codec;
- struct wcd9xxx_pdata *pdata = taiko->pdata;
+ struct wcd9xxx_pdata *pdata = taiko->resmgr.pdata;
int k1, k2, k3, rc = 0;
u8 leg_mode, txfe_bypass, txfe_buff, flag;
u8 i = 0, j = 0;
@@ -6973,46 +4284,38 @@
flag = pdata->amic_settings.use_pdata;
/* Make sure settings are correct */
- if ((pdata->micbias.ldoh_v > TAIKO_LDOH_2P85_V) ||
- (pdata->micbias.bias1_cfilt_sel > TAIKO_CFILT3_SEL) ||
- (pdata->micbias.bias2_cfilt_sel > TAIKO_CFILT3_SEL) ||
- (pdata->micbias.bias3_cfilt_sel > TAIKO_CFILT3_SEL) ||
- (pdata->micbias.bias4_cfilt_sel > TAIKO_CFILT3_SEL)) {
+ if ((pdata->micbias.ldoh_v > WCD9XXX_LDOH_3P0_V) ||
+ (pdata->micbias.bias1_cfilt_sel > WCD9XXX_CFILT3_SEL) ||
+ (pdata->micbias.bias2_cfilt_sel > WCD9XXX_CFILT3_SEL) ||
+ (pdata->micbias.bias3_cfilt_sel > WCD9XXX_CFILT3_SEL) ||
+ (pdata->micbias.bias4_cfilt_sel > WCD9XXX_CFILT3_SEL)) {
rc = -EINVAL;
goto done;
}
-
/* figure out k value */
- k1 = taiko_find_k_value(pdata->micbias.ldoh_v,
- pdata->micbias.cfilt1_mv);
- k2 = taiko_find_k_value(pdata->micbias.ldoh_v,
- pdata->micbias.cfilt2_mv);
- k3 = taiko_find_k_value(pdata->micbias.ldoh_v,
- pdata->micbias.cfilt3_mv);
+ k1 = wcd9xxx_resmgr_get_k_val(&taiko->resmgr, pdata->micbias.cfilt1_mv);
+ k2 = wcd9xxx_resmgr_get_k_val(&taiko->resmgr, pdata->micbias.cfilt2_mv);
+ k3 = wcd9xxx_resmgr_get_k_val(&taiko->resmgr, pdata->micbias.cfilt3_mv);
if (IS_ERR_VALUE(k1) || IS_ERR_VALUE(k2) || IS_ERR_VALUE(k3)) {
rc = -EINVAL;
goto done;
}
-
/* Set voltage level and always use LDO */
snd_soc_update_bits(codec, TAIKO_A_LDO_H_MODE_1, 0x0C,
- (pdata->micbias.ldoh_v << 2));
+ (pdata->micbias.ldoh_v << 2));
- snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_1_VAL, 0xFC,
- (k1 << 2));
- snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_2_VAL, 0xFC,
- (k2 << 2));
- snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_3_VAL, 0xFC,
- (k3 << 2));
+ snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_1_VAL, 0xFC, (k1 << 2));
+ snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_2_VAL, 0xFC, (k2 << 2));
+ snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_3_VAL, 0xFC, (k3 << 2));
snd_soc_update_bits(codec, TAIKO_A_MICB_1_CTL, 0x60,
- (pdata->micbias.bias1_cfilt_sel << 5));
+ (pdata->micbias.bias1_cfilt_sel << 5));
snd_soc_update_bits(codec, TAIKO_A_MICB_2_CTL, 0x60,
- (pdata->micbias.bias2_cfilt_sel << 5));
+ (pdata->micbias.bias2_cfilt_sel << 5));
snd_soc_update_bits(codec, TAIKO_A_MICB_3_CTL, 0x60,
- (pdata->micbias.bias3_cfilt_sel << 5));
- snd_soc_update_bits(codec, taiko->reg_addr.micb_4_ctl, 0x60,
+ (pdata->micbias.bias3_cfilt_sel << 5));
+ snd_soc_update_bits(codec, taiko->resmgr.reg_addr->micb_4_ctl, 0x60,
(pdata->micbias.bias4_cfilt_sel << 5));
for (i = 0; i < 6; j++, i += 2) {
@@ -7255,227 +4558,52 @@
taiko_codec_reg_init_val[i].val);
}
-static void taiko_update_reg_address(struct taiko_priv *priv)
-{
- struct taiko_reg_address *reg_addr = &priv->reg_addr;
- reg_addr->micb_4_mbhc = TAIKO_A_MICB_4_MBHC;
- reg_addr->micb_4_int_rbias = TAIKO_A_MICB_4_INT_RBIAS;
- reg_addr->micb_4_ctl = TAIKO_A_MICB_4_CTL;
-
-}
-
-#ifdef CONFIG_DEBUG_FS
-static int codec_debug_open(struct inode *inode, struct file *file)
-{
- file->private_data = inode->i_private;
- return 0;
-}
-
-static ssize_t codec_debug_write(struct file *filp,
- const char __user *ubuf, size_t cnt, loff_t *ppos)
-{
- char lbuf[32];
- char *buf;
- int rc;
- struct taiko_priv *taiko = filp->private_data;
-
- if (cnt > sizeof(lbuf) - 1)
- return -EINVAL;
-
- rc = copy_from_user(lbuf, ubuf, cnt);
- if (rc)
- return -EFAULT;
-
- lbuf[cnt] = '\0';
- buf = (char *)lbuf;
- taiko->no_mic_headset_override = (*strsep(&buf, " ") == '0') ?
- false : true;
- return rc;
-}
-
-static ssize_t codec_mbhc_debug_read(struct file *file, char __user *buf,
- size_t count, loff_t *pos)
-{
- const int size = 768;
- char buffer[size];
- int n = 0;
- struct taiko_priv *taiko = file->private_data;
- struct snd_soc_codec *codec = taiko->codec;
- const struct mbhc_internal_cal_data *p = &taiko->mbhc_data;
- const s16 v_ins_hu_cur = taiko_get_current_v_ins(taiko, true);
- const s16 v_ins_h_cur = taiko_get_current_v_ins(taiko, false);
-
- n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n", p->dce_z,
- taiko_codec_sta_dce_v(codec, 1, p->dce_z));
- n += scnprintf(buffer + n, size - n, "dce_mb = %x(%dmv)\n",
- p->dce_mb, taiko_codec_sta_dce_v(codec, 1, p->dce_mb));
- n += scnprintf(buffer + n, size - n, "sta_z = %x(%dmv)\n",
- p->sta_z, taiko_codec_sta_dce_v(codec, 0, p->sta_z));
- n += scnprintf(buffer + n, size - n, "sta_mb = %x(%dmv)\n",
- p->sta_mb, taiko_codec_sta_dce_v(codec, 0, p->sta_mb));
- n += scnprintf(buffer + n, size - n, "t_dce = %x\n", p->t_dce);
- n += scnprintf(buffer + n, size - n, "t_sta = %x\n", p->t_sta);
- n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n",
- p->micb_mv);
- n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)%s\n",
- p->v_ins_hu,
- taiko_codec_sta_dce_v(codec, 0, p->v_ins_hu),
- p->v_ins_hu == v_ins_hu_cur ? "*" : "");
- n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)%s\n",
- p->v_ins_h, taiko_codec_sta_dce_v(codec, 1, p->v_ins_h),
- p->v_ins_h == v_ins_h_cur ? "*" : "");
- n += scnprintf(buffer + n, size - n, "adj_v_ins_hu = %x(%dmv)%s\n",
- p->adj_v_ins_hu,
- taiko_codec_sta_dce_v(codec, 0, p->adj_v_ins_hu),
- p->adj_v_ins_hu == v_ins_hu_cur ? "*" : "");
- n += scnprintf(buffer + n, size - n, "adj_v_ins_h = %x(%dmv)%s\n",
- p->adj_v_ins_h,
- taiko_codec_sta_dce_v(codec, 1, p->adj_v_ins_h),
- p->adj_v_ins_h == v_ins_h_cur ? "*" : "");
- n += scnprintf(buffer + n, size - n, "v_b1_hu = %x(%dmv)\n",
- p->v_b1_hu, taiko_codec_sta_dce_v(codec, 0, p->v_b1_hu));
- n += scnprintf(buffer + n, size - n, "v_b1_h = %x(%dmv)\n",
- p->v_b1_h, taiko_codec_sta_dce_v(codec, 1, p->v_b1_h));
- n += scnprintf(buffer + n, size - n, "v_b1_huc = %x(%dmv)\n",
- p->v_b1_huc,
- taiko_codec_sta_dce_v(codec, 1, p->v_b1_huc));
- n += scnprintf(buffer + n, size - n, "v_brh = %x(%dmv)\n",
- p->v_brh, taiko_codec_sta_dce_v(codec, 1, p->v_brh));
- n += scnprintf(buffer + n, size - n, "v_brl = %x(%dmv)\n", p->v_brl,
- taiko_codec_sta_dce_v(codec, 0, p->v_brl));
- n += scnprintf(buffer + n, size - n, "v_no_mic = %x(%dmv)\n",
- p->v_no_mic,
- taiko_codec_sta_dce_v(codec, 0, p->v_no_mic));
- n += scnprintf(buffer + n, size - n, "npoll = %d\n", p->npoll);
- n += scnprintf(buffer + n, size - n, "nbounce_wait = %d\n",
- p->nbounce_wait);
- n += scnprintf(buffer + n, size - n, "v_inval_ins_low = %d\n",
- p->v_inval_ins_low);
- n += scnprintf(buffer + n, size - n, "v_inval_ins_high = %d\n",
- p->v_inval_ins_high);
- if (taiko->mbhc_cfg.gpio)
- n += scnprintf(buffer + n, size - n, "GPIO insert = %d\n",
- taiko_hs_gpio_level_remove(taiko));
- buffer[n] = 0;
-
- return simple_read_from_buffer(buf, count, pos, buffer, n);
-}
-
-static const struct file_operations codec_debug_ops = {
- .open = codec_debug_open,
- .write = codec_debug_write,
-};
-
-static const struct file_operations codec_mbhc_debug_ops = {
- .open = codec_debug_open,
- .read = codec_mbhc_debug_read,
-};
-#endif
-
static int taiko_setup_irqs(struct taiko_priv *taiko)
{
- int ret;
int i;
+ int ret = 0;
struct snd_soc_codec *codec = taiko->codec;
- ret = wcd9xxx_request_irq(codec->control_data, TAIKO_IRQ_MBHC_INSERTION,
- taiko_hs_insert_irq, "Headset insert detect",
- taiko);
- if (ret) {
- pr_err("%s: Failed to request irq %d\n", __func__,
- TAIKO_IRQ_MBHC_INSERTION);
- goto err_insert_irq;
- }
- wcd9xxx_disable_irq(codec->control_data, TAIKO_IRQ_MBHC_INSERTION);
-
- ret = wcd9xxx_request_irq(codec->control_data, TAIKO_IRQ_MBHC_REMOVAL,
- taiko_hs_remove_irq, "Headset remove detect",
- taiko);
- if (ret) {
- pr_err("%s: Failed to request irq %d\n", __func__,
- TAIKO_IRQ_MBHC_REMOVAL);
- goto err_remove_irq;
- }
-
- ret = wcd9xxx_request_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL,
- taiko_dce_handler, "DC Estimation detect",
- taiko);
- if (ret) {
- pr_err("%s: Failed to request irq %d\n", __func__,
- TAIKO_IRQ_MBHC_POTENTIAL);
- goto err_potential_irq;
- }
-
- ret = wcd9xxx_request_irq(codec->control_data, TAIKO_IRQ_MBHC_RELEASE,
- taiko_release_handler, "Button Release detect",
- taiko);
- if (ret) {
- pr_err("%s: Failed to request irq %d\n", __func__,
- TAIKO_IRQ_MBHC_RELEASE);
- goto err_release_irq;
- }
-
- ret = wcd9xxx_request_irq(codec->control_data, TAIKO_IRQ_SLIMBUS,
+ ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS,
taiko_slimbus_irq, "SLIMBUS Slave", taiko);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
- TAIKO_IRQ_SLIMBUS);
- goto err_slimbus_irq;
+ WCD9XXX_IRQ_SLIMBUS);
+ goto exit;
}
for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++)
wcd9xxx_interface_reg_write(codec->control_data,
- TAIKO_SLIM_PGD_PORT_INT_EN0 + i,
- 0xFF);
-
- ret = wcd9xxx_request_irq(codec->control_data,
- TAIKO_IRQ_HPH_PA_OCPL_FAULT,
- taiko_hphl_ocp_irq,
- "HPH_L OCP detect", taiko);
- if (ret) {
- pr_err("%s: Failed to request irq %d\n", __func__,
- TAIKO_IRQ_HPH_PA_OCPL_FAULT);
- goto err_hphl_ocp_irq;
- }
- wcd9xxx_disable_irq(codec->control_data, TAIKO_IRQ_HPH_PA_OCPL_FAULT);
-
- ret = wcd9xxx_request_irq(codec->control_data,
- TAIKO_IRQ_HPH_PA_OCPR_FAULT,
- taiko_hphr_ocp_irq,
- "HPH_R OCP detect", taiko);
- if (ret) {
- pr_err("%s: Failed to request irq %d\n", __func__,
- TAIKO_IRQ_HPH_PA_OCPR_FAULT);
- goto err_hphr_ocp_irq;
- }
- wcd9xxx_disable_irq(codec->control_data, TAIKO_IRQ_HPH_PA_OCPR_FAULT);
-
-err_hphr_ocp_irq:
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_HPH_PA_OCPL_FAULT,
- taiko);
-err_hphl_ocp_irq:
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_SLIMBUS, taiko);
-err_slimbus_irq:
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_RELEASE, taiko);
-err_release_irq:
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL, taiko);
-err_potential_irq:
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_REMOVAL, taiko);
-err_remove_irq:
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_INSERTION, taiko);
-err_insert_irq:
-
+ TAIKO_SLIM_PGD_PORT_INT_EN0 + i,
+ 0xFF);
+exit:
return ret;
}
+int taiko_hs_detect(struct snd_soc_codec *codec,
+ struct wcd9xxx_mbhc_config *mbhc_cfg)
+{
+ struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
+ return wcd9xxx_mbhc_start(&taiko->mbhc, mbhc_cfg);
+}
+EXPORT_SYMBOL_GPL(taiko_hs_detect);
+
+static struct wcd9xxx_reg_address taiko_reg_address = {
+ .micb_4_mbhc = TAIKO_A_MICB_4_MBHC,
+ .micb_4_int_rbias = TAIKO_A_MICB_4_INT_RBIAS,
+ .micb_4_ctl = TAIKO_A_MICB_4_CTL,
+};
+
static int taiko_codec_probe(struct snd_soc_codec *codec)
{
struct wcd9xxx *control;
struct taiko_priv *taiko;
+ struct wcd9xxx_pdata *pdata;
+ struct wcd9xxx *wcd9xxx;
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret = 0;
int i;
- int ch_cnt;
+ void *ptr = NULL;
codec->control_data = dev_get_drvdata(codec->dev->parent);
control = codec->control_data;
@@ -7494,41 +4622,34 @@
tx_hpf_corner_freq_callback);
}
- /* Make sure mbhc micbias register addresses are zeroed out */
- memset(&taiko->mbhc_bias_regs, 0,
- sizeof(struct mbhc_micbias_regs));
- taiko->mbhc_micbias_switched = false;
-
- /* Make sure mbhc intenal calibration data is zeroed out */
- memset(&taiko->mbhc_data, 0,
- sizeof(struct mbhc_internal_cal_data));
- taiko->mbhc_data.t_sta_dce = DEFAULT_DCE_STA_WAIT;
- taiko->mbhc_data.t_dce = DEFAULT_DCE_WAIT;
- taiko->mbhc_data.t_sta = DEFAULT_STA_WAIT;
snd_soc_codec_set_drvdata(codec, taiko);
- taiko->mclk_enabled = false;
- taiko->bandgap_type = TAIKO_BANDGAP_OFF;
- taiko->clock_active = false;
- taiko->config_mode_active = false;
- taiko->mbhc_polling_active = false;
- taiko->mbhc_fake_ins_start = 0;
- taiko->no_mic_headset_override = false;
- taiko->hs_polling_irq_prepared = false;
- mutex_init(&taiko->codec_resource_lock);
+ /* codec resmgr module init */
+ wcd9xxx = codec->control_data;
+ pdata = dev_get_platdata(codec->dev->parent);
+ ret = wcd9xxx_resmgr_init(&taiko->resmgr, codec, wcd9xxx, pdata,
+ &taiko_reg_address);
+ if (ret) {
+ pr_err("%s: wcd9xxx init failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ /* init and start mbhc */
+ ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec);
+ if (ret) {
+ pr_err("%s: mbhc init failed %d\n", __func__, ret);
+ return ret;
+ }
+
taiko->codec = codec;
- taiko->mbhc_state = MBHC_STATE_NONE;
- taiko->mbhc_last_resume = 0;
for (i = 0; i < COMPANDER_MAX; i++) {
taiko->comp_enabled[i] = 0;
taiko->comp_fs[i] = COMPANDER_FS_48KHZ;
}
- taiko->pdata = dev_get_platdata(codec->dev->parent);
taiko->intf_type = wcd9xxx_get_intf_type();
taiko->aux_pga_cnt = 0;
taiko->aux_l_gain = 0x1F;
taiko->aux_r_gain = 0x1F;
- taiko_update_reg_address(taiko);
taiko_update_reg_defaults(codec);
taiko_codec_init_reg(codec);
ret = taiko_handle_pdata(taiko);
@@ -7537,84 +4658,57 @@
goto err_pdata;
}
+ ptr = kmalloc((sizeof(taiko_rx_chs) +
+ sizeof(taiko_tx_chs)), GFP_KERNEL);
+ if (!ptr) {
+ pr_err("%s: no mem for slim chan ctl data\n", __func__);
+ ret = -ENOMEM;
+ goto err_nomem_slimch;
+ }
+
if (taiko->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
snd_soc_dapm_new_controls(dapm, taiko_dapm_i2s_widgets,
ARRAY_SIZE(taiko_dapm_i2s_widgets));
snd_soc_dapm_add_routes(dapm, audio_i2s_map,
ARRAY_SIZE(audio_i2s_map));
+ for (i = 0; i < ARRAY_SIZE(taiko_i2s_dai); i++)
+ INIT_LIST_HEAD(&taiko->dai[i].wcd9xxx_ch_list);
+ } else if (taiko->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ for (i = 0; i < NUM_CODEC_DAIS; i++) {
+ INIT_LIST_HEAD(&taiko->dai[i].wcd9xxx_ch_list);
+ init_waitqueue_head(&taiko->dai[i].dai_wait);
+ }
}
+ control->num_rx_port = TAIKO_RX_MAX;
+ control->rx_chs = ptr;
+ memcpy(control->rx_chs, taiko_rx_chs, sizeof(taiko_rx_chs));
+ control->num_tx_port = TAIKO_TX_MAX;
+ control->tx_chs = ptr + sizeof(taiko_rx_chs);
+ memcpy(control->tx_chs, taiko_tx_chs, sizeof(taiko_tx_chs));
+
snd_soc_dapm_sync(dapm);
(void) taiko_setup_irqs(taiko);
- for (i = 0; i < ARRAY_SIZE(taiko_dai); i++) {
- switch (taiko_dai[i].id) {
- case AIF1_PB:
- ch_cnt = taiko_dai[i].playback.channels_max;
- break;
- case AIF1_CAP:
- ch_cnt = taiko_dai[i].capture.channels_max;
- break;
- case AIF2_PB:
- ch_cnt = taiko_dai[i].playback.channels_max;
- break;
- case AIF2_CAP:
- ch_cnt = taiko_dai[i].capture.channels_max;
- break;
- case AIF3_PB:
- ch_cnt = taiko_dai[i].playback.channels_max;
- break;
- case AIF3_CAP:
- ch_cnt = taiko_dai[i].capture.channels_max;
- break;
- default:
- continue;
- }
- taiko->dai[i].ch_num = kzalloc((sizeof(unsigned int)*
- ch_cnt), GFP_KERNEL);
- }
-
-#ifdef CONFIG_DEBUG_FS
- if (ret == 0) {
- taiko->debugfs_poke =
- debugfs_create_file("TRRS", S_IFREG | S_IRUGO, NULL, taiko,
- &codec_debug_ops);
- taiko->debugfs_mbhc =
- debugfs_create_file("taiko_mbhc", S_IFREG | S_IRUGO,
- NULL, taiko, &codec_mbhc_debug_ops);
- }
-#endif
codec->ignore_pmdown_time = 1;
return ret;
err_pdata:
- mutex_destroy(&taiko->codec_resource_lock);
+ kfree(ptr);
+err_nomem_slimch:
kfree(taiko);
return ret;
}
static int taiko_codec_remove(struct snd_soc_codec *codec)
{
- int i;
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_SLIMBUS, taiko);
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_RELEASE, taiko);
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL, taiko);
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_REMOVAL, taiko);
- wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_INSERTION, taiko);
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- taiko_codec_disable_clock_block(codec);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- taiko_codec_enable_bandgap(codec, TAIKO_BANDGAP_OFF);
- if (taiko->mbhc_fw)
- release_firmware(taiko->mbhc_fw);
- for (i = 0; i < ARRAY_SIZE(taiko_dai); i++)
- kfree(taiko->dai[i].ch_num);
- mutex_destroy(&taiko->codec_resource_lock);
-#ifdef CONFIG_DEBUG_FS
- debugfs_remove(taiko->debugfs_poke);
- debugfs_remove(taiko->debugfs_mbhc);
-#endif
+
+ /* cleanup MBHC */
+ wcd9xxx_mbhc_deinit(&taiko->mbhc);
+ /* cleanup resmgr */
+ wcd9xxx_resmgr_deinit(&taiko->resmgr);
+
kfree(taiko);
return 0;
}
@@ -7652,7 +4746,8 @@
struct platform_device *pdev = to_platform_device(dev);
struct taiko_priv *taiko = platform_get_drvdata(pdev);
dev_dbg(dev, "%s: system resume\n", __func__);
- taiko->mbhc_last_resume = jiffies;
+ /* Notify */
+ wcd9xxx_resmgr_notifier_call(&taiko->resmgr, WCD9XXX_EVENT_POST_RESUME);
return 0;
}
diff --git a/sound/soc/codecs/wcd9320.h b/sound/soc/codecs/wcd9320.h
index 7ca8ff0..7bc5a57 100644
--- a/sound/soc/codecs/wcd9320.h
+++ b/sound/soc/codecs/wcd9320.h
@@ -15,6 +15,8 @@
#include <sound/soc.h>
#include <sound/jack.h>
#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
+#include "wcd9xxx-mbhc.h"
+#include "wcd9xxx-resmgr.h"
#define TAIKO_NUM_REGISTERS 0x400
#define TAIKO_MAX_REGISTER (TAIKO_NUM_REGISTERS-1)
@@ -22,27 +24,13 @@
#define TAIKO_REG_VAL(reg, val) {reg, 0, val}
-#define DEFAULT_DCE_STA_WAIT 55
-#define DEFAULT_DCE_WAIT 60000
-#define DEFAULT_STA_WAIT 5000
-#define VDDIO_MICBIAS_MV 1800
-
-#define STA 0
-#define DCE 1
-
-#define TAIKO_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
- SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
- SND_JACK_BTN_4 | SND_JACK_BTN_5 | \
- SND_JACK_BTN_6 | SND_JACK_BTN_7)
-
extern const u8 taiko_reg_readable[TAIKO_CACHE_SIZE];
extern const u8 taiko_reset_reg_defaults[TAIKO_CACHE_SIZE];
-
-enum taiko_micbias_num {
- TAIKO_MICBIAS1 = 0,
- TAIKO_MICBIAS2,
- TAIKO_MICBIAS3,
- TAIKO_MICBIAS4,
+struct taiko_codec_dai_data {
+ u32 rate;
+ u32 *ch_num;
+ u32 ch_act;
+ u32 ch_tot;
};
enum taiko_pid_current {
@@ -58,130 +46,50 @@
u8 val;
};
-enum taiko_mbhc_clk_freq {
- TAIKO_MCLK_12P2MHZ = 0,
- TAIKO_MCLK_9P6MHZ,
- TAIKO_NUM_CLK_FREQS,
-};
-
enum taiko_mbhc_analog_pwr_cfg {
TAIKO_ANALOG_PWR_COLLAPSED = 0,
TAIKO_ANALOG_PWR_ON,
TAIKO_NUM_ANALOG_PWR_CONFIGS,
};
-enum taiko_mbhc_btn_det_mem {
- TAIKO_BTN_DET_V_BTN_LOW,
- TAIKO_BTN_DET_V_BTN_HIGH,
- TAIKO_BTN_DET_N_READY,
- TAIKO_BTN_DET_N_CIC,
- TAIKO_BTN_DET_GAIN
+/* Number of input and output Slimbus port */
+enum {
+ TAIKO_RX1 = 0,
+ TAIKO_RX2,
+ TAIKO_RX3,
+ TAIKO_RX4,
+ TAIKO_RX5,
+ TAIKO_RX6,
+ TAIKO_RX7,
+ TAIKO_RX8,
+ TAIKO_RX9,
+ TAIKO_RX10,
+ TAIKO_RX11,
+ TAIKO_RX12,
+ TAIKO_RX13,
+ TAIKO_RX_MAX,
};
-struct taiko_mbhc_general_cfg {
- u8 t_ldoh;
- u8 t_bg_fast_settle;
- u8 t_shutdown_plug_rem;
- u8 mbhc_nsa;
- u8 mbhc_navg;
- u8 v_micbias_l;
- u8 v_micbias;
- u8 mbhc_reserved;
- u16 settle_wait;
- u16 t_micbias_rampup;
- u16 t_micbias_rampdown;
- u16 t_supply_bringup;
-} __packed;
-
-struct taiko_mbhc_plug_detect_cfg {
- u32 mic_current;
- u32 hph_current;
- u16 t_mic_pid;
- u16 t_ins_complete;
- u16 t_ins_retry;
- u16 v_removal_delta;
- u8 micbias_slow_ramp;
- u8 reserved0;
- u8 reserved1;
- u8 reserved2;
-} __packed;
-
-struct taiko_mbhc_plug_type_cfg {
- u8 av_detect;
- u8 mono_detect;
- u8 num_ins_tries;
- u8 reserved0;
- s16 v_no_mic;
- s16 v_av_min;
- s16 v_av_max;
- s16 v_hs_min;
- s16 v_hs_max;
- u16 reserved1;
-} __packed;
-
-
-struct taiko_mbhc_btn_detect_cfg {
- s8 c[8];
- u8 nc;
- u8 n_meas;
- u8 mbhc_nsc;
- u8 n_btn_meas;
- u8 n_btn_con;
- u8 num_btn;
- u8 reserved0;
- u8 reserved1;
- u16 t_poll;
- u16 t_bounce_wait;
- u16 t_rel_timeout;
- s16 v_btn_press_delta_sta;
- s16 v_btn_press_delta_cic;
- u16 t_btn0_timeout;
- s16 _v_btn_low[0]; /* v_btn_low[num_btn] */
- s16 _v_btn_high[0]; /* v_btn_high[num_btn] */
- u8 _n_ready[TAIKO_NUM_CLK_FREQS];
- u8 _n_cic[TAIKO_NUM_CLK_FREQS];
- u8 _gain[TAIKO_NUM_CLK_FREQS];
-} __packed;
-
-struct taiko_mbhc_imped_detect_cfg {
- u8 _hs_imped_detect;
- u8 _n_rload;
- u8 _hph_keep_on;
- u8 _repeat_rload_calc;
- u16 _t_dac_ramp_time;
- u16 _rhph_high;
- u16 _rhph_low;
- u16 _rload[0]; /* rload[n_rload] */
- u16 _alpha[0]; /* alpha[n_rload] */
- u16 _beta[3];
-} __packed;
-
-struct taiko_mbhc_config {
- struct snd_soc_jack *headset_jack;
- struct snd_soc_jack *button_jack;
- bool read_fw_bin;
- /* void* calibration contains:
- * struct taiko_mbhc_general_cfg generic;
- * struct taiko_mbhc_plug_detect_cfg plug_det;
- * struct taiko_mbhc_plug_type_cfg plug_type;
- * struct taiko_mbhc_btn_detect_cfg btn_det;
- * struct taiko_mbhc_imped_detect_cfg imped_det;
- * Note: various size depends on btn_det->num_btn
- */
- void *calibration;
- enum taiko_micbias_num micbias;
- int (*mclk_cb_fn) (struct snd_soc_codec*, int, bool);
- unsigned int mclk_rate;
- unsigned int gpio;
- unsigned int gpio_irq;
- int gpio_level_insert;
- /* swap_gnd_mic returns true if extern GND/MIC swap switch toggled */
- bool (*swap_gnd_mic) (struct snd_soc_codec *);
+enum {
+ TAIKO_TX1 = 0,
+ TAIKO_TX2,
+ TAIKO_TX3,
+ TAIKO_TX4,
+ TAIKO_TX5,
+ TAIKO_TX6,
+ TAIKO_TX7,
+ TAIKO_TX8,
+ TAIKO_TX9,
+ TAIKO_TX10,
+ TAIKO_TX11,
+ TAIKO_TX12,
+ TAIKO_TX13,
+ TAIKO_TX14,
+ TAIKO_TX15,
+ TAIKO_TX16,
+ TAIKO_TX_MAX,
};
-extern int taiko_hs_detect(struct snd_soc_codec *codec,
- const struct taiko_mbhc_config *cfg);
-
struct anc_header {
u32 reserved[3];
u32 num_anc_slots;
@@ -189,64 +97,7 @@
extern int taiko_mclk_enable(struct snd_soc_codec *codec, int mclk_enable,
bool dapm);
-
-extern void *taiko_mbhc_cal_btn_det_mp(const struct taiko_mbhc_btn_detect_cfg
- *btn_det,
- const enum taiko_mbhc_btn_det_mem mem);
-
-#define TAIKO_MBHC_CAL_SIZE(buttons, rload) ( \
- sizeof(enum taiko_micbias_num) + \
- sizeof(struct taiko_mbhc_general_cfg) + \
- sizeof(struct taiko_mbhc_plug_detect_cfg) + \
- ((sizeof(s16) + sizeof(s16)) * buttons) + \
- sizeof(struct taiko_mbhc_plug_type_cfg) + \
- sizeof(struct taiko_mbhc_btn_detect_cfg) + \
- sizeof(struct taiko_mbhc_imped_detect_cfg) + \
- ((sizeof(u16) + sizeof(u16)) * rload) \
- )
-
-#define TAIKO_MBHC_CAL_GENERAL_PTR(cali) ( \
- (struct taiko_mbhc_general_cfg *) cali)
-#define TAIKO_MBHC_CAL_PLUG_DET_PTR(cali) ( \
- (struct taiko_mbhc_plug_detect_cfg *) \
- &(TAIKO_MBHC_CAL_GENERAL_PTR(cali)[1]))
-#define TAIKO_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \
- (struct taiko_mbhc_plug_type_cfg *) \
- &(TAIKO_MBHC_CAL_PLUG_DET_PTR(cali)[1]))
-#define TAIKO_MBHC_CAL_BTN_DET_PTR(cali) ( \
- (struct taiko_mbhc_btn_detect_cfg *) \
- &(TAIKO_MBHC_CAL_PLUG_TYPE_PTR(cali)[1]))
-#define TAIKO_MBHC_CAL_IMPED_DET_PTR(cali) ( \
- (struct taiko_mbhc_imped_detect_cfg *) \
- (((void *)&TAIKO_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \
- (TAIKO_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \
- (sizeof(TAIKO_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \
- sizeof(TAIKO_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \
- )
-
-/* minimum size of calibration data assuming there is only one button and
- * one rload.
- */
-#define TAIKO_MBHC_CAL_MIN_SIZE ( \
- sizeof(struct taiko_mbhc_general_cfg) + \
- sizeof(struct taiko_mbhc_plug_detect_cfg) + \
- sizeof(struct taiko_mbhc_plug_type_cfg) + \
- sizeof(struct taiko_mbhc_btn_detect_cfg) + \
- sizeof(struct taiko_mbhc_imped_detect_cfg) + \
- (sizeof(u16) * 2))
-
-#define TAIKO_MBHC_CAL_BTN_SZ(cfg_ptr) ( \
- sizeof(struct taiko_mbhc_btn_detect_cfg) + \
- (cfg_ptr->num_btn * (sizeof(cfg_ptr->_v_btn_low[0]) + \
- sizeof(cfg_ptr->_v_btn_high[0]))))
-
-#define TAIKO_MBHC_CAL_IMPED_MIN_SZ ( \
- sizeof(struct taiko_mbhc_imped_detect_cfg) + \
- sizeof(u16) * 2)
-
-#define TAIKO_MBHC_CAL_IMPED_SZ(cfg_ptr) ( \
- sizeof(struct taiko_mbhc_imped_detect_cfg) + \
- (cfg_ptr->_n_rload * (sizeof(cfg_ptr->_rload[0]) + \
- sizeof(cfg_ptr->_alpha[0]))))
+extern int taiko_hs_detect(struct snd_soc_codec *codec,
+ struct wcd9xxx_mbhc_config *mbhc_cfg);
#endif
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
new file mode 100644
index 0000000..3d7c0d4
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -0,0 +1,3299 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/wcd9xxx/wcd9320_registers.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include "wcd9320.h"
+#include "wcd9xxx-mbhc.h"
+#include "wcd9xxx-resmgr.h"
+
+#define WCD9XXX_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \
+ SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \
+ SND_JACK_UNSUPPORTED)
+#define WCD9XXX_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
+ SND_JACK_BTN_4 | SND_JACK_BTN_5 | \
+ SND_JACK_BTN_6 | SND_JACK_BTN_7)
+
+#define NUM_DCE_PLUG_DETECT 3
+#define NUM_ATTEMPTS_INSERT_DETECT 25
+#define NUM_ATTEMPTS_TO_REPORT 5
+
+#define FAKE_INS_LOW 10
+#define FAKE_INS_HIGH 80
+#define FAKE_INS_HIGH_NO_SWCH 150
+#define FAKE_REMOVAL_MIN_PERIOD_MS 50
+#define FAKE_INS_DELTA_SCALED_MV 300
+
+#define BUTTON_MIN 0x8000
+#define STATUS_REL_DETECTION 0x0C
+
+#define HS_DETECT_PLUG_TIME_MS (5 * 1000)
+#define HS_DETECT_PLUG_INERVAL_MS 100
+#define SWCH_REL_DEBOUNCE_TIME_MS 50
+#define SWCH_IRQ_DEBOUNCE_TIME_US 5000
+
+#define GND_MIC_SWAP_THRESHOLD 2
+#define OCP_ATTEMPT 1
+
+#define FW_READ_ATTEMPTS 15
+#define FW_READ_TIMEOUT 2000000
+
+#define BUTTON_POLLING_SUPPORTED false
+
+#define MCLK_RATE_12288KHZ 12288000
+#define MCLK_RATE_9600KHZ 9600000
+#define WCD9XXX_RCO_CLK_RATE MCLK_RATE_12288KHZ
+
+#define DEFAULT_DCE_STA_WAIT 55
+#define DEFAULT_DCE_WAIT 60000
+#define DEFAULT_STA_WAIT 5000
+
+#define VDDIO_MICBIAS_MV 1800
+
+enum meas_type {
+ STA = 0,
+ DCE,
+};
+
+enum {
+ MBHC_USE_HPHL_TRIGGER = 1,
+ MBHC_USE_MB_TRIGGER = 2
+};
+
+/*
+ * Flags to track of PA and DAC state.
+ * PA and DAC should be tracked separately as AUXPGA loopback requires
+ * only PA to be turned on without DAC being on.
+ */
+enum pa_dac_ack_flags {
+ WCD9XXX_HPHL_PA_OFF_ACK = 0,
+ WCD9XXX_HPHR_PA_OFF_ACK,
+ WCD9XXX_HPHL_DAC_OFF_ACK,
+ WCD9XXX_HPHR_DAC_OFF_ACK
+};
+
+static bool wcd9xxx_mbhc_polling(struct wcd9xxx_mbhc *mbhc)
+{
+ return mbhc->polling_active;
+}
+
+static void wcd9xxx_turn_onoff_override(struct snd_soc_codec *codec, bool on)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, on << 2);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_pause_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+ if (!mbhc->polling_active) {
+ pr_debug("polling not active, nothing to pause\n");
+ return;
+ }
+
+ /* Soft reset MBHC block */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_start_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ int mbhc_state = mbhc->mbhc_state;
+
+ pr_debug("%s: enter\n", __func__);
+ if (!mbhc->polling_active) {
+ pr_debug("Polling is not active, do not start polling\n");
+ return;
+ }
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x84);
+
+ if (!mbhc->no_mic_headset_override &&
+ mbhc_state == MBHC_STATE_POTENTIAL) {
+ pr_debug("%s recovering MBHC state macine\n", __func__);
+ mbhc->mbhc_state = MBHC_STATE_POTENTIAL_RECOVERY;
+ /* set to max button press threshold */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL, 0x7F);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL, 0x7F);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, 0xFF);
+ /* set to max */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL, 0x7F);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, 0xFF);
+ }
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void __wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc,
+ int vddio_switch, bool restartpolling,
+ bool checkpolling)
+{
+ int cfilt_k_val;
+ bool override;
+ struct snd_soc_codec *codec;
+
+ codec = mbhc->codec;
+
+ if (vddio_switch && !mbhc->mbhc_micbias_switched &&
+ (!checkpolling || mbhc->polling_active)) {
+ if (restartpolling)
+ wcd9xxx_pause_hs_polling(mbhc);
+ override = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) &
+ 0x04;
+ if (!override)
+ wcd9xxx_turn_onoff_override(codec, true);
+ /* Adjust threshold if Mic Bias voltage changes */
+ if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
+ cfilt_k_val = wcd9xxx_resmgr_get_k_val(mbhc->resmgr,
+ VDDIO_MICBIAS_MV);
+ usleep_range(10000, 10000);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.cfilt_val,
+ 0xFC, (cfilt_k_val << 2));
+ usleep_range(10000, 10000);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
+ mbhc->mbhc_data.adj_v_ins_hu & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
+ (mbhc->mbhc_data.adj_v_ins_hu >> 8) &
+ 0xFF);
+ pr_debug("%s: Programmed MBHC thresholds to VDDIO\n",
+ __func__);
+ }
+
+ /* Enable MIC BIAS Switch to VDDIO */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x80, 0x80);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x10, 0x00);
+ if (!override)
+ wcd9xxx_turn_onoff_override(codec, false);
+ if (restartpolling)
+ wcd9xxx_start_hs_polling(mbhc);
+
+ mbhc->mbhc_micbias_switched = true;
+ pr_debug("%s: VDDIO switch enabled\n", __func__);
+ } else if (!vddio_switch && mbhc->mbhc_micbias_switched) {
+ if ((!checkpolling || mbhc->polling_active) &&
+ restartpolling)
+ wcd9xxx_pause_hs_polling(mbhc);
+ /* Reprogram thresholds */
+ if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
+ cfilt_k_val =
+ wcd9xxx_resmgr_get_k_val(mbhc->resmgr,
+ mbhc->mbhc_data.micb_mv);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.cfilt_val,
+ 0xFC, (cfilt_k_val << 2));
+ usleep_range(10000, 10000);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
+ mbhc->mbhc_data.v_ins_hu & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
+ (mbhc->mbhc_data.v_ins_hu >> 8) & 0xFF);
+ pr_debug("%s: Programmed MBHC thresholds to MICBIAS\n",
+ __func__);
+ }
+
+ /* Disable MIC BIAS Switch to VDDIO */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x80,
+ 0x00);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x10,
+ 0x00);
+
+ if ((!checkpolling || mbhc->polling_active) && restartpolling)
+ wcd9xxx_start_hs_polling(mbhc);
+
+ mbhc->mbhc_micbias_switched = false;
+ pr_debug("%s: VDDIO switch disabled\n", __func__);
+ }
+}
+
+static void wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc, int vddio_switch)
+{
+ return __wcd9xxx_switch_micbias(mbhc, vddio_switch, true, true);
+}
+
+static s16 wcd9xxx_get_current_v_ins(struct wcd9xxx_mbhc *mbhc, bool hu)
+{
+ s16 v_ins;
+ if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
+ mbhc->mbhc_micbias_switched)
+ v_ins = hu ? (s16)mbhc->mbhc_data.adj_v_ins_hu :
+ (s16)mbhc->mbhc_data.adj_v_ins_h;
+ else
+ v_ins = hu ? (s16)mbhc->mbhc_data.v_ins_hu :
+ (s16)mbhc->mbhc_data.v_ins_h;
+ return v_ins;
+}
+
+void *wcd9xxx_mbhc_cal_btn_det_mp(
+ const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det,
+ const enum wcd9xxx_mbhc_btn_det_mem mem)
+{
+ void *ret = &btn_det->_v_btn_low;
+
+ switch (mem) {
+ case MBHC_BTN_DET_GAIN:
+ ret += sizeof(btn_det->_n_cic);
+ case MBHC_BTN_DET_N_CIC:
+ ret += sizeof(btn_det->_n_ready);
+ case MBHC_BTN_DET_N_READY:
+ ret += sizeof(btn_det->_v_btn_high[0]) * btn_det->num_btn;
+ case MBHC_BTN_DET_V_BTN_HIGH:
+ ret += sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn;
+ case MBHC_BTN_DET_V_BTN_LOW:
+ /* do nothing */
+ break;
+ default:
+ ret = NULL;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_mbhc_cal_btn_det_mp);
+
+static void wcd9xxx_calibrate_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ const s16 v_ins_hu = wcd9xxx_get_current_v_ins(mbhc, true);
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, v_ins_hu & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
+ (v_ins_hu >> 8) & 0xFF);
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
+ mbhc->mbhc_data.v_b1_hu & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
+ (mbhc->mbhc_data.v_b1_hu >> 8) & 0xFF);
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
+ mbhc->mbhc_data.v_b1_h & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
+ (mbhc->mbhc_data.v_b1_h >> 8) & 0xFF);
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
+ mbhc->mbhc_data.v_brh & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
+ (mbhc->mbhc_data.v_brh >> 8) & 0xFF);
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL,
+ mbhc->mbhc_data.v_brl & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL,
+ (mbhc->mbhc_data.v_brl >> 8) & 0xFF);
+}
+
+static void wcd9xxx_codec_switch_cfilt_mode(struct wcd9xxx_mbhc *mbhc,
+ bool fast)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ u8 reg_mode_val, cur_mode_val;
+
+ if (fast)
+ reg_mode_val = WCD9XXX_CFILT_FAST_MODE;
+ else
+ reg_mode_val = WCD9XXX_CFILT_SLOW_MODE;
+
+ cur_mode_val =
+ snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x40;
+
+ if (cur_mode_val != reg_mode_val) {
+ if (mbhc->polling_active)
+ wcd9xxx_pause_hs_polling(mbhc);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x40,
+ reg_mode_val);
+ if (mbhc->polling_active)
+ wcd9xxx_start_hs_polling(mbhc);
+ pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
+ cur_mode_val, reg_mode_val);
+ } else {
+ pr_debug("%s: CFILT Value is already %x\n",
+ __func__, cur_mode_val);
+ }
+}
+
+static void wcd9xxx_jack_report(struct snd_soc_jack *jack, int status, int mask)
+{
+ snd_soc_jack_report_no_dapm(jack, status, mask);
+}
+
+static void __hphocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status,
+ int irq)
+{
+ struct snd_soc_codec *codec;
+
+ pr_debug("%s: clear ocp status %x\n", __func__, jack_status);
+ codec = mbhc->codec;
+ if (mbhc->hph_status & jack_status) {
+ mbhc->hph_status &= ~jack_status;
+ wcd9xxx_jack_report(&mbhc->headset_jack,
+ mbhc->hph_status, WCD9XXX_JACK_MASK);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+ 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+ 0x10);
+ /*
+ * reset retry counter as PA is turned off signifying
+ * start of new OCP detection session
+ */
+ if (WCD9XXX_IRQ_HPH_PA_OCPL_FAULT)
+ mbhc->hphlocp_cnt = 0;
+ else
+ mbhc->hphrocp_cnt = 0;
+ wcd9xxx_enable_irq(codec->control_data, irq);
+ }
+}
+
+static void hphrocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
+{
+ __hphocp_off_report(mbhc, SND_JACK_OC_HPHR,
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+}
+
+static void hphlocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
+{
+ __hphocp_off_report(mbhc, SND_JACK_OC_HPHL,
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+}
+
+static void wcd9xxx_get_mbhc_micbias_regs(struct wcd9xxx_mbhc *mbhc,
+ struct mbhc_micbias_regs *micbias_regs)
+{
+ unsigned int cfilt;
+ struct wcd9xxx_pdata *pdata = mbhc->resmgr->pdata;
+
+ switch (mbhc->mbhc_cfg->micbias) {
+ case MBHC_MICBIAS1:
+ cfilt = pdata->micbias.bias1_cfilt_sel;
+ micbias_regs->mbhc_reg = WCD9XXX_A_MICB_1_MBHC;
+ micbias_regs->int_rbias = WCD9XXX_A_MICB_1_INT_RBIAS;
+ micbias_regs->ctl_reg = WCD9XXX_A_MICB_1_CTL;
+ break;
+ case MBHC_MICBIAS2:
+ cfilt = pdata->micbias.bias2_cfilt_sel;
+ micbias_regs->mbhc_reg = WCD9XXX_A_MICB_2_MBHC;
+ micbias_regs->int_rbias = WCD9XXX_A_MICB_2_INT_RBIAS;
+ micbias_regs->ctl_reg = WCD9XXX_A_MICB_2_CTL;
+ break;
+ case MBHC_MICBIAS3:
+ cfilt = pdata->micbias.bias3_cfilt_sel;
+ micbias_regs->mbhc_reg = WCD9XXX_A_MICB_3_MBHC;
+ micbias_regs->int_rbias = WCD9XXX_A_MICB_3_INT_RBIAS;
+ micbias_regs->ctl_reg = WCD9XXX_A_MICB_3_CTL;
+ break;
+ case MBHC_MICBIAS4:
+ cfilt = pdata->micbias.bias4_cfilt_sel;
+ micbias_regs->mbhc_reg = mbhc->resmgr->reg_addr->micb_4_mbhc;
+ micbias_regs->int_rbias =
+ mbhc->resmgr->reg_addr->micb_4_int_rbias;
+ micbias_regs->ctl_reg = mbhc->resmgr->reg_addr->micb_4_ctl;
+ break;
+ default:
+ /* Should never reach here */
+ pr_err("%s: Invalid MIC BIAS for MBHC\n", __func__);
+ return;
+ }
+
+ micbias_regs->cfilt_sel = cfilt;
+
+ switch (cfilt) {
+ case WCD9XXX_CFILT1_SEL:
+ micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_1_VAL;
+ micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_1_CTL;
+ mbhc->mbhc_data.micb_mv =
+ mbhc->resmgr->pdata->micbias.cfilt1_mv;
+ break;
+ case WCD9XXX_CFILT2_SEL:
+ micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_2_VAL;
+ micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_2_CTL;
+ mbhc->mbhc_data.micb_mv =
+ mbhc->resmgr->pdata->micbias.cfilt2_mv;
+ break;
+ case WCD9XXX_CFILT3_SEL:
+ micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_3_VAL;
+ micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_3_CTL;
+ mbhc->mbhc_data.micb_mv =
+ mbhc->resmgr->pdata->micbias.cfilt3_mv;
+ break;
+ }
+}
+
+static void wcd9xxx_clr_and_turnon_hph_padac(struct wcd9xxx_mbhc *mbhc)
+{
+ bool pa_turned_on = false;
+ struct snd_soc_codec *codec = mbhc->codec;
+ u8 wg_time;
+
+ wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME) ;
+ wg_time += 1;
+
+ if (test_and_clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK,
+ &mbhc->hph_pa_dac_state)) {
+ pr_debug("%s: HPHR clear flag and enable DAC\n", __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL,
+ 0xC0, 0xC0);
+ }
+ if (test_and_clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK,
+ &mbhc->hph_pa_dac_state)) {
+ pr_debug("%s: HPHL clear flag and enable DAC\n", __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL,
+ 0xC0, 0xC0);
+ }
+
+ if (test_and_clear_bit(WCD9XXX_HPHR_PA_OFF_ACK,
+ &mbhc->hph_pa_dac_state)) {
+ pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x10,
+ 1 << 4);
+ pa_turned_on = true;
+ }
+ if (test_and_clear_bit(WCD9XXX_HPHL_PA_OFF_ACK,
+ &mbhc->hph_pa_dac_state)) {
+ pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x20, 1
+ << 5);
+ pa_turned_on = true;
+ }
+
+ if (pa_turned_on) {
+ pr_debug("%s: PA was turned off by MBHC and not by DAPM\n",
+ __func__);
+ usleep_range(wg_time * 1000, wg_time * 1000);
+ }
+}
+
+static int wcd9xxx_cancel_btn_work(struct wcd9xxx_mbhc *mbhc)
+{
+ int r;
+ r = cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);
+ if (r)
+ /* if scheduled mbhc.mbhc_btn_dwork is canceled from here,
+ * we have to unlock from here instead btn_work */
+ wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+ return r;
+}
+
+static bool wcd9xxx_is_hph_dac_on(struct snd_soc_codec *codec, int left)
+{
+ u8 hph_reg_val = 0;
+ if (left)
+ hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL);
+ else
+ hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL);
+
+ return (hph_reg_val & 0xC0) ? true : false;
+}
+
+static bool wcd9xxx_is_hph_pa_on(struct snd_soc_codec *codec)
+{
+ u8 hph_reg_val = 0;
+ hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_EN);
+
+ return (hph_reg_val & 0x30) ? true : false;
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_set_and_turnoff_hph_padac(struct wcd9xxx_mbhc *mbhc)
+{
+ u8 wg_time;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME);
+ wg_time += 1;
+
+ /* If headphone PA is on, check if userspace receives
+ * removal event to sync-up PA's state */
+ if (wcd9xxx_is_hph_pa_on(codec)) {
+ pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
+ set_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+ set_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+ } else {
+ pr_debug("%s PA is off\n", __func__);
+ }
+
+ if (wcd9xxx_is_hph_dac_on(codec, 1))
+ set_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+ if (wcd9xxx_is_hph_dac_on(codec, 0))
+ set_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x30, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL, 0xC0, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xC0, 0x00);
+ usleep_range(wg_time * 1000, wg_time * 1000);
+}
+
+static void wcd9xxx_insert_detect_setup(struct wcd9xxx_mbhc *mbhc, bool ins)
+{
+ if (!mbhc->mbhc_cfg->insert_detect)
+ return;
+ pr_debug("%s: Setting up %s detection\n", __func__,
+ ins ? "insert" : "removal");
+ /* Enable interrupt and insertion detection */
+ snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
+ (0x69 | (ins ? (1 << 1) : 0)));
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_report_plug(struct wcd9xxx_mbhc *mbhc, int insertion,
+ enum snd_jack_types jack_type)
+{
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ pr_debug("%s: enter insertion %d hph_status %x\n",
+ __func__, insertion, mbhc->hph_status);
+ if (!insertion) {
+ /* Report removal */
+ mbhc->hph_status &= ~jack_type;
+ /*
+ * cancel possibly scheduled btn work and
+ * report release if we reported button press
+ */
+ if (wcd9xxx_cancel_btn_work(mbhc))
+ pr_debug("%s: button press is canceled\n", __func__);
+ else if (mbhc->buttons_pressed) {
+ pr_debug("%s: release of button press%d\n",
+ __func__, jack_type);
+ wcd9xxx_jack_report(&mbhc->button_jack, 0,
+ mbhc->buttons_pressed);
+ mbhc->buttons_pressed &=
+ ~WCD9XXX_JACK_BUTTON_MASK;
+ }
+ pr_debug("%s: Reporting removal %d(%x)\n", __func__,
+ jack_type, mbhc->hph_status);
+ wcd9xxx_jack_report(&mbhc->headset_jack, mbhc->hph_status,
+ WCD9XXX_JACK_MASK);
+ wcd9xxx_set_and_turnoff_hph_padac(mbhc);
+ hphrocp_off_report(mbhc, SND_JACK_OC_HPHR);
+ hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
+ mbhc->current_plug = PLUG_TYPE_NONE;
+ mbhc->polling_active = false;
+ } else {
+ if (mbhc->mbhc_cfg->detect_extn_cable) {
+ /* Report removal of current jack type */
+ if (mbhc->hph_status != jack_type) {
+ pr_debug("%s: Reporting removal (%x)\n",
+ __func__, mbhc->hph_status);
+ wcd9xxx_jack_report(&mbhc->headset_jack,
+ 0, WCD9XXX_JACK_MASK);
+ mbhc->hph_status = 0;
+ }
+ }
+ /* Report insertion */
+ mbhc->hph_status |= jack_type;
+
+ if (jack_type == SND_JACK_HEADPHONE) {
+ mbhc->current_plug = PLUG_TYPE_HEADPHONE;
+ } else if (jack_type == SND_JACK_UNSUPPORTED) {
+ mbhc->current_plug = PLUG_TYPE_GND_MIC_SWAP;
+ } else if (jack_type == SND_JACK_HEADSET) {
+ mbhc->polling_active = BUTTON_POLLING_SUPPORTED;
+ mbhc->current_plug = PLUG_TYPE_HEADSET;
+ } else if (jack_type == SND_JACK_LINEOUT) {
+ mbhc->current_plug = PLUG_TYPE_HIGH_HPH;
+ }
+ pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
+ jack_type, mbhc->hph_status);
+ wcd9xxx_jack_report(&mbhc->headset_jack,
+ mbhc->hph_status, WCD9XXX_JACK_MASK);
+ wcd9xxx_clr_and_turnon_hph_padac(mbhc);
+ }
+ /* Setup insert detect */
+ wcd9xxx_insert_detect_setup(mbhc, !insertion);
+
+ pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status);
+}
+
+/* should be called under interrupt context that hold suspend */
+static void wcd9xxx_schedule_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
+ struct work_struct *work)
+{
+ pr_debug("%s: scheduling wcd9xxx_correct_swch_plug\n", __func__);
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+ mbhc->hs_detect_work_stop = false;
+ wcd9xxx_lock_sleep(mbhc->resmgr->core);
+ schedule_work(work);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_cancel_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
+ struct work_struct *work)
+{
+ pr_debug("%s: Canceling correct_plug_swch\n", __func__);
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+ mbhc->hs_detect_work_stop = true;
+ wmb();
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ if (cancel_work_sync(work)) {
+ pr_debug("%s: correct_plug_swch is canceled\n",
+ __func__);
+ wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+ }
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+}
+
+static s16 wcd9xxx_get_current_v_hs_max(struct wcd9xxx_mbhc *mbhc)
+{
+ s16 v_hs_max;
+ struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
+
+ plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+ if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
+ mbhc->mbhc_micbias_switched)
+ v_hs_max = mbhc->mbhc_data.adj_v_hs_max;
+ else
+ v_hs_max = plug_type->v_hs_max;
+ return v_hs_max;
+}
+
+static bool wcd9xxx_is_inval_ins_range(struct wcd9xxx_mbhc *mbhc,
+ s32 mic_volt, bool highhph, bool *highv)
+{
+ s16 v_hs_max;
+ bool invalid = false;
+
+ /* Perform this check only when the high voltage headphone
+ * needs to be considered as invalid
+ */
+ v_hs_max = wcd9xxx_get_current_v_hs_max(mbhc);
+ *highv = mic_volt > v_hs_max;
+ if (!highhph && *highv)
+ invalid = true;
+ else if (mic_volt < mbhc->mbhc_data.v_inval_ins_high &&
+ (mic_volt > mbhc->mbhc_data.v_inval_ins_low))
+ invalid = true;
+
+ return invalid;
+}
+
+static short wcd9xxx_read_sta_result(struct snd_soc_codec *codec)
+{
+ u8 bias_msb, bias_lsb;
+ short bias_value;
+
+ bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B3_STATUS);
+ bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B2_STATUS);
+ bias_value = (bias_msb << 8) | bias_lsb;
+ return bias_value;
+}
+
+static short wcd9xxx_read_dce_result(struct snd_soc_codec *codec)
+{
+ u8 bias_msb, bias_lsb;
+ short bias_value;
+
+ bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B5_STATUS);
+ bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B4_STATUS);
+ bias_value = (bias_msb << 8) | bias_lsb;
+ return bias_value;
+}
+
+static void wcd9xxx_turn_onoff_rel_detection(struct snd_soc_codec *codec,
+ bool on)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, on << 1);
+}
+
+static short __wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
+ bool override_bypass, bool noreldetection)
+{
+ short bias_value;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ wcd9xxx_disable_irq(mbhc->resmgr->core, WCD9XXX_IRQ_MBHC_POTENTIAL);
+ if (noreldetection)
+ wcd9xxx_turn_onoff_rel_detection(codec, false);
+
+ /* Turn on the override */
+ if (!override_bypass)
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
+ if (dce) {
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+ 0x8);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+ 0x0);
+ usleep_range(mbhc->mbhc_data.t_sta_dce,
+ mbhc->mbhc_data.t_sta_dce);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
+ usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
+ bias_value = wcd9xxx_read_dce_result(codec);
+ } else {
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+ 0x8);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+ 0x0);
+ usleep_range(mbhc->mbhc_data.t_sta_dce,
+ mbhc->mbhc_data.t_sta_dce);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
+ usleep_range(mbhc->mbhc_data.t_sta,
+ mbhc->mbhc_data.t_sta);
+ bias_value = wcd9xxx_read_sta_result(codec);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+ 0x8);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x0);
+ }
+ /* Turn off the override after measuring mic voltage */
+ if (!override_bypass)
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04,
+ 0x00);
+
+ if (noreldetection)
+ wcd9xxx_turn_onoff_rel_detection(codec, true);
+ wcd9xxx_enable_irq(mbhc->resmgr->core, WCD9XXX_IRQ_MBHC_POTENTIAL);
+
+ return bias_value;
+}
+
+static short wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
+ bool norel)
+{
+ return __wcd9xxx_codec_sta_dce(mbhc, dce, false, norel);
+}
+
+static s32 wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
+ u16 bias_value)
+{
+ s16 value, z, mb;
+ s32 mv;
+
+ value = bias_value;
+ if (dce) {
+ z = (mbhc->mbhc_data.dce_z);
+ mb = (mbhc->mbhc_data.dce_mb);
+ mv = (value - z) * (s32)mbhc->mbhc_data.micb_mv / (mb - z);
+ } else {
+ z = (mbhc->mbhc_data.sta_z);
+ mb = (mbhc->mbhc_data.sta_mb);
+ mv = (value - z) * (s32)mbhc->mbhc_data.micb_mv / (mb - z);
+ }
+
+ return mv;
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static short wcd9xxx_mbhc_setup_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ short bias_value;
+ u8 cfilt_mode;
+
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ pr_debug("%s: enter\n", __func__);
+ if (!mbhc->mbhc_cfg->calibration) {
+ pr_err("%s: Error, no calibration exists\n", __func__);
+ return -ENODEV;
+ }
+
+ /*
+ * Request BG and clock.
+ * These will be released by wcd9xxx_cleanup_hs_polling
+ */
+ wcd9xxx_resmgr_get_bandgap(mbhc->resmgr, WCD9XXX_BANDGAP_MBHC_MODE);
+ wcd9xxx_resmgr_get_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x01);
+
+ /* Make sure CFILT is in fast mode, save current mode */
+ cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x70, 0x00);
+
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x1F, 0x16);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x84);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x80);
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x1F, 0x1C);
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x40, 0x40);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x00);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x2, 0x2);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+
+ wcd9xxx_calibrate_hs_polling(mbhc);
+
+ /* don't flip override */
+ bias_value = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x40,
+ cfilt_mode);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
+
+ return bias_value;
+}
+
+static void wcd9xxx_shutdown_hs_removal_detect(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ const struct wcd9xxx_mbhc_general_cfg *generic =
+ WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+
+ /* Need MBHC clock */
+ wcd9xxx_resmgr_get_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x80, 0x00);
+
+ usleep_range(generic->t_shutdown_plug_rem,
+ generic->t_shutdown_plug_rem);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
+
+ /* Put requested CLK back */
+ wcd9xxx_resmgr_put_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x00);
+}
+
+static void wcd9xxx_cleanup_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ wcd9xxx_shutdown_hs_removal_detect(mbhc);
+
+ /* Release clock and BG requested by wcd9xxx_mbhc_setup_hs_polling */
+ wcd9xxx_resmgr_put_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+ wcd9xxx_resmgr_put_bandgap(mbhc->resmgr, WCD9XXX_BANDGAP_MBHC_MODE);
+
+ mbhc->polling_active = false;
+ mbhc->mbhc_state = MBHC_STATE_NONE;
+}
+
+static s16 scale_v_micb_vddio(struct wcd9xxx_mbhc *mbhc, int v, bool tovddio)
+{
+ int r;
+ int vddio_k, mb_k;
+ vddio_k = wcd9xxx_resmgr_get_k_val(mbhc->resmgr, VDDIO_MICBIAS_MV);
+ mb_k = wcd9xxx_resmgr_get_k_val(mbhc->resmgr, mbhc->mbhc_data.micb_mv);
+ if (tovddio)
+ r = v * vddio_k / mb_k;
+ else
+ r = v * mb_k / vddio_k;
+ return r;
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_codec_hphr_gnd_switch(struct snd_soc_codec *codec, bool on)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, on);
+ if (on)
+ usleep_range(5000, 5000);
+}
+
+static bool wcd9xxx_is_inval_ins_delta(struct snd_soc_codec *codec,
+ int mic_volt, int mic_volt_prev,
+ int threshold)
+{
+ return abs(mic_volt - mic_volt_prev) > threshold;
+}
+
+/* called under codec_resource_lock acquisition and mbhc override = 1 */
+static enum wcd9xxx_mbhc_plug_type
+wcd9xxx_codec_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
+{
+ int i;
+ bool gndswitch, vddioswitch;
+ int scaled;
+ struct wcd9xxx_mbhc_plug_type_cfg *plug_type_ptr;
+ struct snd_soc_codec *codec = mbhc->codec;
+ const bool vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV);
+ int num_det = (NUM_DCE_PLUG_DETECT + vddio);
+ enum wcd9xxx_mbhc_plug_type plug_type[num_det];
+ s16 mb_v[num_det];
+ s32 mic_mv[num_det];
+ bool inval;
+ bool highdelta;
+ bool ahighv = false, highv;
+
+ pr_debug("%s: enter\n", __func__);
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ /* make sure override is on */
+ WARN_ON(!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x04));
+
+ /* GND and MIC swap detection requires at least 2 rounds of DCE */
+ BUG_ON(num_det < 2);
+
+ plug_type_ptr =
+ WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+
+ plug_type[0] = PLUG_TYPE_INVALID;
+
+ /* performs DCEs for N times
+ * 1st: check if voltage is in invalid range
+ * 2nd - N-2nd: check voltage range and delta
+ * N-1st: check voltage range, delta with HPHR GND switch
+ * Nth: check voltage range with VDDIO switch if micbias V != vddio V*/
+ for (i = 0; i < num_det; i++) {
+ gndswitch = (i == (num_det - 1 - vddio));
+ vddioswitch = (vddio && ((i == num_det - 1) ||
+ (i == num_det - 2)));
+ if (i == 0) {
+ mb_v[i] = wcd9xxx_mbhc_setup_hs_polling(mbhc);
+ mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1 , mb_v[i]);
+ inval = wcd9xxx_is_inval_ins_range(mbhc, mic_mv[i],
+ highhph, &highv);
+ ahighv |= highv;
+ scaled = mic_mv[i];
+ } else {
+ if (vddioswitch)
+ __wcd9xxx_switch_micbias(mbhc, 1,
+ false, false);
+ if (gndswitch)
+ wcd9xxx_codec_hphr_gnd_switch(codec, true);
+ mb_v[i] = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
+ mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1 , mb_v[i]);
+ if (vddioswitch)
+ scaled = scale_v_micb_vddio(mbhc, mic_mv[i],
+ false);
+ else
+ scaled = mic_mv[i];
+ /* !gndswitch & vddioswitch means the previous DCE
+ * was done with gndswitch, don't compare with DCE
+ * with gndswitch */
+ highdelta = wcd9xxx_is_inval_ins_delta(codec, scaled,
+ mic_mv[i - !gndswitch - vddioswitch],
+ FAKE_INS_DELTA_SCALED_MV);
+ inval = (wcd9xxx_is_inval_ins_range(mbhc, mic_mv[i],
+ highhph, &highv) ||
+ highdelta);
+ ahighv |= highv;
+ if (gndswitch)
+ wcd9xxx_codec_hphr_gnd_switch(codec, false);
+ if (vddioswitch)
+ __wcd9xxx_switch_micbias(mbhc, 0,
+ false, false);
+ /* claim UNSUPPORTED plug insertion when
+ * good headset is detected but HPHR GND switch makes
+ * delta difference */
+ if (i == (num_det - 2) && highdelta && !ahighv)
+ plug_type[0] = PLUG_TYPE_GND_MIC_SWAP;
+ else if (i == (num_det - 1) && inval)
+ plug_type[0] = PLUG_TYPE_INVALID;
+ }
+ pr_debug("%s: DCE #%d, %04x, V %d, scaled V %d, GND %d, VDDIO %d, inval %d\n",
+ __func__, i + 1, mb_v[i] & 0xffff, mic_mv[i], scaled,
+ gndswitch, vddioswitch, inval);
+ /* don't need to run further DCEs */
+ if (ahighv && inval)
+ break;
+ mic_mv[i] = scaled;
+ }
+
+ for (i = 0; (plug_type[0] != PLUG_TYPE_GND_MIC_SWAP && !inval) &&
+ (i < num_det); i++) {
+ /*
+ * If we are here, means none of the all
+ * measurements are fake, continue plug type detection.
+ * If all three measurements do not produce same
+ * plug type, restart insertion detection
+ */
+ if (mic_mv[i] < plug_type_ptr->v_no_mic) {
+ plug_type[i] = PLUG_TYPE_HEADPHONE;
+ pr_debug("%s: Detect attempt %d, detected Headphone\n",
+ __func__, i);
+ } else if (highhph && (mic_mv[i] > plug_type_ptr->v_hs_max)) {
+ plug_type[i] = PLUG_TYPE_HIGH_HPH;
+ pr_debug("%s: Detect attempt %d, detected High Headphone\n",
+ __func__, i);
+ } else {
+ plug_type[i] = PLUG_TYPE_HEADSET;
+ pr_debug("%s: Detect attempt %d, detected Headset\n",
+ __func__, i);
+ }
+
+ if (i > 0 && (plug_type[i - 1] != plug_type[i])) {
+ pr_err("%s: Detect attempt %d and %d are not same",
+ __func__, i - 1, i);
+ plug_type[0] = PLUG_TYPE_INVALID;
+ inval = true;
+ break;
+ }
+ }
+
+ pr_debug("%s: Detected plug type %d\n", __func__, plug_type[0]);
+ pr_debug("%s: leave\n", __func__);
+ return plug_type[0];
+}
+
+static bool wcd9xxx_swch_level_remove(struct wcd9xxx_mbhc *mbhc)
+{
+ if (mbhc->mbhc_cfg->gpio)
+ return (gpio_get_value_cansleep(mbhc->mbhc_cfg->gpio) !=
+ mbhc->mbhc_cfg->gpio_level_insert);
+ else if (mbhc->mbhc_cfg->insert_detect)
+ return snd_soc_read(mbhc->codec,
+ WCD9XXX_A_MBHC_INSERT_DET_STATUS) &
+ (1 << 2);
+ else
+ WARN(1, "Invalid jack detection configuration\n");
+
+ return true;
+}
+
+static bool is_clk_active(struct snd_soc_codec *codec)
+{
+ return !!(snd_soc_read(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL) & 0x05);
+}
+
+static int wcd9xxx_enable_hs_detect(struct wcd9xxx_mbhc *mbhc,
+ int insertion, int trigger, bool padac_off)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ int central_bias_enabled = 0;
+ const struct wcd9xxx_mbhc_general_cfg *generic =
+ WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+ const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
+ WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
+
+ pr_debug("%s: enter insertion(%d) trigger(0x%x)\n",
+ __func__, insertion, trigger);
+
+ if (!mbhc->mbhc_cfg->calibration) {
+ pr_err("Error, no wcd9xxx calibration\n");
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0);
+
+ /*
+ * Make sure mic bias and Mic line schmitt trigger
+ * are turned OFF
+ */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
+
+ if (insertion) {
+ wcd9xxx_switch_micbias(mbhc, 0);
+
+ /* DAPM can manipulate PA/DAC bits concurrently */
+ if (padac_off == true)
+ wcd9xxx_set_and_turnoff_hph_padac(mbhc);
+
+ if (trigger & MBHC_USE_HPHL_TRIGGER) {
+ /* Enable HPH Schmitt Trigger */
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x11,
+ 0x11);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x0C,
+ plug_det->hph_current << 2);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x02,
+ 0x02);
+ }
+ if (trigger & MBHC_USE_MB_TRIGGER) {
+ /* enable the mic line schmitt trigger */
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x60, plug_det->mic_current << 5);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x80, 0x80);
+ usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.ctl_reg, 0x01,
+ 0x00);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x10, 0x10);
+ }
+
+ /* setup for insetion detection */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2, 0);
+ } else {
+ pr_debug("setup for removal detection\n");
+ /* Make sure the HPH schmitt trigger is OFF */
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x00);
+
+ /* enable the mic line schmitt trigger */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x60,
+ plug_det->mic_current << 5);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x80, 0x80);
+ usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x10, 0x10);
+
+ /* Setup for low power removal detection */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2,
+ 0x2);
+ }
+
+ if (snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x4) {
+ /* called by interrupt */
+ if (!is_clk_active(codec)) {
+ wcd9xxx_resmgr_enable_config_mode(codec, 1);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+ 0x06, 0);
+ usleep_range(generic->t_shutdown_plug_rem,
+ generic->t_shutdown_plug_rem);
+ wcd9xxx_resmgr_enable_config_mode(codec, 0);
+ } else
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+ 0x06, 0);
+ }
+
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.int_rbias, 0x80, 0);
+
+ /* If central bandgap disabled */
+ if (!(snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE1) & 1)) {
+ snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x3, 0x3);
+ usleep_range(generic->t_bg_fast_settle,
+ generic->t_bg_fast_settle);
+ central_bias_enabled = 1;
+ }
+
+ /* If LDO_H disabled */
+ if (snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE0) & 0x80) {
+ snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x10, 0);
+ snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0x80);
+ usleep_range(generic->t_ldoh, generic->t_ldoh);
+ snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0);
+
+ if (central_bias_enabled)
+ snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x1,
+ 0);
+ }
+
+ snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc, 0x3,
+ mbhc->mbhc_cfg->micbias);
+
+ wcd9xxx_enable_irq(mbhc->resmgr->core, WCD9XXX_IRQ_MBHC_INSERTION);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
+ pr_debug("%s: leave\n", __func__);
+
+ return 0;
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_find_plug_and_report(struct wcd9xxx_mbhc *mbhc,
+ enum wcd9xxx_mbhc_plug_type plug_type)
+{
+ pr_debug("%s: enter current_plug(%d) new_plug(%d)\n",
+ __func__, mbhc->current_plug, plug_type);
+
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ if (plug_type == PLUG_TYPE_HEADPHONE &&
+ mbhc->current_plug == PLUG_TYPE_NONE) {
+ /*
+ * Nothing was reported previously
+ * report a headphone or unsupported
+ */
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ } else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
+ if (!mbhc->mbhc_cfg->detect_extn_cable) {
+ if (mbhc->current_plug == PLUG_TYPE_HEADSET)
+ wcd9xxx_report_plug(mbhc, 0,
+ SND_JACK_HEADSET);
+ else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE)
+ wcd9xxx_report_plug(mbhc, 0,
+ SND_JACK_HEADPHONE);
+ }
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ } else if (plug_type == PLUG_TYPE_HEADSET) {
+ /*
+ * If Headphone was reported previously, this will
+ * only report the mic line
+ */
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADSET);
+ msleep(100);
+ wcd9xxx_start_hs_polling(mbhc);
+ } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
+ if (mbhc->mbhc_cfg->detect_extn_cable) {
+ /* High impedance device found. Report as LINEOUT*/
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ pr_debug("%s: setup mic trigger for further detection\n",
+ __func__);
+ mbhc->lpi_enabled = true;
+ /*
+ * Do not enable HPHL trigger. If playback is active,
+ * it might lead to continuous false HPHL triggers
+ */
+ wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER,
+ false);
+ } else {
+ if (mbhc->current_plug == PLUG_TYPE_NONE)
+ wcd9xxx_report_plug(mbhc, 1,
+ SND_JACK_HEADPHONE);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ pr_debug("setup mic trigger for further detection\n");
+ mbhc->lpi_enabled = true;
+ wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
+ MBHC_USE_HPHL_TRIGGER,
+ false);
+ }
+ } else {
+ WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
+ mbhc->current_plug, plug_type);
+ }
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_mbhc_decide_swch_plug(struct wcd9xxx_mbhc *mbhc)
+{
+ enum wcd9xxx_mbhc_plug_type plug_type;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ wcd9xxx_turn_onoff_override(codec, true);
+ plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
+ wcd9xxx_turn_onoff_override(codec, false);
+
+ if (wcd9xxx_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch level is low when determining plug\n",
+ __func__);
+ return;
+ }
+
+ if (plug_type == PLUG_TYPE_INVALID ||
+ plug_type == PLUG_TYPE_GND_MIC_SWAP) {
+ wcd9xxx_schedule_hs_detect_plug(mbhc,
+ &mbhc->correct_plug_swch);
+ } else if (plug_type == PLUG_TYPE_HEADPHONE) {
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
+ wcd9xxx_schedule_hs_detect_plug(mbhc,
+ &mbhc->correct_plug_swch);
+ } else {
+ pr_debug("%s: Valid plug found, determine plug type %d\n",
+ __func__, plug_type);
+ wcd9xxx_find_plug_and_report(mbhc, plug_type);
+ }
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_mbhc_detect_plug_type(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
+ WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
+
+ pr_debug("%s: enter\n", __func__);
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ /*
+ * Turn on the override,
+ * wcd9xxx_mbhc_setup_hs_polling requires override on
+ */
+ wcd9xxx_turn_onoff_override(codec, true);
+ if (plug_det->t_ins_complete > 20)
+ msleep(plug_det->t_ins_complete);
+ else
+ usleep_range(plug_det->t_ins_complete * 1000,
+ plug_det->t_ins_complete * 1000);
+ /* Turn off the override */
+ wcd9xxx_turn_onoff_override(codec, false);
+
+ if (wcd9xxx_swch_level_remove(mbhc))
+ pr_debug("%s: Switch level low when determining plug\n",
+ __func__);
+ else
+ wcd9xxx_mbhc_decide_swch_plug(mbhc);
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void wcd9xxx_hs_insert_irq_swch(struct wcd9xxx_mbhc *mbhc,
+ bool is_removal)
+{
+ if (!is_removal) {
+ pr_debug("%s: MIC trigger insertion interrupt\n", __func__);
+
+ rmb();
+ if (mbhc->lpi_enabled)
+ msleep(100);
+
+ rmb();
+ if (!mbhc->lpi_enabled) {
+ pr_debug("%s: lpi is disabled\n", __func__);
+ } else if (!wcd9xxx_swch_level_remove(mbhc)) {
+ pr_debug("%s: Valid insertion, detect plug type\n",
+ __func__);
+ wcd9xxx_mbhc_decide_swch_plug(mbhc);
+ } else {
+ pr_debug("%s: Invalid insertion stop plug detection\n",
+ __func__);
+ }
+ } else if (mbhc->mbhc_cfg->detect_extn_cable) {
+ pr_debug("%s: Removal\n", __func__);
+ if (!wcd9xxx_swch_level_remove(mbhc)) {
+ /*
+ * Switch indicates, something is still inserted.
+ * This could be extension cable i.e. headset is
+ * removed from extension cable.
+ */
+ /* cancel detect plug */
+ wcd9xxx_cancel_hs_detect_plug(mbhc,
+ &mbhc->correct_plug_swch);
+ wcd9xxx_mbhc_decide_swch_plug(mbhc);
+ }
+ } else {
+ pr_err("%s: Switch IRQ used, invalid MBHC Removal\n", __func__);
+ }
+}
+
+static bool is_valid_mic_voltage(struct wcd9xxx_mbhc *mbhc, s32 mic_mv)
+{
+ const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
+ WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+ const s16 v_hs_max = wcd9xxx_get_current_v_hs_max(mbhc);
+
+ return (!(mic_mv > 10 && mic_mv < 80) && (mic_mv > plug_type->v_no_mic)
+ && (mic_mv < v_hs_max)) ? true : false;
+}
+
+/*
+ * called under codec_resource_lock acquisition
+ * returns true if mic voltage range is back to normal insertion
+ * returns false either if timedout or removed
+ */
+static bool wcd9xxx_hs_remove_settle(struct wcd9xxx_mbhc *mbhc)
+{
+ int i;
+ bool timedout, settled = false;
+ s32 mic_mv[NUM_DCE_PLUG_DETECT];
+ short mb_v[NUM_DCE_PLUG_DETECT];
+ unsigned long retry = 0, timeout;
+
+ timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
+ while (!(timedout = time_after(jiffies, timeout))) {
+ retry++;
+ if (wcd9xxx_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch indicates removal\n", __func__);
+ break;
+ }
+
+ if (retry > 1)
+ msleep(250);
+ else
+ msleep(50);
+
+ if (wcd9xxx_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch indicates removal\n", __func__);
+ break;
+ }
+
+ for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
+ mb_v[i] = wcd9xxx_codec_sta_dce(mbhc, 1, true);
+ mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1 , mb_v[i]);
+ pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
+ __func__, retry, mic_mv[i], mb_v[i]);
+ }
+
+ if (wcd9xxx_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switcn indicates removal\n", __func__);
+ break;
+ }
+
+ if (mbhc->current_plug == PLUG_TYPE_NONE) {
+ pr_debug("%s : headset/headphone is removed\n",
+ __func__);
+ break;
+ }
+
+ for (i = 0; i < NUM_DCE_PLUG_DETECT; i++)
+ if (!is_valid_mic_voltage(mbhc, mic_mv[i]))
+ break;
+
+ if (i == NUM_DCE_PLUG_DETECT) {
+ pr_debug("%s: MIC voltage settled\n", __func__);
+ settled = true;
+ msleep(200);
+ break;
+ }
+ }
+
+ if (timedout)
+ pr_debug("%s: Microphone did not settle in %d seconds\n",
+ __func__, HS_DETECT_PLUG_TIME_MS);
+ return settled;
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void wcd9xxx_hs_remove_irq_swch(struct wcd9xxx_mbhc *mbhc)
+{
+ pr_debug("%s: enter\n", __func__);
+ if (wcd9xxx_hs_remove_settle(mbhc))
+ wcd9xxx_start_hs_polling(mbhc);
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void wcd9xxx_hs_remove_irq_noswch(struct wcd9xxx_mbhc *mbhc)
+{
+ short bias_value;
+ bool removed = true;
+ struct snd_soc_codec *codec = mbhc->codec;
+ const struct wcd9xxx_mbhc_general_cfg *generic =
+ WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+ int min_us = FAKE_REMOVAL_MIN_PERIOD_MS * 1000;
+
+ pr_debug("%s: enter\n", __func__);
+ if (mbhc->current_plug != PLUG_TYPE_HEADSET) {
+ pr_debug("%s(): Headset is not inserted, ignore removal\n",
+ __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+ 0x08, 0x08);
+ return;
+ }
+
+ usleep_range(generic->t_shutdown_plug_rem,
+ generic->t_shutdown_plug_rem);
+
+ do {
+ bias_value = wcd9xxx_codec_sta_dce(mbhc, 1, true);
+ pr_debug("%s: DCE %d,%d, %d us left\n", __func__, bias_value,
+ wcd9xxx_codec_sta_dce_v(mbhc, 1, bias_value), min_us);
+ if (bias_value < wcd9xxx_get_current_v_ins(mbhc, false)) {
+ pr_debug("%s: checking false removal\n", __func__);
+ msleep(500);
+ removed = !wcd9xxx_hs_remove_settle(mbhc);
+ pr_debug("%s: headset %sactually removed\n", __func__,
+ removed ? "" : "not ");
+ break;
+ }
+ min_us -= mbhc->mbhc_data.t_dce;
+ } while (min_us > 0);
+
+ if (removed) {
+ if (mbhc->mbhc_cfg->detect_extn_cable) {
+ if (!wcd9xxx_swch_level_remove(mbhc)) {
+ /*
+ * extension cable is still plugged in
+ * report it as LINEOUT device
+ */
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ wcd9xxx_enable_hs_detect(mbhc, 1,
+ MBHC_USE_MB_TRIGGER,
+ false);
+ }
+ } else {
+ /* Cancel possibly running hs_detect_work */
+ wcd9xxx_cancel_hs_detect_plug(mbhc,
+ &mbhc->correct_plug_noswch);
+ /*
+ * If this removal is not false, first check the micbias
+ * switch status and switch it to LDOH if it is already
+ * switched to VDDIO.
+ */
+ wcd9xxx_switch_micbias(mbhc, 0);
+
+ wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
+ MBHC_USE_HPHL_TRIGGER,
+ true);
+ }
+ } else {
+ wcd9xxx_start_hs_polling(mbhc);
+ }
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void wcd9xxx_hs_insert_irq_extn(struct wcd9xxx_mbhc *mbhc,
+ bool is_mb_trigger)
+{
+ /* Cancel possibly running hs_detect_work */
+ wcd9xxx_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
+
+ if (is_mb_trigger) {
+ pr_debug("%s: Waiting for Headphone left trigger\n", __func__);
+ wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_HPHL_TRIGGER, false);
+ } else {
+ pr_debug("%s: HPHL trigger received, detecting plug type\n",
+ __func__);
+ wcd9xxx_mbhc_detect_plug_type(mbhc);
+ }
+}
+
+static irqreturn_t wcd9xxx_hs_remove_irq(int irq, void *data)
+{
+ bool vddio;
+ struct wcd9xxx_mbhc *mbhc = data;
+
+ pr_debug("%s: enter, removal interrupt\n", __func__);
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
+ mbhc->mbhc_micbias_switched);
+ if (vddio)
+ __wcd9xxx_switch_micbias(mbhc, 0, false, true);
+
+ if (mbhc->mbhc_cfg->detect_extn_cable &&
+ !wcd9xxx_swch_level_remove(mbhc))
+ wcd9xxx_hs_remove_irq_noswch(mbhc);
+ else
+ wcd9xxx_hs_remove_irq_swch(mbhc);
+
+ /*
+ * if driver turned off vddio switch and headset is not removed,
+ * turn on the vddio switch back, if headset is removed then vddio
+ * switch is off by time now and shouldn't be turn on again from here
+ */
+ if (vddio && mbhc->current_plug == PLUG_TYPE_HEADSET)
+ __wcd9xxx_switch_micbias(mbhc, 1, true, true);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_hs_insert_irq(int irq, void *data)
+{
+ bool is_mb_trigger, is_removal;
+ struct wcd9xxx_mbhc *mbhc = data;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
+
+ is_mb_trigger = !!(snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg) &
+ 0x10);
+ is_removal = !!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_INT_CTL) & 0x02);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
+
+ /* Turn off both HPH and MIC line schmitt triggers */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+
+ if (mbhc->mbhc_cfg->detect_extn_cable &&
+ mbhc->current_plug == PLUG_TYPE_HIGH_HPH)
+ wcd9xxx_hs_insert_irq_extn(mbhc, is_mb_trigger);
+ else
+ wcd9xxx_hs_insert_irq_swch(mbhc, is_removal);
+
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ return IRQ_HANDLED;
+}
+
+static void wcd9xxx_btn_lpress_fn(struct work_struct *work)
+{
+ struct delayed_work *dwork;
+ short bias_value;
+ int dce_mv, sta_mv;
+ struct wcd9xxx_mbhc *mbhc;
+
+ pr_debug("%s:\n", __func__);
+
+ dwork = to_delayed_work(work);
+ mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_btn_dwork);
+
+ bias_value = wcd9xxx_read_sta_result(mbhc->codec);
+ sta_mv = wcd9xxx_codec_sta_dce_v(mbhc, 0, bias_value);
+
+ bias_value = wcd9xxx_read_dce_result(mbhc->codec);
+ dce_mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, bias_value);
+ pr_debug("%s: STA: %d, DCE: %d\n", __func__, sta_mv, dce_mv);
+
+ pr_debug("%s: Reporting long button press event\n", __func__);
+ wcd9xxx_jack_report(&mbhc->button_jack, mbhc->buttons_pressed,
+ mbhc->buttons_pressed);
+
+ pr_debug("%s: leave\n", __func__);
+ wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+}
+
+static void wcd9xxx_mbhc_insert_work(struct work_struct *work)
+{
+ struct delayed_work *dwork;
+ struct wcd9xxx_mbhc *mbhc;
+ struct snd_soc_codec *codec;
+ struct wcd9xxx *core;
+
+ dwork = to_delayed_work(work);
+ mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_insert_dwork);
+ codec = mbhc->codec;
+ core = mbhc->resmgr->core;
+
+ pr_debug("%s:\n", __func__);
+
+ /* Turn off both HPH and MIC line schmitt triggers */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+ wcd9xxx_disable_irq_sync(core, WCD9XXX_IRQ_MBHC_INSERTION);
+ wcd9xxx_mbhc_detect_plug_type(mbhc);
+ wcd9xxx_unlock_sleep(core);
+}
+
+static bool wcd9xxx_mbhc_fw_validate(const struct firmware *fw)
+{
+ u32 cfg_offset;
+ struct wcd9xxx_mbhc_imped_detect_cfg *imped_cfg;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
+
+ if (fw->size < WCD9XXX_MBHC_CAL_MIN_SIZE)
+ return false;
+
+ /*
+ * Previous check guarantees that there is enough fw data up
+ * to num_btn
+ */
+ btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(fw->data);
+ cfg_offset = (u32) ((void *) btn_cfg - (void *) fw->data);
+ if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_BTN_SZ(btn_cfg)))
+ return false;
+
+ /*
+ * Previous check guarantees that there is enough fw data up
+ * to start of impedance detection configuration
+ */
+ imped_cfg = WCD9XXX_MBHC_CAL_IMPED_DET_PTR(fw->data);
+ cfg_offset = (u32) ((void *) imped_cfg - (void *) fw->data);
+
+ if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_MIN_SZ))
+ return false;
+
+ if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_SZ(imped_cfg)))
+ return false;
+
+ return true;
+}
+
+static u16 wcd9xxx_codec_v_sta_dce(struct wcd9xxx_mbhc *mbhc,
+ enum meas_type dce, s16 vin_mv)
+{
+ s16 diff, zero;
+ u32 mb_mv, in;
+ u16 value;
+
+ mb_mv = mbhc->mbhc_data.micb_mv;
+
+ if (mb_mv == 0) {
+ pr_err("%s: Mic Bias voltage is set to zero\n", __func__);
+ return -EINVAL;
+ }
+
+ if (dce) {
+ diff = (mbhc->mbhc_data.dce_mb) - (mbhc->mbhc_data.dce_z);
+ zero = (mbhc->mbhc_data.dce_z);
+ } else {
+ diff = (mbhc->mbhc_data.sta_mb) - (mbhc->mbhc_data.sta_z);
+ zero = (mbhc->mbhc_data.sta_z);
+ }
+ in = (u32) diff * vin_mv;
+
+ value = (u16) (in / mb_mv) + zero;
+ return value;
+}
+
+static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec;
+ s16 btn_mv = 0, btn_delta_mv;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+ struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
+ u16 *btn_high;
+ int i;
+
+ pr_debug("%s: enter\n", __func__);
+ codec = mbhc->codec;
+ btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+ plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+
+ mbhc->mbhc_data.v_ins_hu =
+ wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_hs_max);
+ mbhc->mbhc_data.v_ins_h =
+ wcd9xxx_codec_v_sta_dce(mbhc, DCE, plug_type->v_hs_max);
+
+ mbhc->mbhc_data.v_inval_ins_low = FAKE_INS_LOW;
+ mbhc->mbhc_data.v_inval_ins_high = FAKE_INS_HIGH;
+
+ if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
+ mbhc->mbhc_data.adj_v_hs_max =
+ scale_v_micb_vddio(mbhc, plug_type->v_hs_max, true);
+ mbhc->mbhc_data.adj_v_ins_hu =
+ wcd9xxx_codec_v_sta_dce(mbhc, STA,
+ mbhc->mbhc_data.adj_v_hs_max);
+ mbhc->mbhc_data.adj_v_ins_h =
+ wcd9xxx_codec_v_sta_dce(mbhc, DCE,
+ mbhc->mbhc_data.adj_v_hs_max);
+ mbhc->mbhc_data.v_inval_ins_low =
+ scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_low,
+ false);
+ mbhc->mbhc_data.v_inval_ins_high =
+ scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_high,
+ false);
+ }
+
+ btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
+ MBHC_BTN_DET_V_BTN_HIGH);
+ for (i = 0; i < btn_det->num_btn; i++)
+ btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv;
+
+ mbhc->mbhc_data.v_b1_h = wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv);
+ btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_sta;
+ mbhc->mbhc_data.v_b1_hu =
+ wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_delta_mv);
+
+ btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_cic;
+
+ mbhc->mbhc_data.v_b1_huc =
+ wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_delta_mv);
+
+ mbhc->mbhc_data.v_brh = mbhc->mbhc_data.v_b1_h;
+ mbhc->mbhc_data.v_brl = BUTTON_MIN;
+
+ mbhc->mbhc_data.v_no_mic =
+ wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_no_mic);
+ pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_onoff_ext_mclk(struct wcd9xxx_mbhc *mbhc, bool on)
+{
+ /*
+ * XXX: {codec}_mclk_enable holds WCD9XXX_BCL_LOCK,
+ * therefore wcd9xxx_onoff_ext_mclk caller SHOULDN'T hold
+ * WCD9XXX_BCL_LOCK when it calls wcd9xxx_onoff_ext_mclk()
+ */
+ mbhc->mbhc_cfg->mclk_cb_fn(mbhc->codec, on, false);
+}
+
+static void wcd9xxx_correct_swch_plug(struct work_struct *work)
+{
+ struct wcd9xxx_mbhc *mbhc;
+ struct snd_soc_codec *codec;
+ enum wcd9xxx_mbhc_plug_type plug_type = PLUG_TYPE_INVALID;
+ unsigned long timeout;
+ int retry = 0, pt_gnd_mic_swap_cnt = 0;
+ bool correction = false;
+
+ pr_debug("%s: enter\n", __func__);
+
+ mbhc = container_of(work, struct wcd9xxx_mbhc, correct_plug_swch);
+ codec = mbhc->codec;
+
+ wcd9xxx_onoff_ext_mclk(mbhc, true);
+
+ /*
+ * Keep override on during entire plug type correction work.
+ *
+ * This is okay under the assumption that any switch irqs which use
+ * MBHC block cancel and sync this work so override is off again
+ * prior to switch interrupt handler's MBHC block usage.
+ * Also while this correction work is running, we can guarantee
+ * DAPM doesn't use any MBHC block as this work only runs with
+ * headphone detection.
+ */
+ wcd9xxx_turn_onoff_override(codec, true);
+
+ timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
+ while (!time_after(jiffies, timeout)) {
+ ++retry;
+ rmb();
+ if (mbhc->hs_detect_work_stop) {
+ pr_debug("%s: stop requested\n", __func__);
+ break;
+ }
+
+ msleep(HS_DETECT_PLUG_INERVAL_MS);
+ if (wcd9xxx_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch level is low\n", __func__);
+ break;
+ }
+
+ /* can race with removal interrupt */
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+
+ pr_debug("%s: attempt(%d) current_plug(%d) new_plug(%d)\n",
+ __func__, retry, mbhc->current_plug, plug_type);
+ if (plug_type == PLUG_TYPE_INVALID) {
+ pr_debug("Invalid plug in attempt # %d\n", retry);
+ if (!mbhc->mbhc_cfg->detect_extn_cable &&
+ retry == NUM_ATTEMPTS_TO_REPORT &&
+ mbhc->current_plug == PLUG_TYPE_NONE) {
+ wcd9xxx_report_plug(mbhc, 1,
+ SND_JACK_HEADPHONE);
+ }
+ } else if (plug_type == PLUG_TYPE_HEADPHONE) {
+ pr_debug("Good headphone detected, continue polling\n");
+ if (mbhc->mbhc_cfg->detect_extn_cable) {
+ if (mbhc->current_plug != plug_type)
+ wcd9xxx_report_plug(mbhc, 1,
+ SND_JACK_HEADPHONE);
+ } else if (mbhc->current_plug == PLUG_TYPE_NONE) {
+ wcd9xxx_report_plug(mbhc, 1,
+ SND_JACK_HEADPHONE);
+ }
+ } else {
+ if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
+ pt_gnd_mic_swap_cnt++;
+ if (pt_gnd_mic_swap_cnt <
+ GND_MIC_SWAP_THRESHOLD)
+ continue;
+ else if (pt_gnd_mic_swap_cnt >
+ GND_MIC_SWAP_THRESHOLD) {
+ /*
+ * This is due to GND/MIC switch didn't
+ * work, Report unsupported plug
+ */
+ } else if (mbhc->mbhc_cfg->swap_gnd_mic) {
+ /*
+ * if switch is toggled, check again,
+ * otherwise report unsupported plug
+ */
+ if (mbhc->mbhc_cfg->swap_gnd_mic(codec))
+ continue;
+ }
+ } else
+ pt_gnd_mic_swap_cnt = 0;
+
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ /* Turn off override */
+ wcd9xxx_turn_onoff_override(codec, false);
+ /*
+ * The valid plug also includes PLUG_TYPE_GND_MIC_SWAP
+ */
+ wcd9xxx_find_plug_and_report(mbhc, plug_type);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ pr_debug("Attempt %d found correct plug %d\n", retry,
+ plug_type);
+ correction = true;
+ break;
+ }
+ }
+
+ /* Turn off override */
+ if (!correction)
+ wcd9xxx_turn_onoff_override(codec, false);
+
+ wcd9xxx_onoff_ext_mclk(mbhc, false);
+
+ if (mbhc->mbhc_cfg->detect_extn_cable) {
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ if (mbhc->current_plug == PLUG_TYPE_HEADPHONE ||
+ mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP ||
+ mbhc->current_plug == PLUG_TYPE_INVALID ||
+ plug_type == PLUG_TYPE_INVALID) {
+ /* Enable removal detection */
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ wcd9xxx_enable_hs_detect(mbhc, 0, 0, false);
+ }
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ }
+ pr_debug("%s: leave current_plug(%d)\n", __func__, mbhc->current_plug);
+ /* unlock sleep */
+ wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+}
+
+static void wcd9xxx_swch_irq_handler(struct wcd9xxx_mbhc *mbhc)
+{
+ bool insert;
+ bool is_removed = false;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+
+ mbhc->in_swch_irq_handler = true;
+ /* Wait here for debounce time */
+ usleep_range(SWCH_IRQ_DEBOUNCE_TIME_US, SWCH_IRQ_DEBOUNCE_TIME_US);
+
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+
+ /* cancel pending button press */
+ if (wcd9xxx_cancel_btn_work(mbhc))
+ pr_debug("%s: button press is canceled\n", __func__);
+
+ 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);
+
+ /* Disable Mic Bias pull down and HPH Switch to GND */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01,
+ 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x00);
+ wcd9xxx_mbhc_detect_plug_type(mbhc);
+ } 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);
+ is_removed = true;
+ } else if (mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP) {
+ wcd9xxx_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
+ is_removed = true;
+ } else if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
+ wcd9xxx_pause_hs_polling(mbhc);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
+ is_removed = true;
+ } else if (mbhc->current_plug == PLUG_TYPE_HIGH_HPH) {
+ wcd9xxx_report_plug(mbhc, 0, SND_JACK_LINEOUT);
+ is_removed = true;
+ }
+
+ if (is_removed) {
+ /* Enable Mic Bias pull down and HPH Switch to GND */
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.ctl_reg, 0x01,
+ 0x01);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01,
+ 0x01);
+ /* Make sure mic trigger is turned off */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x90, 0x00);
+ /* Reset MBHC State Machine */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+ 0x08, 0x08);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+ 0x08, 0x00);
+ /* Turn off override */
+ wcd9xxx_turn_onoff_override(codec, false);
+ }
+ }
+
+ mbhc->in_swch_irq_handler = false;
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ pr_debug("%s: leave\n", __func__);
+}
+
+static irqreturn_t wcd9xxx_mech_plug_detect_irq(int irq, void *data)
+{
+ int r = IRQ_HANDLED;
+ struct wcd9xxx_mbhc *mbhc = data;
+
+ pr_debug("%s: enter\n", __func__);
+ if (unlikely(wcd9xxx_lock_sleep(mbhc->resmgr->core) == false)) {
+ pr_warn("%s: failed to hold suspend\n", __func__);
+ r = IRQ_NONE;
+ } else {
+ /* Call handler */
+ wcd9xxx_swch_irq_handler(mbhc);
+ wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+ }
+
+ pr_debug("%s: leave %d\n", __func__, r);
+ return r;
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_codec_drive_v_to_micbias(struct wcd9xxx_mbhc *mbhc,
+ int usec)
+{
+ int cfilt_k_val;
+ bool set = true;
+
+ if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
+ mbhc->mbhc_micbias_switched) {
+ pr_debug("%s: set mic V to micbias V\n", __func__);
+ snd_soc_update_bits(mbhc->codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+ 0x2, 0x2);
+ wcd9xxx_turn_onoff_override(mbhc->codec, true);
+ while (1) {
+ cfilt_k_val =
+ wcd9xxx_resmgr_get_k_val(mbhc->resmgr,
+ set ? mbhc->mbhc_data.micb_mv :
+ VDDIO_MICBIAS_MV);
+ snd_soc_update_bits(mbhc->codec,
+ mbhc->mbhc_bias_regs.cfilt_val,
+ 0xFC, (cfilt_k_val << 2));
+ if (!set)
+ break;
+ usleep_range(usec, usec);
+ set = false;
+ }
+ wcd9xxx_turn_onoff_override(mbhc->codec, false);
+ }
+}
+
+static int wcd9xxx_is_fake_press(struct wcd9xxx_mbhc *mbhc)
+{
+ int i;
+ int r = 0;
+ const int dces = NUM_DCE_PLUG_DETECT;
+ s16 mb_v, v_ins_hu, v_ins_h;
+
+ v_ins_hu = wcd9xxx_get_current_v_ins(mbhc, true);
+ v_ins_h = wcd9xxx_get_current_v_ins(mbhc, false);
+
+ for (i = 0; i < dces; i++) {
+ usleep_range(10000, 10000);
+ if (i == 0) {
+ mb_v = wcd9xxx_codec_sta_dce(mbhc, 0, true);
+ pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
+ wcd9xxx_codec_sta_dce_v(mbhc, 0, mb_v));
+ if (mb_v < (s16)mbhc->mbhc_data.v_b1_hu ||
+ mb_v > v_ins_hu) {
+ r = 1;
+ break;
+ }
+ } else {
+ mb_v = wcd9xxx_codec_sta_dce(mbhc, 1, true);
+ pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
+ wcd9xxx_codec_sta_dce_v(mbhc, 1, mb_v));
+ if (mb_v < (s16)mbhc->mbhc_data.v_b1_h ||
+ mb_v > v_ins_h) {
+ r = 1;
+ break;
+ }
+ }
+ }
+
+ return r;
+}
+
+/* called under codec_resource_lock acquisition */
+static int wcd9xxx_determine_button(const struct wcd9xxx_mbhc *mbhc,
+ const s32 micmv)
+{
+ s16 *v_btn_low, *v_btn_high;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+ int i, btn = -1;
+
+ btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+ v_btn_low = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
+ MBHC_BTN_DET_V_BTN_LOW);
+ v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
+ MBHC_BTN_DET_V_BTN_HIGH);
+
+ for (i = 0; i < btn_det->num_btn; i++) {
+ if ((v_btn_low[i] <= micmv) && (v_btn_high[i] >= micmv)) {
+ btn = i;
+ break;
+ }
+ }
+
+ if (btn == -1)
+ pr_debug("%s: couldn't find button number for mic mv %d\n",
+ __func__, micmv);
+
+ return btn;
+}
+
+static int wcd9xxx_get_button_mask(const int btn)
+{
+ int mask = 0;
+ switch (btn) {
+ case 0:
+ mask = SND_JACK_BTN_0;
+ break;
+ case 1:
+ mask = SND_JACK_BTN_1;
+ break;
+ case 2:
+ mask = SND_JACK_BTN_2;
+ break;
+ case 3:
+ mask = SND_JACK_BTN_3;
+ break;
+ case 4:
+ mask = SND_JACK_BTN_4;
+ break;
+ case 5:
+ mask = SND_JACK_BTN_5;
+ break;
+ case 6:
+ mask = SND_JACK_BTN_6;
+ break;
+ case 7:
+ mask = SND_JACK_BTN_7;
+ break;
+ }
+ return mask;
+}
+
+irqreturn_t wcd9xxx_dce_handler(int irq, void *data)
+{
+ int i, mask;
+ short dce, sta;
+ s32 mv, mv_s, stamv_s;
+ bool vddio;
+ u8 mbhc_status;
+ int btn = -1, meas = 0;
+ struct wcd9xxx_mbhc *mbhc = data;
+ const struct wcd9xxx_mbhc_btn_detect_cfg *d =
+ WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+ short btnmeas[d->n_btn_meas + 1];
+ struct snd_soc_codec *codec = mbhc->codec;
+ struct wcd9xxx *core = mbhc->resmgr->core;
+ int n_btn_meas = d->n_btn_meas;
+
+ pr_debug("%s: enter\n", __func__);
+
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ mbhc_status = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_STATUS) & 0x3E;
+
+ if (mbhc->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) {
+ pr_debug("%s: mbhc is being recovered, skip button press\n",
+ __func__);
+ goto done;
+ }
+
+ mbhc->mbhc_state = MBHC_STATE_POTENTIAL;
+
+ if (!mbhc->polling_active) {
+ pr_warn("%s: mbhc polling is not active, skip button press\n",
+ __func__);
+ goto done;
+ }
+
+ dce = wcd9xxx_read_dce_result(codec);
+ mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, dce);
+
+ /* If switch nterrupt already kicked in, ignore button press */
+ if (mbhc->in_swch_irq_handler) {
+ pr_debug("%s: Swtich level changed, ignore button press\n",
+ __func__);
+ btn = -1;
+ goto done;
+ }
+
+ /* Measure scaled HW DCE */
+ vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
+ mbhc->mbhc_micbias_switched);
+ mv_s = vddio ? scale_v_micb_vddio(mbhc, mv, false) : mv;
+
+ /* Measure scaled HW STA */
+ sta = wcd9xxx_read_sta_result(codec);
+ stamv_s = wcd9xxx_codec_sta_dce_v(mbhc, 0, sta);
+ if (vddio)
+ stamv_s = scale_v_micb_vddio(mbhc, stamv_s, false);
+ if (mbhc_status != STATUS_REL_DETECTION) {
+ if (mbhc->mbhc_last_resume &&
+ !time_after(jiffies, mbhc->mbhc_last_resume + HZ)) {
+ pr_debug("%s: Button is released after resume\n",
+ __func__);
+ n_btn_meas = 0;
+ } else {
+ pr_debug("%s: Button is released without resume",
+ __func__);
+ btn = wcd9xxx_determine_button(mbhc, mv_s);
+ if (btn != wcd9xxx_determine_button(mbhc, stamv_s))
+ btn = -1;
+ goto done;
+ }
+ }
+
+ pr_debug("%s: Meas HW - STA 0x%x,%d,%d\n", __func__,
+ sta & 0xFFFF, wcd9xxx_codec_sta_dce_v(mbhc, 0, sta), stamv_s);
+
+ /* determine pressed button */
+ btnmeas[meas++] = wcd9xxx_determine_button(mbhc, mv_s);
+ pr_debug("%s: Meas HW - DCE 0x%x,%d,%d button %d\n", __func__,
+ dce & 0xFFFF, mv, mv_s, btnmeas[meas - 1]);
+ if (n_btn_meas == 0)
+ btn = btnmeas[0];
+ for (; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1))); meas++) {
+ dce = wcd9xxx_codec_sta_dce(mbhc, 1, false);
+ mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, dce);
+ mv_s = vddio ? scale_v_micb_vddio(mbhc, mv, false) : mv;
+
+ btnmeas[meas] = wcd9xxx_determine_button(mbhc, mv_s);
+ pr_debug("%s: Meas %d - DCE 0x%x,%d,%d button %d\n",
+ __func__, meas, dce & 0xFFFF, mv, mv_s, btnmeas[meas]);
+ /*
+ * if large enough measurements are collected,
+ * start to check if last all n_btn_con measurements were
+ * in same button low/high range
+ */
+ if (meas + 1 >= d->n_btn_con) {
+ for (i = 0; i < d->n_btn_con; i++)
+ if ((btnmeas[meas] < 0) ||
+ (btnmeas[meas] != btnmeas[meas - i]))
+ break;
+ if (i == d->n_btn_con) {
+ /* button pressed */
+ btn = btnmeas[meas];
+ break;
+ } else if ((n_btn_meas - meas) < (d->n_btn_con - 1)) {
+ /*
+ * if left measurements are less than n_btn_con,
+ * it's impossible to find button number
+ */
+ break;
+ }
+ }
+ }
+
+ if (btn >= 0) {
+ if (mbhc->in_swch_irq_handler) {
+ pr_debug(
+ "%s: Switch irq triggered, ignore button press\n",
+ __func__);
+ goto done;
+ }
+ mask = wcd9xxx_get_button_mask(btn);
+ mbhc->buttons_pressed |= mask;
+ wcd9xxx_lock_sleep(core);
+ if (schedule_delayed_work(&mbhc->mbhc_btn_dwork,
+ msecs_to_jiffies(400)) == 0) {
+ WARN(1, "Button pressed twice without release event\n");
+ wcd9xxx_unlock_sleep(core);
+ }
+ } else {
+ pr_debug("%s: bogus button press, too short press?\n",
+ __func__);
+ }
+
+ done:
+ pr_debug("%s: leave\n", __func__);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_release_handler(int irq, void *data)
+{
+ int ret;
+ struct wcd9xxx_mbhc *mbhc = data;
+
+ pr_debug("%s: enter\n", __func__);
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ mbhc->mbhc_state = MBHC_STATE_RELEASE;
+
+ wcd9xxx_codec_drive_v_to_micbias(mbhc, 10000);
+
+ if (mbhc->buttons_pressed & WCD9XXX_JACK_BUTTON_MASK) {
+ ret = wcd9xxx_cancel_btn_work(mbhc);
+ if (ret == 0) {
+ pr_debug("%s: Reporting long button release event\n",
+ __func__);
+ wcd9xxx_jack_report(&mbhc->button_jack, 0,
+ mbhc->buttons_pressed);
+ } else {
+ if (wcd9xxx_is_fake_press(mbhc)) {
+ pr_debug("%s: Fake button press interrupt\n",
+ __func__);
+ } else {
+ if (mbhc->in_swch_irq_handler) {
+ pr_debug("%s: Switch irq kicked in, ignore\n",
+ __func__);
+ } else {
+ pr_debug("%s: Reporting btn press\n",
+ __func__);
+ wcd9xxx_jack_report(&mbhc->button_jack,
+ mbhc->buttons_pressed,
+ mbhc->buttons_pressed);
+ pr_debug("%s: Reporting btn release\n",
+ __func__);
+ wcd9xxx_jack_report(&mbhc->button_jack,
+ 0, mbhc->buttons_pressed);
+ }
+ }
+ }
+
+ mbhc->buttons_pressed &= ~WCD9XXX_JACK_BUTTON_MASK;
+ }
+
+ wcd9xxx_calibrate_hs_polling(mbhc);
+
+ msleep(SWCH_REL_DEBOUNCE_TIME_MS);
+ wcd9xxx_start_hs_polling(mbhc);
+
+ pr_debug("%s: leave\n", __func__);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_hphl_ocp_irq(int irq, void *data)
+{
+ struct wcd9xxx_mbhc *mbhc = data;
+ struct snd_soc_codec *codec;
+
+ pr_info("%s: received HPHL OCP irq\n", __func__);
+
+ if (mbhc) {
+ codec = mbhc->codec;
+ if (mbhc->hphlocp_cnt++ < OCP_ATTEMPT) {
+ pr_info("%s: retry\n", __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
+ 0x10, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
+ 0x10, 0x10);
+ } else {
+ wcd9xxx_disable_irq(codec->control_data,
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+ mbhc->hphlocp_cnt = 0;
+ mbhc->hph_status |= SND_JACK_OC_HPHL;
+ wcd9xxx_jack_report(&mbhc->headset_jack,
+ mbhc->hph_status,
+ WCD9XXX_JACK_MASK);
+ }
+ } else {
+ pr_err("%s: Bad wcd9xxx private data\n", __func__);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_hphr_ocp_irq(int irq, void *data)
+{
+ struct wcd9xxx_mbhc *mbhc = data;
+ struct snd_soc_codec *codec;
+
+ pr_info("%s: received HPHR OCP irq\n", __func__);
+ codec = mbhc->codec;
+ if (mbhc->hphrocp_cnt++ < OCP_ATTEMPT) {
+ pr_info("%s: retry\n", __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+ 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+ 0x10);
+ } else {
+ wcd9xxx_disable_irq(mbhc->resmgr->core,
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+ mbhc->hphrocp_cnt = 0;
+ mbhc->hph_status |= SND_JACK_OC_HPHR;
+ wcd9xxx_jack_report(&mbhc->headset_jack,
+ mbhc->hph_status, WCD9XXX_JACK_MASK);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int wcd9xxx_acdb_mclk_index(const int rate)
+{
+ if (rate == MCLK_RATE_12288KHZ)
+ return 0;
+ else if (rate == MCLK_RATE_9600KHZ)
+ return 1;
+ else {
+ BUG_ON(1);
+ return -EINVAL;
+ }
+}
+
+static void wcd9xxx_update_mbhc_clk_rate(struct wcd9xxx_mbhc *mbhc, u32 rate)
+{
+ u32 dce_wait, sta_wait;
+ u8 ncic, nmeas, navg;
+ void *calibration;
+ u8 *n_cic, *n_ready;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+ u8 npoll = 4, nbounce_wait = 30;
+ struct snd_soc_codec *codec = mbhc->codec;
+ int idx = wcd9xxx_acdb_mclk_index(rate);
+ int idxmclk = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
+
+ pr_debug("%s: Updating clock rate dependents, rate = %u\n", __func__,
+ rate);
+ calibration = mbhc->mbhc_cfg->calibration;
+
+ /*
+ * First compute the DCE / STA wait times depending on tunable
+ * parameters. The value is computed in microseconds
+ */
+ btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
+ n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_READY);
+ n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_CIC);
+ nmeas = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration)->n_meas;
+ navg = WCD9XXX_MBHC_CAL_GENERAL_PTR(calibration)->mbhc_navg;
+
+ /* ncic stays with the same what we had during calibration */
+ ncic = n_cic[idxmclk];
+ dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (rate / 1000);
+ sta_wait = (1000 * 128 * (navg + 1)) / (rate / 1000);
+ mbhc->mbhc_data.t_dce = dce_wait;
+ mbhc->mbhc_data.t_sta = sta_wait;
+ mbhc->mbhc_data.t_sta_dce = ((1000 * 256) / (rate / 1000) *
+ n_ready[idx]) + 10;
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL, n_ready[idx]);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL, ncic);
+
+ if (rate == MCLK_RATE_12288KHZ) {
+ npoll = 4;
+ nbounce_wait = 30;
+ } else if (rate == MCLK_RATE_9600KHZ) {
+ npoll = 3;
+ nbounce_wait = 23;
+ }
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL, npoll);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL, nbounce_wait);
+ pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_mbhc_cal(struct wcd9xxx_mbhc *mbhc)
+{
+ u8 cfilt_mode, bg_mode;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_turn_onoff_rel_detection(codec, false);
+
+ /* t_dce and t_sta are updated by wcd9xxx_update_mbhc_clk_rate() */
+ WARN_ON(!mbhc->mbhc_data.t_dce);
+ WARN_ON(!mbhc->mbhc_data.t_sta);
+
+ /*
+ * LDOH and CFILT are already configured during pdata handling.
+ * Only need to make sure CFILT and bandgap are in Fast mode.
+ * Need to restore defaults once calculation is done.
+ */
+ cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x40, 0x00);
+ bg_mode = snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL,
+ 0x02, 0x02);
+
+ /*
+ * Micbias, CFILT, LDOH, MBHC MUX mode settings
+ * to perform ADC calibration
+ */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x60,
+ mbhc->mbhc_cfg->micbias << 5);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1, 0x60, 0x60);
+ snd_soc_write(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x78);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x04);
+
+ /* DCE measurement for 0 volts */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x81);
+ usleep_range(100, 100);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
+ usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
+ mbhc->mbhc_data.dce_z = wcd9xxx_read_dce_result(codec);
+
+ /* DCE measurment for MB voltage */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x82);
+ usleep_range(100, 100);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
+ usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
+ mbhc->mbhc_data.dce_mb = wcd9xxx_read_dce_result(codec);
+
+ /* STA measuremnt for 0 volts */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x81);
+ usleep_range(100, 100);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
+ usleep_range(mbhc->mbhc_data.t_sta, mbhc->mbhc_data.t_sta);
+ mbhc->mbhc_data.sta_z = wcd9xxx_read_sta_result(codec);
+
+ /* STA Measurement for MB Voltage */
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x82);
+ usleep_range(100, 100);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
+ usleep_range(mbhc->mbhc_data.t_sta, mbhc->mbhc_data.t_sta);
+ mbhc->mbhc_data.sta_mb = wcd9xxx_read_sta_result(codec);
+
+ /* Restore default settings. */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x40,
+ cfilt_mode);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x02,
+ bg_mode);
+
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x84);
+ usleep_range(100, 100);
+
+ wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_turn_onoff_rel_detection(codec, true);
+ pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_mbhc_setup(struct wcd9xxx_mbhc *mbhc)
+{
+ int n;
+ u8 *gain;
+ struct wcd9xxx_mbhc_general_cfg *generic;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+ struct snd_soc_codec *codec = mbhc->codec;
+ const int idx = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
+
+ pr_debug("%s: enter\n", __func__);
+ generic = WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+ btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+
+ for (n = 0; n < 8; n++) {
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_FIR_B1_CFG,
+ 0x07, n);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_FIR_B2_CFG,
+ btn_det->c[n]);
+ }
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x07,
+ btn_det->nc);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
+ generic->mbhc_nsa << 4);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x0F,
+ btn_det->n_meas);
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL,
+ generic->mbhc_navg);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x80, 0x80);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
+ btn_det->mbhc_nsc << 3);
+
+ snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc, 0x03,
+ MBHC_MICBIAS2);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xF0, 0xF0);
+
+ gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_GAIN);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x78,
+ gain[idx] << 3);
+
+ pr_debug("%s: leave\n", __func__);
+}
+
+static int wcd9xxx_setup_jack_detect_irq(struct wcd9xxx_mbhc *mbhc)
+{
+ int ret = 0;
+ void *core = mbhc->resmgr->core;
+
+ if (mbhc->mbhc_cfg->gpio) {
+ ret = request_threaded_irq(mbhc->mbhc_cfg->gpio_irq, NULL,
+ wcd9xxx_mech_plug_detect_irq,
+ (IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_DISABLED),
+ "headset detect", mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request gpio irq %d\n", __func__,
+ mbhc->mbhc_cfg->gpio_irq);
+ } else {
+ ret = enable_irq_wake(mbhc->mbhc_cfg->gpio_irq);
+ if (ret)
+ pr_err("%s: Failed to enable wake up irq %d\n",
+ __func__, mbhc->mbhc_cfg->gpio_irq);
+ }
+ } else if (mbhc->mbhc_cfg->insert_detect) {
+ /* Enable HPHL_10K_SW */
+ snd_soc_update_bits(mbhc->codec, WCD9XXX_A_RX_HPH_OCP_CTL,
+ 1 << 1, 1 << 1);
+ ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_JACK_SWITCH,
+ wcd9xxx_mech_plug_detect_irq,
+ "Jack Detect",
+ mbhc);
+ if (ret)
+ pr_err("%s: Failed to request insert detect irq %d\n",
+ __func__, WCD9XXX_IRQ_MBHC_JACK_SWITCH);
+ }
+
+ return ret;
+}
+
+static int wcd9xxx_init_and_calibrate(struct wcd9xxx_mbhc *mbhc)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+
+ /* Enable MCLK during calibration */
+ wcd9xxx_onoff_ext_mclk(mbhc, true);
+ wcd9xxx_mbhc_setup(mbhc);
+ wcd9xxx_mbhc_cal(mbhc);
+ wcd9xxx_mbhc_calc_thres(mbhc);
+ wcd9xxx_onoff_ext_mclk(mbhc, false);
+ wcd9xxx_calibrate_hs_polling(mbhc);
+
+ /* Enable Mic Bias pull down and HPH Switch to GND */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x01);
+ INIT_WORK(&mbhc->correct_plug_swch, wcd9xxx_correct_swch_plug);
+
+ if (!IS_ERR_VALUE(ret)) {
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+ 0x10);
+ wcd9xxx_enable_irq(codec->control_data,
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+ wcd9xxx_enable_irq(codec->control_data,
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+
+ /* Initialize mechanical mbhc */
+ ret = wcd9xxx_setup_jack_detect_irq(mbhc);
+
+ if (!ret && mbhc->mbhc_cfg->gpio) {
+ /* Requested with IRQF_DISABLED */
+ enable_irq(mbhc->mbhc_cfg->gpio_irq);
+
+ /* Bootup time detection */
+ wcd9xxx_swch_irq_handler(mbhc);
+ } else if (!ret && mbhc->mbhc_cfg->insert_detect) {
+ pr_debug("%s: Setting up codec own insert detection\n",
+ __func__);
+ /* Setup for insertion detection */
+ wcd9xxx_insert_detect_setup(mbhc, true);
+ }
+ }
+
+ pr_debug("%s: leave\n", __func__);
+
+ return ret;
+}
+
+static void wcd9xxx_mbhc_fw_read(struct work_struct *work)
+{
+ struct delayed_work *dwork;
+ struct wcd9xxx_mbhc *mbhc;
+ struct snd_soc_codec *codec;
+ const struct firmware *fw;
+ int ret = -1, retry = 0;
+
+ dwork = to_delayed_work(work);
+ mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_firmware_dwork);
+ codec = mbhc->codec;
+
+ while (retry < FW_READ_ATTEMPTS) {
+ retry++;
+ pr_info("%s:Attempt %d to request MBHC firmware\n",
+ __func__, retry);
+ ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
+ codec->dev);
+
+ if (ret != 0) {
+ usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT);
+ } else {
+ pr_info("%s: MBHC Firmware read succesful\n", __func__);
+ break;
+ }
+ }
+
+ if (ret != 0) {
+ pr_err("%s: Cannot load MBHC firmware use default cal\n",
+ __func__);
+ } else if (wcd9xxx_mbhc_fw_validate(fw) == false) {
+ pr_err("%s: Invalid MBHC cal data size use default cal\n",
+ __func__);
+ release_firmware(fw);
+ } else {
+ mbhc->mbhc_cfg->calibration = (void *)fw->data;
+ mbhc->mbhc_fw = fw;
+ }
+
+ (void) wcd9xxx_init_and_calibrate(mbhc);
+}
+
+#ifdef CONFIG_DEBUG_FS
+ssize_t codec_mbhc_debug_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ const int size = 768;
+ char buffer[size];
+ int n = 0;
+ struct wcd9xxx_mbhc *mbhc = file->private_data;
+ const struct mbhc_internal_cal_data *p = &mbhc->mbhc_data;
+ const s16 v_ins_hu_cur = wcd9xxx_get_current_v_ins(mbhc, true);
+ const s16 v_ins_h_cur = wcd9xxx_get_current_v_ins(mbhc, false);
+
+ n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n", p->dce_z,
+ wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_z));
+ n += scnprintf(buffer + n, size - n, "dce_mb = %x(%dmv)\n",
+ p->dce_mb, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_mb));
+ n += scnprintf(buffer + n, size - n, "sta_z = %x(%dmv)\n",
+ p->sta_z, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_z));
+ n += scnprintf(buffer + n, size - n, "sta_mb = %x(%dmv)\n",
+ p->sta_mb, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_mb));
+ n += scnprintf(buffer + n, size - n, "t_dce = %x\n", p->t_dce);
+ n += scnprintf(buffer + n, size - n, "t_sta = %x\n", p->t_sta);
+ n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n",
+ p->micb_mv);
+ n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)%s\n",
+ p->v_ins_hu,
+ wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_ins_hu),
+ p->v_ins_hu == v_ins_hu_cur ? "*" : "");
+ n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)%s\n",
+ p->v_ins_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->v_ins_h),
+ p->v_ins_h == v_ins_h_cur ? "*" : "");
+ n += scnprintf(buffer + n, size - n, "adj_v_ins_hu = %x(%dmv)%s\n",
+ p->adj_v_ins_hu,
+ wcd9xxx_codec_sta_dce_v(mbhc, 0, p->adj_v_ins_hu),
+ p->adj_v_ins_hu == v_ins_hu_cur ? "*" : "");
+ n += scnprintf(buffer + n, size - n, "adj_v_ins_h = %x(%dmv)%s\n",
+ p->adj_v_ins_h,
+ wcd9xxx_codec_sta_dce_v(mbhc, 1, p->adj_v_ins_h),
+ p->adj_v_ins_h == v_ins_h_cur ? "*" : "");
+ n += scnprintf(buffer + n, size - n, "v_b1_hu = %x(%dmv)\n",
+ p->v_b1_hu,
+ wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_b1_hu));
+ n += scnprintf(buffer + n, size - n, "v_b1_h = %x(%dmv)\n",
+ p->v_b1_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->v_b1_h));
+ n += scnprintf(buffer + n, size - n, "v_b1_huc = %x(%dmv)\n",
+ p->v_b1_huc,
+ wcd9xxx_codec_sta_dce_v(mbhc, 1, p->v_b1_huc));
+ n += scnprintf(buffer + n, size - n, "v_brh = %x(%dmv)\n",
+ p->v_brh, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->v_brh));
+ n += scnprintf(buffer + n, size - n, "v_brl = %x(%dmv)\n", p->v_brl,
+ wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_brl));
+ n += scnprintf(buffer + n, size - n, "v_no_mic = %x(%dmv)\n",
+ p->v_no_mic,
+ wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_no_mic));
+ n += scnprintf(buffer + n, size - n, "v_inval_ins_low = %d\n",
+ p->v_inval_ins_low);
+ n += scnprintf(buffer + n, size - n, "v_inval_ins_high = %d\n",
+ p->v_inval_ins_high);
+ n += scnprintf(buffer + n, size - n, "Insert detect insert = %d\n",
+ !wcd9xxx_swch_level_remove(mbhc));
+ buffer[n] = 0;
+
+ return simple_read_from_buffer(buf, count, pos, buffer, n);
+}
+
+static int codec_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t codec_debug_write(struct file *filp,
+ const char __user *ubuf, size_t cnt,
+ loff_t *ppos)
+{
+ char lbuf[32];
+ char *buf;
+ int rc;
+ struct wcd9xxx_mbhc *mbhc = filp->private_data;
+
+ if (cnt > sizeof(lbuf) - 1)
+ return -EINVAL;
+
+ rc = copy_from_user(lbuf, ubuf, cnt);
+ if (rc)
+ return -EFAULT;
+
+ lbuf[cnt] = '\0';
+ buf = (char *)lbuf;
+ mbhc->no_mic_headset_override = (*strsep(&buf, " ") == '0') ?
+ false : true;
+ return rc;
+}
+
+static const struct file_operations mbhc_trrs_debug_ops = {
+ .open = codec_debug_open,
+ .write = codec_debug_write,
+};
+
+static const struct file_operations mbhc_debug_ops = {
+ .open = codec_debug_open,
+ .read = codec_mbhc_debug_read,
+};
+
+static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+ mbhc->debugfs_poke =
+ debugfs_create_file("TRRS", S_IFREG | S_IRUGO, NULL, mbhc,
+ &mbhc_trrs_debug_ops);
+ mbhc->debugfs_mbhc =
+ debugfs_create_file("wcd9xxx_mbhc", S_IFREG | S_IRUGO,
+ NULL, mbhc, &mbhc_debug_ops);
+}
+
+static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+ debugfs_remove(mbhc->debugfs_poke);
+ debugfs_remove(mbhc->debugfs_mbhc);
+}
+#else
+static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+}
+
+static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+}
+#endif
+
+int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
+ struct wcd9xxx_mbhc_config *mbhc_cfg)
+{
+ int rc = 0;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+
+ if (!codec) {
+ pr_err("%s: no codec\n", __func__);
+ return -EINVAL;
+ }
+
+ if (mbhc_cfg->mclk_rate != MCLK_RATE_12288KHZ &&
+ mbhc_cfg->mclk_rate != MCLK_RATE_9600KHZ) {
+ pr_err("Error: unsupported clock rate %d\n",
+ mbhc_cfg->mclk_rate);
+ return -EINVAL;
+ }
+
+ /* Save mbhc config */
+ mbhc->mbhc_cfg = mbhc_cfg;
+
+ /* Get HW specific mbhc registers' address */
+ wcd9xxx_get_mbhc_micbias_regs(mbhc, &mbhc->mbhc_bias_regs);
+
+ /* Put CFILT in fast mode by default */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x40, WCD9XXX_CFILT_FAST_MODE);
+
+ if (!mbhc->mbhc_cfg->read_fw_bin)
+ rc = wcd9xxx_init_and_calibrate(mbhc);
+ else
+ schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
+ usecs_to_jiffies(FW_READ_TIMEOUT));
+
+ pr_debug("%s: leave %d\n", __func__, rc);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_mbhc_start);
+
+static enum wcd9xxx_micbias_num
+wcd9xxx_event_to_micbias(const enum wcd9xxx_notify_event event)
+{
+ enum wcd9xxx_micbias_num ret;
+ switch (event) {
+ case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
+ ret = MBHC_MICBIAS1;
+ case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
+ ret = MBHC_MICBIAS2;
+ case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
+ ret = MBHC_MICBIAS3;
+ case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
+ ret = MBHC_MICBIAS4;
+ default:
+ ret = MBHC_MICBIAS_INVALID;
+ }
+ return ret;
+}
+
+static int wcd9xxx_event_to_cfilt(const enum wcd9xxx_notify_event event)
+{
+ int ret;
+ switch (event) {
+ case WCD9XXX_EVENT_PRE_CFILT_1_OFF:
+ case WCD9XXX_EVENT_POST_CFILT_1_OFF:
+ case WCD9XXX_EVENT_PRE_CFILT_1_ON:
+ case WCD9XXX_EVENT_POST_CFILT_1_ON:
+ ret = WCD9XXX_CFILT1_SEL;
+ break;
+ case WCD9XXX_EVENT_PRE_CFILT_2_OFF:
+ case WCD9XXX_EVENT_POST_CFILT_2_OFF:
+ case WCD9XXX_EVENT_PRE_CFILT_2_ON:
+ case WCD9XXX_EVENT_POST_CFILT_2_ON:
+ ret = WCD9XXX_CFILT2_SEL;
+ break;
+ case WCD9XXX_EVENT_PRE_CFILT_3_OFF:
+ case WCD9XXX_EVENT_POST_CFILT_3_OFF:
+ case WCD9XXX_EVENT_PRE_CFILT_3_ON:
+ case WCD9XXX_EVENT_POST_CFILT_3_ON:
+ ret = WCD9XXX_CFILT3_SEL;
+ break;
+ default:
+ ret = -1;
+ }
+ return ret;
+}
+
+static int wcd9xxx_get_mbhc_cfilt_sel(struct wcd9xxx_mbhc *mbhc)
+{
+ int cfilt;
+ const struct wcd9xxx_pdata *pdata = mbhc->resmgr->pdata;
+
+ switch (mbhc->mbhc_cfg->micbias) {
+ case MBHC_MICBIAS1:
+ cfilt = pdata->micbias.bias1_cfilt_sel;
+ break;
+ case MBHC_MICBIAS2:
+ cfilt = pdata->micbias.bias2_cfilt_sel;
+ break;
+ case MBHC_MICBIAS3:
+ cfilt = pdata->micbias.bias3_cfilt_sel;
+ break;
+ case MBHC_MICBIAS4:
+ cfilt = pdata->micbias.bias4_cfilt_sel;
+ break;
+ default:
+ cfilt = MBHC_MICBIAS_INVALID;
+ break;
+ }
+ return cfilt;
+}
+
+static int wcd9xxx_event_notify(struct notifier_block *self, unsigned long val,
+ void *data)
+{
+ int ret = 0;
+ struct wcd9xxx_mbhc *mbhc = ((struct wcd9xxx_resmgr *)data)->mbhc;
+ struct snd_soc_codec *codec = mbhc->codec;
+ enum wcd9xxx_notify_event event = (enum wcd9xxx_notify_event)val;
+
+ pr_debug("%s: enter event %s(%d)\n", __func__,
+ wcd9xxx_get_event_string(event), event);
+
+ switch (event) {
+ /* MICBIAS usage change */
+ case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
+ case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
+ case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
+ case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
+ if (mbhc->mbhc_cfg->micbias == wcd9xxx_event_to_micbias(event))
+ wcd9xxx_switch_micbias(mbhc, 0);
+ break;
+ case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
+ case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
+ case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
+ case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
+ if (mbhc->mbhc_cfg->micbias ==
+ wcd9xxx_event_to_micbias(event) &&
+ wcd9xxx_mbhc_polling(mbhc)) {
+ /* if polling is on, restart it */
+ wcd9xxx_pause_hs_polling(mbhc);
+ wcd9xxx_start_hs_polling(mbhc);
+ }
+ break;
+ case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
+ case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
+ case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
+ case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
+ if (mbhc->mbhc_cfg->micbias ==
+ wcd9xxx_event_to_micbias(event) &&
+ wcd9xxx_is_hph_pa_on(codec))
+ wcd9xxx_switch_micbias(mbhc, 1);
+ break;
+ /* PA usage change */
+ case WCD9XXX_EVENT_PRE_HPHL_PA_ON:
+ if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg & 0x80)))
+ /* if micbias is enabled, switch to vddio */
+ wcd9xxx_switch_micbias(mbhc, 1);
+ break;
+ case WCD9XXX_EVENT_PRE_HPHR_PA_ON:
+ /* Not used now */
+ break;
+ case WCD9XXX_EVENT_POST_HPHL_PA_OFF:
+ /* if HPH PAs are off, report OCP and switch back to CFILT */
+ clear_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+ clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+ if (mbhc->hph_status & SND_JACK_OC_HPHL)
+ hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
+ wcd9xxx_switch_micbias(mbhc, 0);
+ break;
+ case WCD9XXX_EVENT_POST_HPHR_PA_OFF:
+ /* if HPH PAs are off, report OCP and switch back to CFILT */
+ clear_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+ clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+ if (mbhc->hph_status & SND_JACK_OC_HPHR)
+ hphrocp_off_report(mbhc, SND_JACK_OC_HPHL);
+ wcd9xxx_switch_micbias(mbhc, 0);
+ break;
+ /* Clock usage change */
+ case WCD9XXX_EVENT_PRE_MCLK_ON:
+ break;
+ case WCD9XXX_EVENT_POST_MCLK_ON:
+ /* Change to lower TxAAF frequency */
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
+ 1 << 4);
+ /* Re-calibrate clock rate dependent values */
+ wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->mbhc_cfg->mclk_rate);
+ /* If clock source changes, stop and restart polling */
+ if (wcd9xxx_mbhc_polling(mbhc)) {
+ wcd9xxx_calibrate_hs_polling(mbhc);
+ wcd9xxx_start_hs_polling(mbhc);
+ }
+ break;
+ case WCD9XXX_EVENT_PRE_MCLK_OFF:
+ /* If clock source changes, stop and restart polling */
+ if (wcd9xxx_mbhc_polling(mbhc))
+ wcd9xxx_pause_hs_polling(mbhc);
+ break;
+ case WCD9XXX_EVENT_POST_MCLK_OFF:
+ break;
+ case WCD9XXX_EVENT_PRE_RCO_ON:
+ break;
+ case WCD9XXX_EVENT_POST_RCO_ON:
+ /* Change to higher TxAAF frequency */
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
+ 0 << 4);
+ /* Re-calibrate clock rate dependent values */
+ wcd9xxx_update_mbhc_clk_rate(mbhc, WCD9XXX_RCO_CLK_RATE);
+ /* If clock source changes, stop and restart polling */
+ if (wcd9xxx_mbhc_polling(mbhc)) {
+ wcd9xxx_calibrate_hs_polling(mbhc);
+ wcd9xxx_start_hs_polling(mbhc);
+ }
+ break;
+ case WCD9XXX_EVENT_PRE_RCO_OFF:
+ /* If clock source changes, stop and restart polling */
+ if (wcd9xxx_mbhc_polling(mbhc))
+ wcd9xxx_pause_hs_polling(mbhc);
+ break;
+ case WCD9XXX_EVENT_POST_RCO_OFF:
+ break;
+ /* CFILT usage change */
+ case WCD9XXX_EVENT_PRE_CFILT_1_ON:
+ case WCD9XXX_EVENT_PRE_CFILT_2_ON:
+ case WCD9XXX_EVENT_PRE_CFILT_3_ON:
+ if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
+ wcd9xxx_event_to_cfilt(event))
+ /*
+ * Switch CFILT to slow mode if MBHC CFILT is being
+ * used.
+ */
+ wcd9xxx_codec_switch_cfilt_mode(mbhc, false);
+ break;
+ case WCD9XXX_EVENT_POST_CFILT_1_OFF:
+ case WCD9XXX_EVENT_POST_CFILT_2_OFF:
+ case WCD9XXX_EVENT_POST_CFILT_3_OFF:
+ if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
+ wcd9xxx_event_to_cfilt(event))
+ /*
+ * Switch CFILT to fast mode if MBHC CFILT is not
+ * used anymore.
+ */
+ wcd9xxx_codec_switch_cfilt_mode(mbhc, true);
+ break;
+ /* System resume */
+ case WCD9XXX_EVENT_POST_RESUME:
+ mbhc->mbhc_last_resume = jiffies;
+ break;
+ /* BG mode chage */
+ case WCD9XXX_EVENT_PRE_BG_OFF:
+ case WCD9XXX_EVENT_POST_BG_OFF:
+ case WCD9XXX_EVENT_PRE_BG_AUDIO_ON:
+ case WCD9XXX_EVENT_POST_BG_AUDIO_ON:
+ case WCD9XXX_EVENT_PRE_BG_MBHC_ON:
+ case WCD9XXX_EVENT_POST_BG_MBHC_ON:
+ /* Not used for now */
+ break;
+ default:
+ WARN(1, "Unknown event %d\n", event);
+ ret = -EINVAL;
+ }
+
+ pr_debug("%s: leave\n", __func__);
+
+ return 0;
+}
+
+/*
+ * wcd9xxx_mbhc_init : initialize MBHC internal structures.
+ *
+ * NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used
+ */
+int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
+ struct snd_soc_codec *codec)
+{
+ int ret;
+ void *core;
+
+ pr_debug("%s: enter\n", __func__);
+ memset(&mbhc->mbhc_bias_regs, 0, sizeof(struct mbhc_micbias_regs));
+ memset(&mbhc->mbhc_data, 0, sizeof(struct mbhc_internal_cal_data));
+
+ mbhc->mbhc_data.t_sta_dce = DEFAULT_DCE_STA_WAIT;
+ mbhc->mbhc_data.t_dce = DEFAULT_DCE_WAIT;
+ mbhc->mbhc_data.t_sta = DEFAULT_STA_WAIT;
+ mbhc->mbhc_micbias_switched = false;
+ mbhc->polling_active = false;
+ mbhc->mbhc_state = MBHC_STATE_NONE;
+ mbhc->in_swch_irq_handler = false;
+ mbhc->current_plug = PLUG_TYPE_NONE;
+ mbhc->lpi_enabled = false;
+ mbhc->no_mic_headset_override = false;
+ mbhc->mbhc_last_resume = 0;
+ mbhc->codec = codec;
+ mbhc->resmgr = resmgr;
+ mbhc->resmgr->mbhc = mbhc;
+
+ ret = snd_soc_jack_new(codec, "Headset Jack", WCD9XXX_JACK_MASK,
+ &mbhc->headset_jack);
+ if (ret) {
+ pr_err("%s: Failed to create new jack\n", __func__);
+ return ret;
+ }
+
+ ret = snd_soc_jack_new(codec, "Button Jack", WCD9XXX_JACK_BUTTON_MASK,
+ &mbhc->button_jack);
+ if (ret) {
+ pr_err("Failed to create new jack\n");
+ return ret;
+ }
+
+ mbhc->mbhc_cfg = kzalloc(sizeof(*mbhc->mbhc_cfg), GFP_KERNEL);
+ if (!mbhc->mbhc_cfg)
+ return -ENOMEM;
+
+ INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork, wcd9xxx_mbhc_fw_read);
+ INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd9xxx_btn_lpress_fn);
+ INIT_DELAYED_WORK(&mbhc->mbhc_insert_dwork, wcd9xxx_mbhc_insert_work);
+
+ /* Register event notifier */
+ mbhc->nblock.notifier_call = wcd9xxx_event_notify;
+ ret = wcd9xxx_resmgr_register_notifier(mbhc->resmgr, &mbhc->nblock);
+ if (ret) {
+ pr_err("%s: Failed to register notifier %d\n", __func__, ret);
+ return ret;
+ }
+
+ wcd9xxx_init_debugfs(mbhc);
+
+ core = mbhc->resmgr->core;
+ ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_INSERTION,
+ wcd9xxx_hs_insert_irq,
+ "Headset insert detect", mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ WCD9XXX_IRQ_MBHC_INSERTION);
+ goto err_insert_irq;
+ }
+ wcd9xxx_disable_irq(core, WCD9XXX_IRQ_MBHC_INSERTION);
+
+ ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_REMOVAL,
+ wcd9xxx_hs_remove_irq,
+ "Headset remove detect", mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ WCD9XXX_IRQ_MBHC_REMOVAL);
+ goto err_remove_irq;
+ }
+
+ ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_POTENTIAL,
+ wcd9xxx_dce_handler, "DC Estimation detect",
+ mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ WCD9XXX_IRQ_MBHC_POTENTIAL);
+ goto err_potential_irq;
+ }
+
+ ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_RELEASE,
+ wcd9xxx_release_handler,
+ "Button Release detect", mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ WCD9XXX_IRQ_MBHC_RELEASE);
+ goto err_release_irq;
+ }
+
+ ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+ wcd9xxx_hphl_ocp_irq, "HPH_L OCP detect",
+ mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+ goto err_hphl_ocp_irq;
+ }
+ wcd9xxx_disable_irq(core, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+
+ ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
+ wcd9xxx_hphr_ocp_irq, "HPH_R OCP detect",
+ mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+ goto err_hphr_ocp_irq;
+ }
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+
+ pr_debug("%s: leave ret %d\n", __func__, ret);
+ return ret;
+
+err_hphr_ocp_irq:
+ wcd9xxx_free_irq(core, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, mbhc);
+err_hphl_ocp_irq:
+ wcd9xxx_free_irq(core, WCD9XXX_IRQ_MBHC_RELEASE, mbhc);
+err_release_irq:
+ wcd9xxx_free_irq(core, WCD9XXX_IRQ_MBHC_POTENTIAL, mbhc);
+err_potential_irq:
+ wcd9xxx_free_irq(core, WCD9XXX_IRQ_MBHC_REMOVAL, mbhc);
+err_remove_irq:
+ wcd9xxx_free_irq(core, WCD9XXX_IRQ_MBHC_INSERTION, mbhc);
+err_insert_irq:
+ wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
+
+ pr_debug("%s: leave ret %d\n", __func__, ret);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_mbhc_init);
+
+void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc)
+{
+ void *cdata = mbhc->codec->control_data;
+
+ wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_SLIMBUS, mbhc);
+ wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_RELEASE, mbhc);
+ wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_POTENTIAL, mbhc);
+ wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_REMOVAL, mbhc);
+ wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_INSERTION, mbhc);
+
+ if (mbhc->mbhc_fw)
+ release_firmware(mbhc->mbhc_fw);
+
+ wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
+
+ wcd9xxx_cleanup_debugfs(mbhc);
+
+ kfree(mbhc->mbhc_cfg);
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_mbhc_deinit);
+
+MODULE_DESCRIPTION("wcd9xxx MBHC module");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.h b/sound/soc/codecs/wcd9xxx-mbhc.h
new file mode 100644
index 0000000..fb1dfdc
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-mbhc.h
@@ -0,0 +1,315 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __WCD9XXX_MBHC_H__
+#define __WCD9XXX_MBHC_H__
+
+#include "wcd9xxx-resmgr.h"
+
+#define WCD9XXX_CFILT_FAST_MODE 0x00
+#define WCD9XXX_CFILT_SLOW_MODE 0x40
+
+struct mbhc_micbias_regs {
+ u16 cfilt_val;
+ u16 cfilt_ctl;
+ u16 mbhc_reg;
+ u16 int_rbias;
+ u16 ctl_reg;
+ u8 cfilt_sel;
+};
+
+/* Data used by MBHC */
+struct mbhc_internal_cal_data {
+ u16 dce_z;
+ u16 dce_mb;
+ u16 sta_z;
+ u16 sta_mb;
+ u32 t_sta_dce;
+ u32 t_dce;
+ u32 t_sta;
+ u32 micb_mv;
+ u16 v_ins_hu;
+ u16 v_ins_h;
+ u16 v_b1_hu;
+ u16 v_b1_h;
+ u16 v_b1_huc;
+ u16 v_brh;
+ u16 v_brl;
+ u16 v_no_mic;
+ s16 adj_v_hs_max;
+ u16 adj_v_ins_hu;
+ u16 adj_v_ins_h;
+ s16 v_inval_ins_low;
+ s16 v_inval_ins_high;
+};
+
+enum wcd9xxx_mbhc_plug_type {
+ PLUG_TYPE_INVALID = -1,
+ PLUG_TYPE_NONE,
+ PLUG_TYPE_HEADSET,
+ PLUG_TYPE_HEADPHONE,
+ PLUG_TYPE_HIGH_HPH,
+ PLUG_TYPE_GND_MIC_SWAP,
+};
+
+enum wcd9xxx_micbias_num {
+ MBHC_MICBIAS_INVALID = -1,
+ MBHC_MICBIAS1,
+ MBHC_MICBIAS2,
+ MBHC_MICBIAS3,
+ MBHC_MICBIAS4,
+};
+
+enum wcd9xxx_mbhc_state {
+ MBHC_STATE_NONE = -1,
+ MBHC_STATE_POTENTIAL,
+ MBHC_STATE_POTENTIAL_RECOVERY,
+ MBHC_STATE_RELEASE,
+};
+
+enum wcd9xxx_mbhc_btn_det_mem {
+ MBHC_BTN_DET_V_BTN_LOW,
+ MBHC_BTN_DET_V_BTN_HIGH,
+ MBHC_BTN_DET_N_READY,
+ MBHC_BTN_DET_N_CIC,
+ MBHC_BTN_DET_GAIN
+};
+
+enum wcd9xxx_mbhc_clk_freq {
+ TAIKO_MCLK_12P2MHZ = 0,
+ TAIKO_MCLK_9P6MHZ,
+ TAIKO_NUM_CLK_FREQS,
+};
+
+struct wcd9xxx_mbhc_general_cfg {
+ u8 t_ldoh;
+ u8 t_bg_fast_settle;
+ u8 t_shutdown_plug_rem;
+ u8 mbhc_nsa;
+ u8 mbhc_navg;
+ u8 v_micbias_l;
+ u8 v_micbias;
+ u8 mbhc_reserved;
+ u16 settle_wait;
+ u16 t_micbias_rampup;
+ u16 t_micbias_rampdown;
+ u16 t_supply_bringup;
+} __packed;
+
+struct wcd9xxx_mbhc_plug_detect_cfg {
+ u32 mic_current;
+ u32 hph_current;
+ u16 t_mic_pid;
+ u16 t_ins_complete;
+ u16 t_ins_retry;
+ u16 v_removal_delta;
+ u8 micbias_slow_ramp;
+ u8 reserved0;
+ u8 reserved1;
+ u8 reserved2;
+} __packed;
+
+struct wcd9xxx_mbhc_plug_type_cfg {
+ u8 av_detect;
+ u8 mono_detect;
+ u8 num_ins_tries;
+ u8 reserved0;
+ s16 v_no_mic;
+ s16 v_av_min;
+ s16 v_av_max;
+ s16 v_hs_min;
+ s16 v_hs_max;
+ u16 reserved1;
+} __packed;
+
+struct wcd9xxx_mbhc_btn_detect_cfg {
+ s8 c[8];
+ u8 nc;
+ u8 n_meas;
+ u8 mbhc_nsc;
+ u8 n_btn_meas;
+ u8 n_btn_con;
+ u8 num_btn;
+ u8 reserved0;
+ u8 reserved1;
+ u16 t_poll;
+ u16 t_bounce_wait;
+ u16 t_rel_timeout;
+ s16 v_btn_press_delta_sta;
+ s16 v_btn_press_delta_cic;
+ u16 t_btn0_timeout;
+ s16 _v_btn_low[0]; /* v_btn_low[num_btn] */
+ s16 _v_btn_high[0]; /* v_btn_high[num_btn] */
+ u8 _n_ready[TAIKO_NUM_CLK_FREQS];
+ u8 _n_cic[TAIKO_NUM_CLK_FREQS];
+ u8 _gain[TAIKO_NUM_CLK_FREQS];
+} __packed;
+
+struct wcd9xxx_mbhc_imped_detect_cfg {
+ u8 _hs_imped_detect;
+ u8 _n_rload;
+ u8 _hph_keep_on;
+ u8 _repeat_rload_calc;
+ u16 _t_dac_ramp_time;
+ u16 _rhph_high;
+ u16 _rhph_low;
+ u16 _rload[0]; /* rload[n_rload] */
+ u16 _alpha[0]; /* alpha[n_rload] */
+ u16 _beta[3];
+} __packed;
+
+struct wcd9xxx_mbhc_config {
+ bool read_fw_bin;
+ /*
+ * void* calibration contains:
+ * struct wcd9xxx_mbhc_general_cfg generic;
+ * struct wcd9xxx_mbhc_plug_detect_cfg plug_det;
+ * struct wcd9xxx_mbhc_plug_type_cfg plug_type;
+ * struct wcd9xxx_mbhc_btn_detect_cfg btn_det;
+ * struct wcd9xxx_mbhc_imped_detect_cfg imped_det;
+ * Note: various size depends on btn_det->num_btn
+ */
+ void *calibration;
+ enum wcd9xxx_micbias_num micbias;
+ int (*mclk_cb_fn) (struct snd_soc_codec*, int, bool);
+ unsigned int mclk_rate;
+ unsigned int gpio;
+ unsigned int gpio_irq;
+ int gpio_level_insert;
+ bool insert_detect; /* codec has own MBHC_INSERT_DETECT */
+ bool detect_extn_cable;
+ /* swap_gnd_mic returns true if extern GND/MIC swap switch toggled */
+ bool (*swap_gnd_mic) (struct snd_soc_codec *);
+};
+
+struct wcd9xxx_mbhc {
+ bool polling_active;
+ /* Delayed work to report long button press */
+ struct delayed_work mbhc_btn_dwork;
+ int buttons_pressed;
+ enum wcd9xxx_mbhc_state mbhc_state;
+ struct wcd9xxx_mbhc_config *mbhc_cfg;
+
+ struct mbhc_internal_cal_data mbhc_data;
+
+ struct mbhc_micbias_regs mbhc_bias_regs;
+ bool mbhc_micbias_switched;
+
+ u32 hph_status; /* track headhpone status */
+ u8 hphlocp_cnt; /* headphone left ocp retry */
+ u8 hphrocp_cnt; /* headphone right ocp retry */
+
+ /* Work to perform MBHC Firmware Read */
+ struct delayed_work mbhc_firmware_dwork;
+ const struct firmware *mbhc_fw;
+
+ struct delayed_work mbhc_insert_dwork;
+
+ u8 current_plug;
+ struct work_struct correct_plug_swch;
+ /*
+ * Work to perform polling on microphone voltage
+ * in order to correct plug type once plug type
+ * is detected as headphone
+ */
+ struct work_struct correct_plug_noswch;
+ bool hs_detect_work_stop;
+
+ bool lpi_enabled; /* low power insertion detection */
+ bool in_swch_irq_handler;
+
+ struct wcd9xxx_resmgr *resmgr;
+ struct snd_soc_codec *codec;
+
+ bool no_mic_headset_override;
+
+ /* track PA/DAC state */
+ unsigned long hph_pa_dac_state;
+
+ unsigned long mbhc_last_resume; /* in jiffies */
+
+ bool insert_detect_level_insert;
+
+ struct snd_soc_jack headset_jack;
+ struct snd_soc_jack button_jack;
+
+ struct notifier_block nblock;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs_poke;
+ struct dentry *debugfs_mbhc;
+#endif
+};
+
+#define WCD9XXX_MBHC_CAL_SIZE(buttons, rload) ( \
+ sizeof(enum wcd9xxx_micbias_num) + \
+ sizeof(struct wcd9xxx_mbhc_general_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_plug_detect_cfg) + \
+ ((sizeof(s16) + sizeof(s16)) * buttons) + \
+ sizeof(struct wcd9xxx_mbhc_plug_type_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_btn_detect_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + \
+ ((sizeof(u16) + sizeof(u16)) * rload) \
+ )
+
+#define WCD9XXX_MBHC_CAL_GENERAL_PTR(cali) ( \
+ (struct wcd9xxx_mbhc_general_cfg *) cali)
+#define WCD9XXX_MBHC_CAL_PLUG_DET_PTR(cali) ( \
+ (struct wcd9xxx_mbhc_plug_detect_cfg *) \
+ &(WCD9XXX_MBHC_CAL_GENERAL_PTR(cali)[1]))
+#define WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \
+ (struct wcd9xxx_mbhc_plug_type_cfg *) \
+ &(WCD9XXX_MBHC_CAL_PLUG_DET_PTR(cali)[1]))
+#define WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali) ( \
+ (struct wcd9xxx_mbhc_btn_detect_cfg *) \
+ &(WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(cali)[1]))
+#define WCD9XXX_MBHC_CAL_IMPED_DET_PTR(cali) ( \
+ (struct wcd9xxx_mbhc_imped_detect_cfg *) \
+ (((void *)&WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \
+ (WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \
+ (sizeof(WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \
+ sizeof(WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \
+ )
+
+/* minimum size of calibration data assuming there is only one button and
+ * one rload.
+ */
+#define WCD9XXX_MBHC_CAL_MIN_SIZE ( \
+ sizeof(struct wcd9xxx_mbhc_general_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_plug_detect_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_plug_type_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_btn_detect_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + \
+ (sizeof(u16) * 2) \
+ )
+
+#define WCD9XXX_MBHC_CAL_BTN_SZ(cfg_ptr) ( \
+ sizeof(struct wcd9xxx_mbhc_btn_detect_cfg) + \
+ (cfg_ptr->num_btn * (sizeof(cfg_ptr->_v_btn_low[0]) + \
+ sizeof(cfg_ptr->_v_btn_high[0]))))
+
+#define WCD9XXX_MBHC_CAL_IMPED_MIN_SZ ( \
+ sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + sizeof(u16) * 2)
+
+#define WCD9XXX_MBHC_CAL_IMPED_SZ(cfg_ptr) ( \
+ sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + \
+ (cfg_ptr->_n_rload * \
+ (sizeof(cfg_ptr->_rload[0]) + sizeof(cfg_ptr->_alpha[0]))))
+
+int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
+ struct wcd9xxx_mbhc_config *mbhc_cfg);
+int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
+ struct snd_soc_codec *codec);
+void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc);
+void *wcd9xxx_mbhc_cal_btn_det_mp(
+ const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det,
+ const enum wcd9xxx_mbhc_btn_det_mem mem);
+#endif /* __WCD9XXX_MBHC_H__ */
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.c b/sound/soc/codecs/wcd9xxx-resmgr.c
new file mode 100644
index 0000000..5dfa41c
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-resmgr.c
@@ -0,0 +1,654 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/wcd9xxx/wcd9320_registers.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include "wcd9xxx-resmgr.h"
+
+static char wcd9xxx_event_string[][64] = {
+ "WCD9XXX_EVENT_INVALID",
+
+ "WCD9XXX_EVENT_PRE_RCO_ON",
+ "WCD9XXX_EVENT_POST_RCO_ON",
+ "WCD9XXX_EVENT_PRE_RCO_OFF",
+ "WCD9XXX_EVENT_POST_RCO_OFF",
+
+ "WCD9XXX_EVENT_PRE_MCLK_ON",
+ "WCD9XXX_EVENT_POST_MCLK_ON",
+ "WCD9XXX_EVENT_PRE_MCLK_OFF",
+ "WCD9XXX_EVENT_POST_MCLK_OFF",
+
+ "WCD9XXX_EVENT_PRE_BG_OFF",
+ "WCD9XXX_EVENT_POST_BG_OFF",
+ "WCD9XXX_EVENT_PRE_BG_AUDIO_ON",
+ "WCD9XXX_EVENT_POST_BG_AUDIO_ON",
+ "WCD9XXX_EVENT_PRE_BG_MBHC_ON",
+ "WCD9XXX_EVENT_POST_BG_MBHC_ON",
+
+ "WCD9XXX_EVENT_PRE_MICBIAS_1_OFF",
+ "WCD9XXX_EVENT_POST_MICBIAS_1_OFF",
+ "WCD9XXX_EVENT_PRE_MICBIAS_2_OFF",
+ "WCD9XXX_EVENT_POST_MICBIAS_2_OFF",
+ "WCD9XXX_EVENT_PRE_MICBIAS_3_OFF",
+ "WCD9XXX_EVENT_POST_MICBIAS_3_OFF",
+ "WCD9XXX_EVENT_PRE_MICBIAS_4_OFF",
+ "WCD9XXX_EVENT_POST_MICBIAS_4_OFF",
+ "WCD9XXX_EVENT_PRE_MICBIAS_1_ON",
+ "WCD9XXX_EVENT_POST_MICBIAS_1_ON",
+ "WCD9XXX_EVENT_PRE_MICBIAS_2_ON",
+ "WCD9XXX_EVENT_POST_MICBIAS_2_ON",
+ "WCD9XXX_EVENT_PRE_MICBIAS_3_ON",
+ "WCD9XXX_EVENT_POST_MICBIAS_3_ON",
+ "WCD9XXX_EVENT_PRE_MICBIAS_4_ON",
+ "WCD9XXX_EVENT_POST_MICBIAS_4_ON",
+
+ "WCD9XXX_EVENT_PRE_CFILT_1_OFF",
+ "WCD9XXX_EVENT_POST_CFILT_1_OFF",
+ "WCD9XXX_EVENT_PRE_CFILT_2_OFF",
+ "WCD9XXX_EVENT_POST_CFILT_2_OFF",
+ "WCD9XXX_EVENT_PRE_CFILT_3_OFF",
+ "WCD9XXX_EVENT_POST_CFILT_3_OFF",
+ "WCD9XXX_EVENT_PRE_CFILT_1_ON",
+ "WCD9XXX_EVENT_POST_CFILT_1_ON",
+ "WCD9XXX_EVENT_PRE_CFILT_2_ON",
+ "WCD9XXX_EVENT_POST_CFILT_2_ON",
+ "WCD9XXX_EVENT_PRE_CFILT_3_ON",
+ "WCD9XXX_EVENT_POST_CFILT_3_ON",
+
+ "WCD9XXX_EVENT_PRE_HPHL_PA_ON",
+ "WCD9XXX_EVENT_POST_HPHL_PA_OFF",
+ "WCD9XXX_EVENT_PRE_HPHR_PA_ON",
+ "WCD9XXX_EVENT_POST_HPHR_PA_OFF",
+
+ "WCD9XXX_EVENT_POST_RESUME",
+
+ "WCD9XXX_EVENT_LAST",
+};
+
+static enum wcd9xxx_clock_type wcd9xxx_save_clock(struct wcd9xxx_resmgr
+ *resmgr);
+static void wcd9xxx_restore_clock(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_type type);
+
+const char *wcd9xxx_get_event_string(enum wcd9xxx_notify_event type)
+{
+ return wcd9xxx_event_string[type];
+}
+
+void wcd9xxx_resmgr_notifier_call(struct wcd9xxx_resmgr *resmgr,
+ const enum wcd9xxx_notify_event e)
+{
+ pr_debug("%s: notifier call event %d\n", __func__, e);
+ blocking_notifier_call_chain(&resmgr->notifier, e, resmgr);
+}
+
+static void wcd9xxx_codec_disable_bg(struct wcd9xxx_resmgr *resmgr)
+{
+ /* Notify bg mode change */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_OFF);
+ /* Disable bg */
+ snd_soc_write(resmgr->codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x00);
+ usleep_range(100, 100);
+ /* Notify bg mode change */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_OFF);
+}
+
+static void wcd9xxx_codec_enable_bg_audio(struct wcd9xxx_resmgr *resmgr)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ /* Notify bandgap mode change */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_AUDIO_ON);
+ /* Enable bg */
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x80);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x04, 0x04);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x01, 0x01);
+ usleep_range(1000, 1000);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x00);
+ /* Notify bandgap mode change */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_AUDIO_ON);
+}
+
+static void wcd9xxx_enable_bg_mbhc(struct wcd9xxx_resmgr *resmgr)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ /* Notify bandgap mode change */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_MBHC_ON);
+
+ /*
+ * bandgap mode becomes fast,
+ * mclk should be off or clk buff source souldn't be VBG
+ * Let's turn off mclk always
+ */
+ WARN_ON(snd_soc_read(codec, WCD9XXX_A_CLK_BUFF_EN2) & (1 << 2));
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x2, 0x2);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x80);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x4, 0x4);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x01, 0x01);
+ usleep_range(1000, 1000);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x00);
+
+ /* Notify bandgap mode change */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_MBHC_ON);
+}
+
+static void wcd9xxx_disable_bg(struct wcd9xxx_resmgr *resmgr)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+ snd_soc_write(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x00);
+}
+
+static void wcd9xxx_disable_clock_block(struct wcd9xxx_resmgr *resmgr)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ pr_debug("%s: enter\n", __func__);
+ WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+
+ /* Notify */
+ if (resmgr->clk_type == WCD9XXX_CLK_RCO)
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_RCO_OFF);
+ else
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_PRE_MCLK_OFF);
+ /* Disable clock */
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x00);
+ usleep_range(50, 50);
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x02);
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x00);
+ usleep_range(50, 50);
+ /* Notify */
+ if (resmgr->clk_type == WCD9XXX_CLK_RCO)
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_POST_RCO_OFF);
+ else
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_POST_MCLK_OFF);
+ pr_debug("%s: leave\n", __func__);
+}
+
+/*
+ * wcd9xxx_resmgr_get_bandgap : Vote for bandgap ref
+ * choice : WCD9XXX_BANDGAP_AUDIO_MODE, WCD9XXX_BANDGAP_MBHC_MODE
+ */
+void wcd9xxx_resmgr_get_bandgap(struct wcd9xxx_resmgr *resmgr,
+ const enum wcd9xxx_bandgap_type choice)
+{
+ enum wcd9xxx_clock_type clock_save;
+
+ pr_debug("%s: enter, wants %d\n", __func__, choice);
+
+ WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+ switch (choice) {
+ case WCD9XXX_BANDGAP_AUDIO_MODE:
+ resmgr->bg_audio_users++;
+ if (resmgr->bg_audio_users == 1 && resmgr->bg_mbhc_users) {
+ /*
+ * Current bg is MBHC mode, about to switch to
+ * audio mode.
+ */
+ WARN_ON(resmgr->bandgap_type !=
+ WCD9XXX_BANDGAP_MBHC_MODE);
+
+ /* BG mode can be changed only with clock off */
+ clock_save = wcd9xxx_save_clock(resmgr);
+ /* Swtich BG mode */
+ wcd9xxx_codec_disable_bg(resmgr);
+ wcd9xxx_codec_enable_bg_audio(resmgr);
+ /* restore clock */
+ wcd9xxx_restore_clock(resmgr, clock_save);
+ } else if (resmgr->bg_audio_users == 1) {
+ /* currently off, just enable it */
+ WARN_ON(resmgr->bandgap_type != WCD9XXX_BANDGAP_OFF);
+ wcd9xxx_codec_enable_bg_audio(resmgr);
+ }
+ resmgr->bandgap_type = WCD9XXX_BANDGAP_AUDIO_MODE;
+ break;
+ case WCD9XXX_BANDGAP_MBHC_MODE:
+ resmgr->bg_mbhc_users++;
+ if (resmgr->bandgap_type == WCD9XXX_BANDGAP_MBHC_MODE ||
+ resmgr->bandgap_type == WCD9XXX_BANDGAP_AUDIO_MODE)
+ /* do nothing */
+ break;
+
+ /* bg mode can be changed only with clock off */
+ clock_save = wcd9xxx_save_clock(resmgr);
+ /* enable bg with MBHC mode */
+ wcd9xxx_enable_bg_mbhc(resmgr);
+ /* restore clock */
+ wcd9xxx_restore_clock(resmgr, clock_save);
+ /* save current mode */
+ resmgr->bandgap_type = WCD9XXX_BANDGAP_MBHC_MODE;
+ break;
+ default:
+ pr_err("%s: Error, Invalid bandgap settings\n", __func__);
+ break;
+ }
+
+ pr_debug("%s: bg users audio %d, mbhc %d\n", __func__,
+ resmgr->bg_audio_users, resmgr->bg_mbhc_users);
+}
+
+/*
+ * wcd9xxx_resmgr_put_bandgap : Unvote bandgap ref that has been voted
+ * choice : WCD9XXX_BANDGAP_AUDIO_MODE, WCD9XXX_BANDGAP_MBHC_MODE
+ */
+void wcd9xxx_resmgr_put_bandgap(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_bandgap_type choice)
+{
+ enum wcd9xxx_clock_type clock_save;
+
+ pr_debug("%s: enter choice %d\n", __func__, choice);
+
+ WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+ switch (choice) {
+ case WCD9XXX_BANDGAP_AUDIO_MODE:
+ if (--resmgr->bg_audio_users == 0) {
+ if (resmgr->bg_mbhc_users) {
+ /* bg mode can be changed only with clock off */
+ clock_save = wcd9xxx_save_clock(resmgr);
+ /* switch to MBHC mode */
+ wcd9xxx_enable_bg_mbhc(resmgr);
+ /* restore clock */
+ wcd9xxx_restore_clock(resmgr, clock_save);
+ resmgr->bandgap_type =
+ WCD9XXX_BANDGAP_MBHC_MODE;
+ } else {
+ /* turn off */
+ wcd9xxx_disable_bg(resmgr);
+ resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
+ }
+ }
+ break;
+ case WCD9XXX_BANDGAP_MBHC_MODE:
+ WARN(resmgr->bandgap_type == WCD9XXX_BANDGAP_OFF,
+ "Unexpected bandgap type %d\n", resmgr->bandgap_type);
+ if (--resmgr->bg_mbhc_users == 0 &&
+ resmgr->bandgap_type == WCD9XXX_BANDGAP_MBHC_MODE) {
+ wcd9xxx_disable_bg(resmgr);
+ resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
+ }
+ break;
+ default:
+ pr_err("%s: Error, Invalid bandgap settings\n", __func__);
+ break;
+ }
+
+ pr_debug("%s: bg users audio %d, mbhc %d\n", __func__,
+ resmgr->bg_audio_users, resmgr->bg_mbhc_users);
+}
+
+void wcd9xxx_resmgr_enable_rx_bias(struct wcd9xxx_resmgr *resmgr, u32 enable)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ if (enable) {
+ resmgr->rx_bias_count++;
+ if (resmgr->rx_bias_count == 1)
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_COM_BIAS,
+ 0x80, 0x80);
+ } else {
+ resmgr->rx_bias_count--;
+ if (!resmgr->rx_bias_count)
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_COM_BIAS,
+ 0x80, 0x00);
+ }
+}
+
+int wcd9xxx_resmgr_enable_config_mode(struct snd_soc_codec *codec, int enable)
+{
+ pr_debug("%s: enable = %d\n", __func__, enable);
+ if (enable) {
+ snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x10, 0);
+ /* bandgap mode to fast */
+ snd_soc_write(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x17);
+ usleep_range(5, 5);
+ snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x80, 0x80);
+ snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_TEST, 0x80, 0x80);
+ usleep_range(10, 10);
+ snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_TEST, 0x80, 0);
+ usleep_range(10000, 10000);
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x08, 0x08);
+ } else {
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x1, 0);
+ snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x80, 0);
+ /* clk source to ext clk and clk buff ref to VBG */
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x0C, 0x04);
+ }
+
+ return 0;
+}
+
+static void wcd9xxx_enable_clock_block(struct wcd9xxx_resmgr *resmgr,
+ int config_mode)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ pr_debug("%s: config_mode = %d\n", __func__, config_mode);
+ /* transit to RCO requires mclk off */
+ WARN_ON(snd_soc_read(codec, WCD9XXX_A_CLK_BUFF_EN2) & (1 << 2));
+ if (config_mode) {
+ /* Notify */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_RCO_ON);
+ /* enable RCO and switch to it */
+ wcd9xxx_resmgr_enable_config_mode(codec, 1);
+ snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02);
+ usleep_range(1000, 1000);
+ } else {
+ /* Notify */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_MCLK_ON);
+ /* switch to MCLK */
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x08, 0x00);
+ /* if RCO is enabled, switch from it */
+ if (snd_soc_read(codec, WCD9XXX_A_RC_OSC_FREQ) & 0x80) {
+ snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02);
+ wcd9xxx_resmgr_enable_config_mode(codec, 0);
+ }
+ }
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x01, 0x01);
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x00);
+
+ /* on MCLK */
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x04);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL, 0x01, 0x01);
+ usleep_range(50, 50);
+
+ /* Notify */
+ if (config_mode)
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_POST_RCO_ON);
+ else
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_POST_MCLK_ON);
+}
+
+/*
+ * disable clock and return previous clock state
+ */
+static enum wcd9xxx_clock_type wcd9xxx_save_clock(struct wcd9xxx_resmgr *resmgr)
+{
+ WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+ if (resmgr->clk_type != WCD9XXX_CLK_OFF)
+ wcd9xxx_disable_clock_block(resmgr);
+ return resmgr->clk_type != WCD9XXX_CLK_OFF;
+}
+
+static void wcd9xxx_restore_clock(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_type type)
+{
+ if (type != WCD9XXX_CLK_OFF)
+ wcd9xxx_enable_clock_block(resmgr, type == WCD9XXX_CLK_RCO);
+}
+
+void wcd9xxx_resmgr_get_clk_block(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_type type)
+{
+ pr_debug("%s: current %d, requested %d, rco_users %d, mclk_users %d\n",
+ __func__, resmgr->clk_type, type,
+ resmgr->clk_rco_users, resmgr->clk_mclk_users);
+ WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+ switch (type) {
+ case WCD9XXX_CLK_RCO:
+ if (++resmgr->clk_rco_users == 1 &&
+ resmgr->clk_type == WCD9XXX_CLK_OFF) {
+ /* enable RCO and switch to it */
+ wcd9xxx_enable_clock_block(resmgr, 1);
+ resmgr->clk_type = WCD9XXX_CLK_RCO;
+ }
+ break;
+ case WCD9XXX_CLK_MCLK:
+ if (++resmgr->clk_mclk_users == 1 &&
+ resmgr->clk_type == WCD9XXX_CLK_OFF) {
+ /* switch to MCLK */
+ wcd9xxx_enable_clock_block(resmgr, 0);
+ resmgr->clk_type = WCD9XXX_CLK_MCLK;
+ } else if (resmgr->clk_mclk_users == 1 &&
+ resmgr->clk_type == WCD9XXX_CLK_RCO) {
+ /* if RCO is enabled, switch from it */
+ WARN_ON(!(snd_soc_read(resmgr->codec,
+ WCD9XXX_A_RC_OSC_FREQ) & 0x80));
+ /* disable clock block */
+ wcd9xxx_disable_clock_block(resmgr);
+ /* switch to RCO */
+ wcd9xxx_enable_clock_block(resmgr, 0);
+ resmgr->clk_type = WCD9XXX_CLK_MCLK;
+ }
+ break;
+ default:
+ pr_err("%s: Error, Invalid clock get request %d\n", __func__,
+ type);
+ break;
+ }
+ pr_debug("%s: leave\n", __func__);
+}
+
+void wcd9xxx_resmgr_put_clk_block(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_type type)
+{
+ pr_debug("%s: current %d, put %d\n", __func__, resmgr->clk_type, type);
+
+ WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+ switch (type) {
+ case WCD9XXX_CLK_RCO:
+ if (--resmgr->clk_rco_users == 0 &&
+ resmgr->clk_type == WCD9XXX_CLK_RCO) {
+ wcd9xxx_disable_clock_block(resmgr);
+ resmgr->clk_type = WCD9XXX_CLK_OFF;
+ }
+ break;
+ case WCD9XXX_CLK_MCLK:
+ if (--resmgr->clk_mclk_users == 0 &&
+ resmgr->clk_rco_users == 0) {
+ wcd9xxx_disable_clock_block(resmgr);
+ resmgr->clk_type = WCD9XXX_CLK_OFF;
+ } else if (resmgr->clk_mclk_users == 0 &&
+ resmgr->clk_rco_users) {
+ /* disable clock */
+ wcd9xxx_disable_clock_block(resmgr);
+ /* switch to RCO */
+ wcd9xxx_enable_clock_block(resmgr, 1);
+ resmgr->clk_type = WCD9XXX_CLK_RCO;
+ }
+ break;
+ default:
+ pr_err("%s: Error, Invalid clock get request %d\n", __func__,
+ type);
+ break;
+ }
+ WARN_ON(resmgr->clk_rco_users < 0);
+ WARN_ON(resmgr->clk_mclk_users < 0);
+
+ pr_debug("%s: new rco_users %d, mclk_users %d\n", __func__,
+ resmgr->clk_rco_users, resmgr->clk_mclk_users);
+}
+
+static void wcd9xxx_resmgr_update_cfilt_usage(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_cfilt_sel cfilt_sel,
+ bool inc)
+{
+ u16 micb_cfilt_reg;
+ enum wcd9xxx_notify_event e_pre_on, e_post_off;
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ switch (cfilt_sel) {
+ case WCD9XXX_CFILT1_SEL:
+ micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_1_CTL;
+ e_pre_on = WCD9XXX_EVENT_PRE_CFILT_1_ON;
+ e_post_off = WCD9XXX_EVENT_POST_CFILT_1_OFF;
+ break;
+ case WCD9XXX_CFILT2_SEL:
+ micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_2_CTL;
+ e_pre_on = WCD9XXX_EVENT_PRE_CFILT_2_ON;
+ e_post_off = WCD9XXX_EVENT_POST_CFILT_2_OFF;
+ break;
+ case WCD9XXX_CFILT3_SEL:
+ micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_3_CTL;
+ e_pre_on = WCD9XXX_EVENT_PRE_CFILT_3_ON;
+ e_post_off = WCD9XXX_EVENT_POST_CFILT_3_OFF;
+ break;
+ default:
+ WARN(1, "Invalid CFILT selection %d\n", cfilt_sel);
+ return; /* should not happen */
+ }
+
+ if (inc) {
+ if ((resmgr->cfilt_users[cfilt_sel]++) == 0) {
+ /* Notify */
+ wcd9xxx_resmgr_notifier_call(resmgr, e_pre_on);
+ /* Enable CFILT */
+ snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0x80);
+ }
+ } else {
+ /*
+ * Check if count not zero, decrease
+ * then check if zero, go ahead disable cfilter
+ */
+ WARN(resmgr->cfilt_users[cfilt_sel] == 0,
+ "Invalid CFILT use count 0\n");
+ if ((--resmgr->cfilt_users[cfilt_sel]) == 0) {
+ /* Disable CFILT */
+ snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0);
+ /* Notify MBHC so MBHC can switch CFILT to fast mode */
+ wcd9xxx_resmgr_notifier_call(resmgr, e_post_off);
+ }
+ }
+}
+
+void wcd9xxx_resmgr_cfilt_get(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_cfilt_sel cfilt_sel)
+{
+ return wcd9xxx_resmgr_update_cfilt_usage(resmgr, cfilt_sel, true);
+}
+
+void wcd9xxx_resmgr_cfilt_put(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_cfilt_sel cfilt_sel)
+{
+ return wcd9xxx_resmgr_update_cfilt_usage(resmgr, cfilt_sel, false);
+}
+
+int wcd9xxx_resmgr_get_k_val(struct wcd9xxx_resmgr *resmgr,
+ unsigned int cfilt_mv)
+{
+ int rc = -EINVAL;
+ unsigned int ldoh_v = resmgr->pdata->micbias.ldoh_v;
+ unsigned min_mv, max_mv;
+
+ switch (ldoh_v) {
+ case WCD9XXX_LDOH_1P95_V:
+ min_mv = 160;
+ max_mv = 1800;
+ break;
+ case WCD9XXX_LDOH_2P35_V:
+ min_mv = 200;
+ max_mv = 2200;
+ break;
+ case WCD9XXX_LDOH_2P75_V:
+ min_mv = 240;
+ max_mv = 2600;
+ break;
+ case WCD9XXX_LDOH_3P0_V:
+ min_mv = 260;
+ max_mv = 2875;
+ break;
+ default:
+ goto done;
+ }
+
+ if (cfilt_mv < min_mv || cfilt_mv > max_mv)
+ goto done;
+
+ for (rc = 4; rc <= 44; rc++) {
+ min_mv = max_mv * (rc) / 44;
+ if (min_mv >= cfilt_mv) {
+ rc -= 4;
+ break;
+ }
+ }
+done:
+ return rc;
+}
+
+int wcd9xxx_resmgr_register_notifier(struct wcd9xxx_resmgr *resmgr,
+ struct notifier_block *nblock)
+{
+ return blocking_notifier_chain_register(&resmgr->notifier, nblock);
+}
+
+int wcd9xxx_resmgr_unregister_notifier(struct wcd9xxx_resmgr *resmgr,
+ struct notifier_block *nblock)
+{
+ return blocking_notifier_chain_unregister(&resmgr->notifier, nblock);
+}
+
+int wcd9xxx_resmgr_init(struct wcd9xxx_resmgr *resmgr,
+ struct snd_soc_codec *codec,
+ struct wcd9xxx *wcd9xxx,
+ struct wcd9xxx_pdata *pdata,
+ struct wcd9xxx_reg_address *reg_addr)
+{
+ WARN(ARRAY_SIZE(wcd9xxx_event_string) != WCD9XXX_EVENT_LAST + 1,
+ "Event string table isn't up to date!, %d != %d\n",
+ ARRAY_SIZE(wcd9xxx_event_string), WCD9XXX_EVENT_LAST + 1);
+
+ resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
+ resmgr->codec = codec;
+ /* This gives access of core handle to lock/unlock suspend */
+ resmgr->core = wcd9xxx;
+ resmgr->pdata = pdata;
+ resmgr->reg_addr = reg_addr;
+
+ BLOCKING_INIT_NOTIFIER_HEAD(&resmgr->notifier);
+
+ mutex_init(&resmgr->codec_resource_lock);
+
+ return 0;
+}
+
+void wcd9xxx_resmgr_deinit(struct wcd9xxx_resmgr *resmgr)
+{
+ mutex_destroy(&resmgr->codec_resource_lock);
+}
+
+void wcd9xxx_resmgr_bcl_lock(struct wcd9xxx_resmgr *resmgr)
+{
+ mutex_lock(&resmgr->codec_resource_lock);
+}
+
+void wcd9xxx_resmgr_bcl_unlock(struct wcd9xxx_resmgr *resmgr)
+{
+ mutex_unlock(&resmgr->codec_resource_lock);
+}
+
+MODULE_DESCRIPTION("wcd9xxx resmgr module");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.h b/sound/soc/codecs/wcd9xxx-resmgr.h
new file mode 100644
index 0000000..2d04102
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-resmgr.h
@@ -0,0 +1,191 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __WCD9XXX_COMMON_H__
+#define __WCD9XXX_COMMON_H__
+
+#include <linux/notifier.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+
+enum wcd9xxx_bandgap_type {
+ WCD9XXX_BANDGAP_OFF,
+ WCD9XXX_BANDGAP_AUDIO_MODE,
+ WCD9XXX_BANDGAP_MBHC_MODE,
+};
+
+enum wcd9xxx_clock_type {
+ WCD9XXX_CLK_OFF,
+ WCD9XXX_CLK_RCO,
+ WCD9XXX_CLK_MCLK,
+};
+
+enum wcd9xxx_cfilt_sel {
+ WCD9XXX_CFILT1_SEL,
+ WCD9XXX_CFILT2_SEL,
+ WCD9XXX_CFILT3_SEL,
+ WCD9XXX_NUM_OF_CFILT,
+};
+
+struct wcd9xxx_reg_address {
+ u16 micb_4_ctl;
+ u16 micb_4_int_rbias;
+ u16 micb_4_mbhc;
+};
+
+enum wcd9xxx_notify_event {
+ WCD9XXX_EVENT_INVALID,
+
+ WCD9XXX_EVENT_PRE_RCO_ON,
+ WCD9XXX_EVENT_POST_RCO_ON,
+ WCD9XXX_EVENT_PRE_RCO_OFF,
+ WCD9XXX_EVENT_POST_RCO_OFF,
+
+ WCD9XXX_EVENT_PRE_MCLK_ON,
+ WCD9XXX_EVENT_POST_MCLK_ON,
+ WCD9XXX_EVENT_PRE_MCLK_OFF,
+ WCD9XXX_EVENT_POST_MCLK_OFF,
+
+ WCD9XXX_EVENT_PRE_BG_OFF,
+ WCD9XXX_EVENT_POST_BG_OFF,
+ WCD9XXX_EVENT_PRE_BG_AUDIO_ON,
+ WCD9XXX_EVENT_POST_BG_AUDIO_ON,
+ WCD9XXX_EVENT_PRE_BG_MBHC_ON,
+ WCD9XXX_EVENT_POST_BG_MBHC_ON,
+
+ WCD9XXX_EVENT_PRE_MICBIAS_1_OFF,
+ WCD9XXX_EVENT_POST_MICBIAS_1_OFF,
+ WCD9XXX_EVENT_PRE_MICBIAS_2_OFF,
+ WCD9XXX_EVENT_POST_MICBIAS_2_OFF,
+ WCD9XXX_EVENT_PRE_MICBIAS_3_OFF,
+ WCD9XXX_EVENT_POST_MICBIAS_3_OFF,
+ WCD9XXX_EVENT_PRE_MICBIAS_4_OFF,
+ WCD9XXX_EVENT_POST_MICBIAS_4_OFF,
+ WCD9XXX_EVENT_PRE_MICBIAS_1_ON,
+ WCD9XXX_EVENT_POST_MICBIAS_1_ON,
+ WCD9XXX_EVENT_PRE_MICBIAS_2_ON,
+ WCD9XXX_EVENT_POST_MICBIAS_2_ON,
+ WCD9XXX_EVENT_PRE_MICBIAS_3_ON,
+ WCD9XXX_EVENT_POST_MICBIAS_3_ON,
+ WCD9XXX_EVENT_PRE_MICBIAS_4_ON,
+ WCD9XXX_EVENT_POST_MICBIAS_4_ON,
+
+ WCD9XXX_EVENT_PRE_CFILT_1_OFF,
+ WCD9XXX_EVENT_POST_CFILT_1_OFF,
+ WCD9XXX_EVENT_PRE_CFILT_2_OFF,
+ WCD9XXX_EVENT_POST_CFILT_2_OFF,
+ WCD9XXX_EVENT_PRE_CFILT_3_OFF,
+ WCD9XXX_EVENT_POST_CFILT_3_OFF,
+ WCD9XXX_EVENT_PRE_CFILT_1_ON,
+ WCD9XXX_EVENT_POST_CFILT_1_ON,
+ WCD9XXX_EVENT_PRE_CFILT_2_ON,
+ WCD9XXX_EVENT_POST_CFILT_2_ON,
+ WCD9XXX_EVENT_PRE_CFILT_3_ON,
+ WCD9XXX_EVENT_POST_CFILT_3_ON,
+
+ WCD9XXX_EVENT_PRE_HPHL_PA_ON,
+ WCD9XXX_EVENT_POST_HPHL_PA_OFF,
+ WCD9XXX_EVENT_PRE_HPHR_PA_ON,
+ WCD9XXX_EVENT_POST_HPHR_PA_OFF,
+
+ WCD9XXX_EVENT_POST_RESUME,
+
+ WCD9XXX_EVENT_LAST,
+};
+
+struct wcd9xxx_resmgr {
+ struct snd_soc_codec *codec;
+ struct wcd9xxx *core;
+
+ u32 rx_bias_count;
+
+ enum wcd9xxx_bandgap_type bandgap_type;
+ u16 bg_audio_users;
+ u16 bg_mbhc_users;
+
+ enum wcd9xxx_clock_type clk_type;
+ u16 clk_rco_users;
+ u16 clk_mclk_users;
+
+ /* cfilt users per cfilts */
+ u16 cfilt_users[WCD9XXX_NUM_OF_CFILT];
+
+ struct wcd9xxx_reg_address *reg_addr;
+
+ struct wcd9xxx_pdata *pdata;
+
+ struct blocking_notifier_head notifier;
+ /* Notifier needs mbhc pointer with resmgr */
+ struct wcd9xxx_mbhc *mbhc;
+
+ /*
+ * Currently, only used for mbhc purpose, to protect
+ * concurrent execution of mbhc threaded irq handlers and
+ * kill race between DAPM and MBHC. But can serve as a
+ * general lock to protect codec resource
+ */
+ struct mutex codec_resource_lock;
+};
+
+int wcd9xxx_resmgr_init(struct wcd9xxx_resmgr *resmgr,
+ struct snd_soc_codec *codec,
+ struct wcd9xxx *wcd9xxx,
+ struct wcd9xxx_pdata *pdata,
+ struct wcd9xxx_reg_address *reg_addr);
+void wcd9xxx_resmgr_deinit(struct wcd9xxx_resmgr *resmgr);
+
+int wcd9xxx_resmgr_enable_config_mode(struct snd_soc_codec *codec, int enable);
+
+void wcd9xxx_resmgr_enable_rx_bias(struct wcd9xxx_resmgr *resmgr, u32 enable);
+void wcd9xxx_resmgr_get_clk_block(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_type type);
+void wcd9xxx_resmgr_put_clk_block(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_type type);
+void wcd9xxx_resmgr_get_bandgap(struct wcd9xxx_resmgr *resmgr,
+ const enum wcd9xxx_bandgap_type choice);
+void wcd9xxx_resmgr_put_bandgap(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_bandgap_type choice);
+void wcd9xxx_resmgr_cfilt_get(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_cfilt_sel cfilt_sel);
+void wcd9xxx_resmgr_cfilt_put(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_cfilt_sel cfilt_sel);
+
+void wcd9xxx_resmgr_bcl_lock(struct wcd9xxx_resmgr *resmgr);
+#define WCD9XXX_BCL_LOCK(resmgr) \
+{ \
+ pr_debug("%s: Acquiring BCL\n", __func__); \
+ wcd9xxx_resmgr_bcl_lock(resmgr); \
+ pr_debug("%s: Acquiring BCL done\n", __func__); \
+}
+
+void wcd9xxx_resmgr_bcl_unlock(struct wcd9xxx_resmgr *resmgr);
+#define WCD9XXX_BCL_UNLOCK(resmgr) \
+{ \
+ pr_debug("%s: Release BCL\n", __func__); \
+ wcd9xxx_resmgr_bcl_unlock(resmgr); \
+}
+
+#define WCD9XXX_BCL_ASSERT_LOCKED(resmgr) \
+{ \
+ WARN_ONCE(!mutex_is_locked(&resmgr->codec_resource_lock), \
+ "%s: BCL should have acquired\n", __func__); \
+}
+
+const char *wcd9xxx_get_event_string(enum wcd9xxx_notify_event type);
+int wcd9xxx_resmgr_get_k_val(struct wcd9xxx_resmgr *resmgr,
+ unsigned int cfilt_mv);
+int wcd9xxx_resmgr_register_notifier(struct wcd9xxx_resmgr *resmgr,
+ struct notifier_block *nblock);
+int wcd9xxx_resmgr_unregister_notifier(struct wcd9xxx_resmgr *resmgr,
+ struct notifier_block *nblock);
+void wcd9xxx_resmgr_notifier_call(struct wcd9xxx_resmgr *resmgr,
+ const enum wcd9xxx_notify_event e);
+
+#endif /* __WCD9XXX_COMMON_H__ */
diff --git a/sound/soc/msm/apq8064-i2s.c b/sound/soc/msm/apq8064-i2s.c
index e309370..795b421 100644
--- a/sound/soc/msm/apq8064-i2s.c
+++ b/sound/soc/msm/apq8064-i2s.c
@@ -2690,8 +2690,7 @@
static void __exit msm_audio_exit(void)
{
- if (!(cpu_is_apq8064() || cpu_is_apq8064ab()) ||
- (socinfo_get_id() == 130)) {
+ if (!soc_class_is_apq8064() || socinfo_get_id() == 130) {
pr_err("%s: Not the right machine type\n", __func__);
return ;
}
diff --git a/sound/soc/msm/apq8064.c b/sound/soc/msm/apq8064.c
index b921df1..4fe002b 100644
--- a/sound/soc/msm/apq8064.c
+++ b/sound/soc/msm/apq8064.c
@@ -859,7 +859,6 @@
unsigned int rx_ch_cnt = 0, tx_ch_cnt = 0;
unsigned int num_tx_ch = 0;
-
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
pr_debug("%s: rx_0_ch=%d\n", __func__, msm_slim_0_rx_ch);
@@ -877,25 +876,7 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
- msm_slim_0_rx_ch, rx_ch);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
} else {
-
- if (codec_dai->id == 2)
- num_tx_ch = msm_slim_0_tx_ch;
- else if (codec_dai->id == 5) {
- /* DAI 5 is used for external EC reference from codec.
- * Since Rx is fed as reference for EC, the config of
- * this DAI is based on that of the Rx path.
- */
- num_tx_ch = msm_slim_0_rx_ch;
- }
-
pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__,
codec_dai->name, codec_dai->id, num_tx_ch);
@@ -905,6 +886,19 @@
pr_err("%s: failed to get codec chan map\n", __func__);
goto end;
}
+ /* For tabla_tx1 case */
+ if (codec_dai->id == 1)
+ num_tx_ch = msm_slim_0_tx_ch;
+ /* For tabla_tx3 case */
+ else if (codec_dai->id == 4) {
+ /* DAI 5 is used for external EC reference from codec.
+ * Since Rx is fed as reference for EC, the config of
+ * this DAI is based on that of the Rx path.
+ */
+ num_tx_ch = msm_slim_0_rx_ch;
+ } else {
+ num_tx_ch = tx_ch_cnt;
+ }
ret = snd_soc_dai_set_channel_map(cpu_dai,
num_tx_ch, tx_ch, 0 , 0);
@@ -912,15 +906,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai,
- num_tx_ch, tx_ch, 0, 0);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
-
-
}
end:
return ret;
@@ -965,13 +950,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
- num_rx_ch, rx_ch);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
} else {
num_tx_ch = params_channels(params);
@@ -992,13 +970,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai,
- num_tx_ch, tx_ch, 0, 0);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
}
end:
return ret;
@@ -1128,14 +1099,14 @@
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ unsigned int rx_ch[TABLA_RX_MAX] = {138, 139, 140, 141, 142, 143, 144};
+ unsigned int tx_ch[TABLA_TX_MAX] = {128, 129, 130, 131, 132, 133, 134,
+ 135, 136, 137};
+
pr_debug("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
- /*if (machine_is_msm_liquid()) {
- top_spk_pamp_gpio = (PM8921_GPIO_PM_TO_SYS(19));
- bottom_spk_pamp_gpio = (PM8921_GPIO_PM_TO_SYS(18));
- }*/
-
snd_soc_dapm_new_controls(dapm, apq8064_dapm_widgets,
ARRAY_SIZE(apq8064_dapm_widgets));
@@ -1221,6 +1192,8 @@
mbhc_cfg.read_fw_bin = apq8064_hs_detect_use_firmware;
err = tabla_hs_detect(codec, &mbhc_cfg);
+ snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+ tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
return err;
}
@@ -1353,6 +1326,8 @@
pr_debug("%s channels->min %u channels->max %u ()\n", __func__,
channels->min, channels->max);
+ if (channels->max < 2)
+ channels->min = channels->max = 2;
rate->min = rate->max = 48000;
return 0;
@@ -2101,7 +2076,7 @@
{
int ret;
u32 version = socinfo_get_platform_version();
- if (!(cpu_is_apq8064() || cpu_is_apq8064ab()) ||
+ if (!soc_class_is_apq8064() ||
(socinfo_get_id() == 130) ||
(machine_is_apq8064_mtp() &&
(SOCINFO_VERSION_MINOR(version) == 1))) {
@@ -2142,8 +2117,7 @@
static void __exit msm_audio_exit(void)
{
- if (!(cpu_is_apq8064() || cpu_is_apq8064ab()) ||
- (socinfo_get_id() == 130)) {
+ if (!soc_class_is_apq8064() || socinfo_get_id() == 130) {
pr_err("%s: Not the right machine type\n", __func__);
return ;
}
diff --git a/sound/soc/msm/mdm9615.c b/sound/soc/msm/mdm9615.c
index 1000a8b..5a47efe 100644
--- a/sound/soc/msm/mdm9615.c
+++ b/sound/soc/msm/mdm9615.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -89,6 +89,20 @@
.drv = GPIOMUX_DRV_8MA,
.pull = GPIOMUX_PULL_NONE,
};
+static struct gpiomux_setting audio_sec_i2s[] = {
+ /* Suspend state */
+ {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ },
+ /* Active state */
+ {
+ .func = GPIOMUX_FUNC_2,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+ }
+};
static struct gpiomux_setting cdc_i2s_dout = {
.func = GPIOMUX_FUNC_1,
@@ -142,6 +156,42 @@
},
};
+static struct msm_gpiomux_config msm9615_audio_sec_i2s_codec_configs[] = {
+ {
+ .gpio = GPIO_SPKR_I2S_MCLK,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &cdc_i2s_mclk,
+ },
+ },
+ {
+ .gpio = GPIO_SEC_I2S_SCK,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &audio_sec_i2s[0],
+ [GPIOMUX_ACTIVE] = &audio_sec_i2s[1],
+ },
+ },
+ {
+ .gpio = GPIO_SEC_I2S_DOUT,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &audio_sec_i2s[0],
+ [GPIOMUX_ACTIVE] = &audio_sec_i2s[1],
+ },
+ },
+ {
+ .gpio = GPIO_SEC_I2S_WS,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &audio_sec_i2s[0],
+ [GPIOMUX_ACTIVE] = &audio_sec_i2s[1],
+ },
+ },
+ {
+ .gpio = GPIO_SEC_I2S_DIN,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &audio_sec_i2s[0],
+ [GPIOMUX_ACTIVE] = &audio_sec_i2s[1],
+ },
+ },
+};
/* Physical address for LPA CSR
* LPA SIF mux registers. These are
* ioremap( ) for Virtual address.
@@ -274,6 +324,9 @@
static struct platform_device *mdm9615_snd_device_slim;
static struct platform_device *mdm9615_snd_device_i2s;
+static u32 sif_reg_value = 0x0000;
+static u32 spare_reg_value = 0x0000;
+
static bool hs_detect_use_gpio;
module_param(hs_detect_use_gpio, bool, 0444);
MODULE_PARM_DESC(hs_detect_use_gpio, "Use GPIO for headset detection");
@@ -862,13 +915,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
- mdm9615_slim_0_rx_ch, rx_ch);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
} else {
ret = snd_soc_dai_get_channel_map(codec_dai,
&tx_ch_cnt, tx_ch, &rx_ch_cnt , rx_ch);
@@ -882,13 +928,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai,
- mdm9615_slim_0_tx_ch, tx_ch, 0, 0);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
}
end:
return ret;
@@ -947,6 +986,10 @@
msm9615_i2s_rx_ch_get, msm9615_i2s_rx_ch_put),
SOC_ENUM_EXT("PRI_TX Channels", mdm9615_enum[2],
msm9615_i2s_tx_ch_get, msm9615_i2s_tx_ch_put),
+ SOC_ENUM_EXT("SEC_RX Channels", mdm9615_enum[3],
+ msm9615_i2s_rx_ch_get, msm9615_i2s_rx_ch_put),
+ SOC_ENUM_EXT("SEC_TX Channels", mdm9615_enum[4],
+ msm9615_i2s_tx_ch_get, msm9615_i2s_tx_ch_put),
};
static int msm9615_i2s_audrx_init(struct snd_soc_pcm_runtime *rtd)
@@ -1234,47 +1277,64 @@
return ret;
}
-static void msm9615_config_i2s_sif_mux(u8 value)
+static void msm9615_config_i2s_sif_mux(u8 value, u8 i2s_intf)
{
struct msm_i2s_ctl *pintf = &msm9x15_i2s_ctl;
- u32 sif_shadow = 0x0000;
- /* Make this variable global if both secondary and
- * primary needs to be supported. This is required
- * to retain bits in interace and set only specific
- * bits in the register. Also set Sec Intf bits.
- * Secondary interface bits are 0,1.
- **/
- sif_shadow = (sif_shadow & LPASS_SIF_MUX_CTL_PRI_MUX_SEL_BMSK) |
- (value << LPASS_SIF_MUX_CTL_PRI_MUX_SEL_SHFT);
+ u32 sif_shadow = 0x0000;
+
+ pr_debug("%s() Value = 0x%x intf = 0x%x\n", __func__, value, i2s_intf);
+ if (i2s_intf == MSM_INTF_PRIM) {
+ sif_shadow = (sif_shadow & LPASS_SIF_MUX_CTL_PRI_MUX_SEL_BMSK) |
+ (value << LPASS_SIF_MUX_CTL_PRI_MUX_SEL_SHFT);
+ pr_debug("%s() Sif shadow = 0x%x\n", __func__, sif_shadow);
+ sif_reg_value =
+ ((sif_reg_value & LPASS_SIF_MUX_CTL_SEC_MUX_SEL_BMSK) |
+ sif_shadow);
+ }
+ if (i2s_intf == MSM_INTF_SECN) {
+ sif_shadow = (sif_shadow & LPASS_SIF_MUX_CTL_SEC_MUX_SEL_BMSK) |
+ (value << LPASS_SIF_MUX_CTL_SEC_MUX_SEL_SHFT);
+ pr_debug("%s() Sif shadow = 0x%x\n", __func__, sif_shadow);
+ sif_reg_value =
+ ((sif_reg_value & LPASS_SIF_MUX_CTL_PRI_MUX_SEL_BMSK) |
+ sif_shadow);
+ }
if (pintf->sif_virt_addr != NULL)
- iowrite32(sif_shadow, pintf->sif_virt_addr);
+ iowrite32(sif_reg_value, pintf->sif_virt_addr);
/* Dont read SIF register. Device crashes. */
- pr_debug("%s() SIF Reg = 0x%x\n", __func__, sif_shadow);
+ pr_debug("%s() SIF Reg = 0x%x\n", __func__, sif_reg_value);
}
static void msm9615_config_i2s_spare_mux(u8 value, u8 i2s_intf)
{
struct msm_i2s_ctl *pintf = &msm9x15_i2s_ctl;
u32 spare_shadow = 0x0000;
- /* Make this variable global if both secondary and
- * primary needs to be supported. This is required
- * to retain bits in interace and set only specific
- * bits in the register. Also set Sec Intf bits.
- **/
+
+ pr_debug("%s() Value = 0x%x intf = 0x%x\n", __func__, value, i2s_intf);
if (i2s_intf == MSM_INTF_PRIM) {
/* Configure Primary SIF */
- spare_shadow = (spare_shadow & LPAIF_SPARE_MUX_CTL_PRI_MUX_SEL_BMSK
- ) | (value << LPAIF_SPARE_MUX_CTL_PRI_MUX_SEL_SHFT);
+ spare_shadow =
+ (spare_shadow & LPAIF_SPARE_MUX_CTL_PRI_MUX_SEL_BMSK) |
+ (value << LPAIF_SPARE_MUX_CTL_PRI_MUX_SEL_SHFT);
+ pr_debug("%s() Spare shadow = 0x%x\n", __func__, spare_shadow);
+ spare_reg_value =
+ ((spare_shadow & LPAIF_SPARE_MUX_CTL_SEC_MUX_SEL_BMSK) |
+ spare_shadow);
}
if (i2s_intf == MSM_INTF_SECN) {
/*Secondary interface configuration*/
- spare_shadow = (spare_shadow & LPAIF_SPARE_MUX_CTL_SEC_MUX_SEL_BMSK
- ) | (value << LPAIF_SPARE_MUX_CTL_SEC_MUX_SEL_SHFT);
+ spare_shadow =
+ (spare_shadow & LPAIF_SPARE_MUX_CTL_SEC_MUX_SEL_BMSK) |
+ (value << LPAIF_SPARE_MUX_CTL_SEC_MUX_SEL_SHFT);
+ pr_debug("%s() Spare shadow = 0x%x\n", __func__, spare_shadow);
+ spare_reg_value =
+ ((spare_shadow & LPAIF_SPARE_MUX_CTL_PRI_MUX_SEL_BMSK) |
+ spare_shadow);
}
if (pintf->spare_virt_addr != NULL)
- iowrite32(spare_shadow, pintf->spare_virt_addr);
+ iowrite32(spare_reg_value, pintf->spare_virt_addr);
/* Dont read SPARE register. Device crashes. */
- pr_debug("%s( ): SPARE Reg =0x%x\n", __func__, spare_shadow);
+ pr_debug("%s( ): SPARE Reg =0x%x\n", __func__, spare_reg_value);
}
static int msm9615_i2s_hw_params(struct snd_pcm_substream *substream,
@@ -1342,7 +1402,8 @@
return -EINVAL;
}
msm9615_config_i2s_sif_mux(
- pintf->mux_ctl[MSM_DIR_BOTH].sifconfig);
+ pintf->mux_ctl[MSM_DIR_BOTH].sifconfig,
+ i2s_intf);
msm9615_config_i2s_spare_mux(
pintf->mux_ctl[MSM_DIR_BOTH].spareconfig,
i2s_intf);
@@ -1368,7 +1429,8 @@
return -EINVAL;
}
msm9615_config_i2s_sif_mux(
- pintf->mux_ctl[MSM_DIR_TX].sifconfig);
+ pintf->mux_ctl[MSM_DIR_TX].sifconfig,
+ i2s_intf);
msm9615_config_i2s_spare_mux(
pintf->mux_ctl[MSM_DIR_TX].spareconfig,
i2s_intf);
@@ -1397,7 +1459,8 @@
return -EINVAL;
}
msm9615_config_i2s_sif_mux(
- pintf->mux_ctl[MSM_DIR_RX].sifconfig);
+ pintf->mux_ctl[MSM_DIR_RX].sifconfig,
+ i2s_intf);
msm9615_config_i2s_spare_mux(
pintf->mux_ctl[MSM_DIR_RX].spareconfig,
i2s_intf);
@@ -1451,11 +1514,34 @@
pintf->intf_status[i2s_intf][MSM_DIR_TX]);
}
-static void mdm9615_install_codec_i2s_gpio(void)
+void msm9615_config_port_select(void)
{
- msm_gpiomux_install(msm9615_audio_prim_i2s_codec_configs,
- ARRAY_SIZE(msm9615_audio_prim_i2s_codec_configs));
+ iowrite32(SEC_PCM_PORT_SLC_VALUE, secpcm_portslc_virt_addr);
+ pr_debug("%s() port select after updating = 0x%x\n",
+ __func__, ioread32(secpcm_portslc_virt_addr));
}
+static void mdm9615_install_codec_i2s_gpio(struct snd_pcm_substream *substream)
+{
+ u8 i2s_intf, i2s_dir;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+
+ if (!msm9615_i2s_intf_dir_sel(cpu_dai->name, &i2s_intf, &i2s_dir)) {
+ pr_debug("%s( ): cpu name = %s intf =%d dir = %d\n",
+ __func__, cpu_dai->name, i2s_intf, i2s_dir);
+ if (i2s_intf == MSM_INTF_PRIM) {
+ msm_gpiomux_install(
+ msm9615_audio_prim_i2s_codec_configs,
+ ARRAY_SIZE(msm9615_audio_prim_i2s_codec_configs));
+ } else if (i2s_intf == MSM_INTF_SECN) {
+ msm_gpiomux_install(msm9615_audio_sec_i2s_codec_configs,
+ ARRAY_SIZE(msm9615_audio_sec_i2s_codec_configs));
+ msm9615_config_port_select();
+
+ }
+ }
+}
+
static int msm9615_i2s_prepare(struct snd_pcm_substream *substream)
{
u8 ret = 0;
@@ -1463,7 +1549,7 @@
if (wcd9xxx_get_intf_type() < 0)
ret = -ENODEV;
else if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
- mdm9615_install_codec_i2s_gpio();
+ mdm9615_install_codec_i2s_gpio(substream);
return ret;
}
@@ -1488,6 +1574,15 @@
.vin_sel = 2,
.inv_int_pol = 0,
};
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ /* Tabla SLIMBUS configuration
+ * RX1, RX2, RX3, RX4, RX5, RX6, RX7
+ * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10
+ */
+ unsigned int rx_ch[TABLA_RX_MAX] = {138, 139, 140, 141, 142, 143, 144};
+ unsigned int tx_ch[TABLA_TX_MAX] = {128, 129, 130, 131, 132, 133, 134,
+ 135, 136, 137};
pr_debug("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
@@ -1539,6 +1634,10 @@
err = tabla_hs_detect(codec, &mbhc_cfg);
+ snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+ tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
+
+
return err;
}
@@ -1734,15 +1833,6 @@
pr_debug("%s() SIF Reg = 0x%x\n", __func__, sif_shadow);
}
-void msm9615_config_port_select(void)
-{
- pr_debug("%s() port select defualt = 0x%x\n",
- __func__, ioread32(secpcm_portslc_virt_addr));
- iowrite32(SEC_PCM_PORT_SLC_VALUE, secpcm_portslc_virt_addr);
- pr_debug("%s() port select after updating = 0x%x\n",
- __func__, ioread32(secpcm_portslc_virt_addr));
-}
-
static int mdm9615_auxpcm_startup(struct snd_pcm_substream *substream)
{
int ret = 0;
@@ -2049,6 +2139,30 @@
.be_hw_params_fixup = msm9615_i2s_tx_be_hw_params_fixup,
.ops = &msm9615_i2s_be_ops,
},
+ {
+ .name = LPASS_BE_SEC_I2S_RX,
+ .stream_name = "Secondary I2S Playback",
+ .cpu_dai_name = "msm-dai-q6.4",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_I2S_RX,
+ .be_hw_params_fixup = msm9615_i2s_rx_be_hw_params_fixup,
+ .ops = &msm9615_i2s_be_ops,
+ },
+ {
+ .name = LPASS_BE_SEC_I2S_TX,
+ .stream_name = "Secondary I2S Capture",
+ .cpu_dai_name = "msm-dai-q6.5",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_I2S_TX,
+ .be_hw_params_fixup = msm9615_i2s_tx_be_hw_params_fixup,
+ .ops = &msm9615_i2s_be_ops,
+ },
};
static struct snd_soc_dai_link mdm9615_dai_slimbus_tabla[] = {
@@ -2097,8 +2211,8 @@
},
[1] = {
.name = "mdm9615-tabla-snd-card-i2s",
- .controls = tabla_mdm9615_controls,
- .num_controls = ARRAY_SIZE(tabla_mdm9615_controls),
+ .controls = tabla_msm9615_i2s_controls,
+ .num_controls = ARRAY_SIZE(tabla_msm9615_i2s_controls),
},
};
diff --git a/sound/soc/msm/mpq8064.c b/sound/soc/msm/mpq8064.c
index 957b656..7656f9f 100644
--- a/sound/soc/msm/mpq8064.c
+++ b/sound/soc/msm/mpq8064.c
@@ -45,6 +45,11 @@
#define TOP_SPK_AMP_POS 0x4
#define TOP_SPK_AMP_NEG 0x8
+#define GPIO_AUX_PCM_DOUT 43
+#define GPIO_AUX_PCM_DIN 44
+#define GPIO_AUX_PCM_SYNC 45
+#define GPIO_AUX_PCM_CLK 46
+
#define TABLA_EXT_CLK_RATE 12288000
#define TABLA_MBHC_DEF_BUTTONS 8
@@ -135,6 +140,7 @@
static int msm_slim_0_tx_ch = 1;
static int msm_hdmi_rx_ch = 8;
static int mi2s_rate_variable;
+static int hdmi_rate_variable;
static struct clk *codec_clk;
static int clk_users;
@@ -518,6 +524,8 @@
static const char * const hdmi_rx_ch_text[] = {"Two", "Three", "Four",
"Five", "Six", "Seven", "Eight"};
static const char * const mi2s_rate[] = {"Default", "Variable"};
+static const char * const hdmi_rate[] = {"Default", "Variable"};
+
static const struct soc_enum msm_enum[] = {
@@ -526,6 +534,7 @@
SOC_ENUM_SINGLE_EXT(4, slim0_tx_ch_text),
SOC_ENUM_SINGLE_EXT(7, hdmi_rx_ch_text),
SOC_ENUM_SINGLE_EXT(2, mi2s_rate),
+ SOC_ENUM_SINGLE_EXT(2, hdmi_rate),
};
@@ -601,6 +610,21 @@
return 0;
}
+static int msm_hdmi_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ hdmi_rate_variable = ucontrol->value.integer.value[0];
+ pr_debug("%s: hdmi_rate_variable = %d\n", __func__, hdmi_rate_variable);
+ return 0;
+}
+
+static int msm_hdmi_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = hdmi_rate_variable;
+ return 0;
+}
+
static const struct snd_kcontrol_new tabla_msm_controls[] = {
SOC_ENUM_EXT("Speaker Function", msm_enum[0], msm_get_spk,
msm_set_spk),
@@ -613,6 +637,9 @@
SOC_ENUM_EXT("SEC RX Rate", msm_enum[4],
msm_mi2s_rate_get,
msm_mi2s_rate_put),
+ SOC_ENUM_EXT("HDMI RX Rate", msm_enum[5],
+ msm_hdmi_rate_get,
+ msm_hdmi_rate_put),
};
@@ -719,13 +746,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
- msm_slim_0_rx_ch, rx_ch);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
} else {
ret = snd_soc_dai_get_channel_map(codec_dai,
&tx_ch_cnt, tx_ch, &rx_ch_cnt , rx_ch);
@@ -739,14 +759,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai,
- msm_slim_0_tx_ch, tx_ch, 0, 0);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
-
}
end:
@@ -759,6 +771,10 @@
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ unsigned int rx_ch[TABLA_RX_MAX] = {138, 139, 140, 141, 142, 143, 144};
+ unsigned int tx_ch[TABLA_TX_MAX] = {128, 129, 130, 131, 132, 133, 134,
+ 135, 136, 137};
pr_debug("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
@@ -793,6 +809,8 @@
codec_clk = clk_get(cpu_dai->dev, "osr_clk");
err = tabla_hs_detect(codec, &mbhc_cfg);
+ snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+ tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
return err;
}
@@ -871,7 +889,9 @@
pr_debug("%s channels->min %u channels->max %u ()\n", __func__,
channels->min, channels->max);
- rate->min = rate->max = 48000;
+ /*Configure the sample rate as 48000 KHz for the LPCM playback*/
+ if (!hdmi_rate_variable)
+ rate->min = rate->max = 48000;
channels->min = channels->max = msm_hdmi_rx_ch;
return 0;
@@ -942,6 +962,78 @@
return ret;
}
+static int mpq8064_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ /* PCM only supports mono output with 8khz sample rate */
+ rate->min = rate->max = 8000;
+ channels->min = channels->max = 1;
+
+ return 0;
+}
+
+static int mpq8064_aux_pcm_get_gpios(void)
+{
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+
+ ret = gpio_request(GPIO_AUX_PCM_DOUT, "AUX PCM DOUT");
+ if (ret < 0) {
+ pr_err("%s: Failed to request gpio(%d): AUX PCM DOUT",
+ __func__, GPIO_AUX_PCM_DOUT);
+ goto fail_dout;
+ }
+
+ ret = gpio_request(GPIO_AUX_PCM_DIN, "AUX PCM DIN");
+ if (ret < 0) {
+ pr_err("%s: Failed to request gpio(%d): AUX PCM DIN",
+ __func__, GPIO_AUX_PCM_DIN);
+ goto fail_din;
+ }
+
+ ret = gpio_request(GPIO_AUX_PCM_SYNC, "AUX PCM SYNC");
+ if (ret < 0) {
+ pr_err("%s: Failed to request gpio(%d): AUX PCM SYNC",
+ __func__, GPIO_AUX_PCM_SYNC);
+ goto fail_sync;
+ }
+ ret = gpio_request(GPIO_AUX_PCM_CLK, "AUX PCM CLK");
+ if (ret < 0) {
+ pr_err("%s: Failed to request gpio(%d): AUX PCM CLK",
+ __func__, GPIO_AUX_PCM_CLK);
+ goto fail_clk;
+ }
+
+ return 0;
+
+fail_clk:
+ gpio_free(GPIO_AUX_PCM_SYNC);
+fail_sync:
+ gpio_free(GPIO_AUX_PCM_DIN);
+fail_din:
+ gpio_free(GPIO_AUX_PCM_DOUT);
+fail_dout:
+
+ return ret;
+}
+
+static int mpq8064_aux_pcm_free_gpios(void)
+{
+ gpio_free(GPIO_AUX_PCM_DIN);
+ gpio_free(GPIO_AUX_PCM_DOUT);
+ gpio_free(GPIO_AUX_PCM_SYNC);
+ gpio_free(GPIO_AUX_PCM_CLK);
+
+ return 0;
+}
+
static int msm_startup(struct snd_pcm_substream *substream)
{
pr_debug("%s(): substream = %s stream = %d\n", __func__,
@@ -955,12 +1047,39 @@
substream->name, substream->stream);
}
+static int mpq8064_auxpcm_startup(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ pr_debug("%s(): substream = %s\n", __func__, substream->name);
+ ret = mpq8064_aux_pcm_get_gpios();
+ if (ret < 0) {
+ pr_err("%s: Aux PCM GPIO request failed\n", __func__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void mpq8064_auxpcm_shutdown(struct snd_pcm_substream *substream)
+{
+
+ pr_debug("%s(): substream = %s\n", __func__, substream->name);
+ mpq8064_aux_pcm_free_gpios();
+}
+
+
static struct snd_soc_ops msm_be_ops = {
.startup = msm_startup,
.hw_params = msm_hw_params,
.shutdown = msm_shutdown,
};
+static struct snd_soc_ops mpq8064_auxpcm_be_ops = {
+ .startup = mpq8064_auxpcm_startup,
+ .shutdown = mpq8064_auxpcm_shutdown,
+};
+
+
static int mpq8064_sec_i2s_rx_free_gpios(void)
{
int i;
@@ -1341,6 +1460,20 @@
.ignore_pmdown_time = 1, /* this dailink has playback support */
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA8,
},
+ {
+ .name = "AUXPCM Hostless",
+ .stream_name = "AUXPCM Hostless",
+ .cpu_dai_name = "AUXPCM_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1, /* dainlink has playback support */
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
/* Backend DAI Links */
{
.name = LPASS_BE_SLIMBUS_0_RX,
@@ -1451,6 +1584,31 @@
.no_pcm = 1,
.be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
},
+ /* AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_AUXPCM_RX,
+ .stream_name = "AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6.2",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_AUXPCM_RX,
+ .be_hw_params_fixup = mpq8064_auxpcm_be_params_fixup,
+ .ops = &mpq8064_auxpcm_be_ops,
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = LPASS_BE_AUXPCM_TX,
+ .stream_name = "AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6.3",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_AUXPCM_TX,
+ .be_hw_params_fixup = mpq8064_auxpcm_be_params_fixup,
+ },
};
diff --git a/sound/soc/msm/msm-compr-q6.c b/sound/soc/msm/msm-compr-q6.c
index 550a492..8202982 100644
--- a/sound/soc/msm/msm-compr-q6.c
+++ b/sound/soc/msm/msm-compr-q6.c
@@ -32,6 +32,7 @@
#include <linux/android_pmem.h>
#include <sound/timer.h>
#include <mach/qdsp6v2/q6core.h>
+#include <sound/pcm.h>
#include "msm-compr-q6.h"
#include "msm-pcm-routing.h"
@@ -140,6 +141,10 @@
break;
} else
atomic_set(&prtd->pending_buffer, 0);
+ if (runtime->status->hw_ptr >= runtime->control->appl_ptr) {
+ runtime->render_flag |= SNDRV_RENDER_STOPPED;
+ break;
+ }
buf = prtd->audio_client->port[IN].buf;
pr_debug("%s:writing %d bytes of buffer[%d] to dsp 2\n",
__func__, prtd->pcm_count, prtd->out_head);
@@ -197,6 +202,7 @@
wake_up(&the_locks.eos_wait);
atomic_set(&prtd->eos, 0);
}
+ atomic_set(&prtd->pending_buffer, 1);
break;
case ASM_DATA_EVENT_READ_DONE: {
pr_debug("ASM_DATA_EVENT_READ_DONE\n");
@@ -461,6 +467,9 @@
return ret;
}
break;
+ case SND_AUDIOCODEC_MP2:
+ pr_debug("%s: SND_AUDIOCODEC_MP2\n", __func__);
+ break;
default:
return -EINVAL;
}
@@ -551,9 +560,7 @@
prtd->enabled = 1;
if (compr->info.codec_param.codec.id ==
- SND_AUDIOCODEC_AC3_PASS_THROUGH ||
- compr->info.codec_param.codec.id ==
- SND_AUDIOCODEC_DTS_PASS_THROUGH)
+ SND_AUDIOCODEC_PASS_THROUGH)
msm_pcm_routing_reg_psthr_stream(
soc_prtd->dai_link->be_id,
prtd->session_id, substream->stream,
@@ -562,6 +569,70 @@
return ret;
}
+static int msm_compr_restart(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct compr_audio *compr = runtime->private_data;
+ struct msm_audio *prtd = &compr->prtd;
+ struct audio_aio_write_param param;
+ struct audio_buffer *buf = NULL;
+ struct output_meta_data_st output_meta_data;
+ int time_stamp_flag = 0;
+ int buffer_length = 0;
+
+ pr_err("msm_compr_restart\n");
+ if (runtime->render_flag & SNDRV_RENDER_STOPPED) {
+ buf = prtd->audio_client->port[IN].buf;
+ pr_debug("%s:writing %d bytes of buffer[%d] to dsp 2\n",
+ __func__, prtd->pcm_count, prtd->out_head);
+ pr_debug("%s:writing buffer[%d] from 0x%08x\n",
+ __func__, prtd->out_head,
+ ((unsigned int)buf[0].phys
+ + (prtd->out_head * prtd->pcm_count)));
+
+ if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
+ time_stamp_flag = SET_TIMESTAMP;
+ else
+ time_stamp_flag = NO_TIMESTAMP;
+ memcpy(&output_meta_data, (char *)(buf->data +
+ prtd->out_head * prtd->pcm_count),
+ COMPRE_OUTPUT_METADATA_SIZE);
+
+ buffer_length = output_meta_data.frame_size;
+ pr_debug("meta_data_length: %d, frame_length: %d\n",
+ output_meta_data.meta_data_length,
+ output_meta_data.frame_size);
+ pr_debug("timestamp_msw: %d, timestamp_lsw: %d\n",
+ output_meta_data.timestamp_msw,
+ output_meta_data.timestamp_lsw);
+ if (buffer_length == 0) {
+ pr_debug("Recieved a zero length buffer-break out");
+ return -EINVAL;
+ }
+ param.paddr = (unsigned long)buf[0].phys
+ + (prtd->out_head * prtd->pcm_count)
+ + output_meta_data.meta_data_length;
+ param.len = buffer_length;
+ param.msw_ts = output_meta_data.timestamp_msw;
+ param.lsw_ts = output_meta_data.timestamp_lsw;
+ param.flags = time_stamp_flag;
+ param.uid = (unsigned long)buf[0].phys
+ + (prtd->out_head * prtd->pcm_count
+ + output_meta_data.meta_data_length);
+ if (q6asm_async_write(prtd->audio_client,
+ ¶m) < 0)
+ pr_err("%s:q6asm_async_write failed\n",
+ __func__);
+ else
+ prtd->out_head =
+ (prtd->out_head + 1) & (runtime->periods - 1);
+
+ runtime->render_flag &= ~SNDRV_RENDER_STOPPED;
+ return 0;
+ }
+ return 0;
+}
+
static int msm_compr_trigger(struct snd_pcm_substream *substream, int cmd)
{
int ret = 0;
@@ -582,12 +653,14 @@
break;
case SNDRV_PCM_TRIGGER_STOP:
atomic_set(&prtd->start, 0);
+ runtime->render_flag &= ~SNDRV_RENDER_STOPPED;
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
atomic_set(&prtd->start, 0);
+ runtime->render_flag &= ~SNDRV_RENDER_STOPPED;
break;
default:
ret = -EINVAL;
@@ -602,7 +675,7 @@
{
pr_debug("%s\n", __func__);
/* MP3 Block */
- compr->info.compr_cap.num_codecs = 12;
+ compr->info.compr_cap.num_codecs = 13;
compr->info.compr_cap.min_fragment_size = runtime->hw.period_bytes_min;
compr->info.compr_cap.max_fragment_size = runtime->hw.period_bytes_max;
compr->info.compr_cap.min_fragments = runtime->hw.periods_min;
@@ -619,6 +692,7 @@
compr->info.compr_cap.codecs[9] = SND_AUDIOCODEC_AMRWBPLUS;
compr->info.compr_cap.codecs[10] = SND_AUDIOCODEC_PASS_THROUGH;
compr->info.compr_cap.codecs[11] = SND_AUDIOCODEC_PCM;
+ compr->info.compr_cap.codecs[12] = SND_AUDIOCODEC_MP2;
/* Add new codecs here and update num_codecs*/
}
@@ -648,6 +722,7 @@
}
prtd = &compr->prtd;
prtd->substream = substream;
+ runtime->render_flag = SNDRV_DMA_MODE;
prtd->audio_client = q6asm_audio_client_alloc(
(app_cb)compr_event_handler, compr);
if (!prtd->audio_client) {
@@ -768,9 +843,7 @@
q6asm_audio_client_buf_free_contiguous(dir,
prtd->audio_client);
if (compr->info.codec_param.codec.id ==
- SND_AUDIOCODEC_AC3_PASS_THROUGH ||
- compr->info.codec_param.codec.id ==
- SND_AUDIOCODEC_DTS_PASS_THROUGH)
+ SND_AUDIOCODEC_PASS_THROUGH)
msm_pcm_routing_reg_psthr_stream(
soc_prtd->dai_link->be_id,
prtd->session_id, substream->stream,
@@ -832,6 +905,7 @@
pr_debug("%s\n", __func__);
prtd->mmap_flag = 1;
+ runtime->render_flag = SNDRV_NON_DMA_MODE;
if (runtime->dma_addr && runtime->dma_bytes) {
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
result = remap_pfn_range(vma, vma->vm_start,
@@ -1115,6 +1189,10 @@
pr_debug("msm_compr_ioctl SND_AUDIOCODEC_PCM\n");
compr->codec = FORMAT_MULTI_CHANNEL_LINEAR_PCM;
break;
+ case SND_AUDIOCODEC_MP2:
+ pr_debug("SND_AUDIOCODEC_MP2\n");
+ compr->codec = FORMAT_MP2;
+ break;
default:
pr_err("msm_compr_ioctl failed..unknown codec\n");
return -EFAULT;
@@ -1131,6 +1209,7 @@
prtd->cmd_ack = 1;
wake_up(&the_locks.eos_wait);
atomic_set(&prtd->eos, 0);
+ atomic_set(&prtd->pending_buffer, 1);
}
/* A unlikely race condition possible with FLUSH
@@ -1177,6 +1256,7 @@
.trigger = msm_compr_trigger,
.pointer = msm_compr_pointer,
.mmap = msm_compr_mmap,
+ .restart = msm_compr_restart,
};
static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
index 16a4aaa..dc8d9e6 100644
--- a/sound/soc/msm/msm-dai-fe.c
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -270,9 +270,9 @@
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rate_min = 8000,
- .rate_max = 48000,
+ .rate_max = 192000,
},
.capture = {
.stream_name = "SLIMBUS0 Hostless Capture",
@@ -280,9 +280,9 @@
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rate_min = 8000,
- .rate_max = 48000,
+ .rate_max = 192000,
},
.ops = &msm_fe_dai_ops,
.name = "SLIMBUS0_HOSTLESS",
diff --git a/sound/soc/msm/msm-dai-q6-hdmi.c b/sound/soc/msm/msm-dai-q6-hdmi.c
index 2b3dd5f..1995f1a 100644
--- a/sound/soc/msm/msm-dai-q6-hdmi.c
+++ b/sound/soc/msm/msm-dai-q6-hdmi.c
@@ -91,11 +91,25 @@
u32 channel_allocation = 0;
u32 level_shift = 0; /* 0dB */
bool down_mix = FALSE;
+ int sample_rate = 48000;
dai_data->channels = params_channels(params);
dai_data->rate = params_rate(params);
dai_data->port_config.hdmi_multi_ch.reserved = 0;
+ switch (dai_data->rate) {
+ case 48000:
+ sample_rate = HDMI_SAMPLE_RATE_48KHZ;
+ break;
+ case 44100:
+ sample_rate = HDMI_SAMPLE_RATE_44_1KHZ;
+ break;
+ case 32000:
+ sample_rate = HDMI_SAMPLE_RATE_32KHZ;
+ break;
+ }
+ hdmi_msm_audio_sample_rate_reset(sample_rate);
+
switch (dai_data->channels) {
case 2:
channel_allocation = 0;
diff --git a/sound/soc/msm/msm-dai-q6.c b/sound/soc/msm/msm-dai-q6.c
index ee1ab79..18c2329 100644
--- a/sound/soc/msm/msm-dai-q6.c
+++ b/sound/soc/msm/msm-dai-q6.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -674,6 +674,7 @@
case PRIMARY_I2S_TX:
case PRIMARY_I2S_RX:
case SECONDARY_I2S_RX:
+ case SECONDARY_I2S_TX:
rc = msm_dai_q6_cdc_hw_params(params, dai, substream->stream);
break;
@@ -1379,6 +1380,7 @@
case PRIMARY_I2S_TX:
case PRIMARY_I2S_RX:
case SECONDARY_I2S_RX:
+ case SECONDARY_I2S_TX:
rc = msm_dai_q6_cdc_set_fmt(dai, fmt);
break;
default:
@@ -1831,6 +1833,7 @@
rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_i2s_rx_dai);
break;
case PRIMARY_I2S_TX:
+ case SECONDARY_I2S_TX:
rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_i2s_tx_dai);
break;
case PCM_RX:
diff --git a/sound/soc/msm/msm-lowlatency-pcm-q6.c b/sound/soc/msm/msm-lowlatency-pcm-q6.c
index 129f69f..fcfcb66 100644
--- a/sound/soc/msm/msm-lowlatency-pcm-q6.c
+++ b/sound/soc/msm/msm-lowlatency-pcm-q6.c
@@ -218,8 +218,27 @@
if (prtd->enabled)
return 0;
+ if (!prtd->set_channel_map) {
+ memset(prtd->channel_map, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+ if (prtd->channel_mode == 1) {
+ prtd->channel_map[0] = PCM_CHANNEL_FL;
+ } else if (prtd->channel_mode == 2) {
+ prtd->channel_map[0] = PCM_CHANNEL_FL;
+ prtd->channel_map[0] = PCM_CHANNEL_FR;
+ } else if (prtd->channel_mode == 6) {
+ prtd->channel_map[0] = PCM_CHANNEL_FC;
+ prtd->channel_map[0] = PCM_CHANNEL_FL;
+ prtd->channel_map[0] = PCM_CHANNEL_FR;
+ prtd->channel_map[0] = PCM_CHANNEL_LB;
+ prtd->channel_map[0] = PCM_CHANNEL_RB;
+ prtd->channel_map[0] = PCM_CHANNEL_LFE;
+ } else {
+ pr_err("%s: ERROR.unsupported num_ch = %u\n", __func__,
+ prtd->channel_mode);
+ }
+ }
ret = q6asm_media_format_block_multi_ch_pcm(prtd->audio_client,
- runtime->rate, runtime->channels);
+ runtime->rate, runtime->channels, prtd->channel_map);
if (ret < 0)
pr_info("%s: CMD Format block failed\n", __func__);
@@ -389,6 +408,7 @@
}
prtd->dsp_cnt = 0;
+ prtd->set_channel_map = false;
runtime->private_data = prtd;
pr_debug("substream->pcm->device = %d\n", substream->pcm->device);
pr_debug("soc_prtd->dai_link->be_id = %d\n", soc_prtd->dai_link->be_id);
diff --git a/sound/soc/msm/msm-multi-ch-pcm-q6.c b/sound/soc/msm/msm-multi-ch-pcm-q6.c
index 5b0759c..7d04f95 100644
--- a/sound/soc/msm/msm-multi-ch-pcm-q6.c
+++ b/sound/soc/msm/msm-multi-ch-pcm-q6.c
@@ -269,9 +269,29 @@
prtd->channel_mode = runtime->channels;
if (prtd->enabled)
return 0;
-
+ pr_debug("prtd->set_channel_map: %d", prtd->set_channel_map);
+ if (!prtd->set_channel_map) {
+ pr_debug("using default channel map");
+ memset(prtd->channel_map, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+ if (prtd->channel_mode == 1) {
+ prtd->channel_map[0] = PCM_CHANNEL_FL;
+ } else if (prtd->channel_mode == 2) {
+ prtd->channel_map[1] = PCM_CHANNEL_FL;
+ prtd->channel_map[2] = PCM_CHANNEL_FR;
+ } else if (prtd->channel_mode == 6) {
+ prtd->channel_map[0] = PCM_CHANNEL_FC;
+ prtd->channel_map[1] = PCM_CHANNEL_FL;
+ prtd->channel_map[2] = PCM_CHANNEL_FR;
+ prtd->channel_map[3] = PCM_CHANNEL_LB;
+ prtd->channel_map[4] = PCM_CHANNEL_RB;
+ prtd->channel_map[5] = PCM_CHANNEL_LFE;
+ } else {
+ pr_err("%s: ERROR.unsupported num_ch = %u\n", __func__,
+ prtd->channel_mode);
+ }
+ }
ret = q6asm_media_format_block_multi_ch_pcm(prtd->audio_client,
- runtime->rate, runtime->channels);
+ runtime->rate, runtime->channels, prtd->channel_map);
if (ret < 0)
pr_info("%s: CMD Format block failed\n", __func__);
@@ -452,6 +472,7 @@
}
prtd->dsp_cnt = 0;
+ prtd->set_channel_map = false;
runtime->private_data = prtd;
pr_debug("substream->pcm->device = %d\n", substream->pcm->device);
pr_debug("soc_prtd->dai_link->be_id = %d\n", soc_prtd->dai_link->be_id);
@@ -492,6 +513,15 @@
return rc;
}
+void multi_ch_pcm_set_channel_map(char *channel_mapping)
+{
+ pr_debug("%s\n", __func__);
+ if (multi_ch_pcm_audio.prtd) {
+ multi_ch_pcm_audio.prtd->set_channel_map = true;
+ memcpy(multi_ch_pcm_audio.prtd->channel_map, channel_mapping,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+ }
+}
static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
diff --git a/sound/soc/msm/msm-pcm-q6.h b/sound/soc/msm/msm-pcm-q6.h
index f1af99a..2678498 100644
--- a/sound/soc/msm/msm-pcm-q6.h
+++ b/sound/soc/msm/msm-pcm-q6.h
@@ -81,6 +81,8 @@
int periods;
int mmap_flag;
atomic_t pending_buffer;
+ bool set_channel_map;
+ char channel_map[8];
};
struct output_meta_data_st {
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index 4d0caa3..2b889b5 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -78,6 +78,7 @@
static const DECLARE_TLV_DB_LINEAR(compressed2_rx_vol_gain, 0,
INT_RX_VOL_MAX_STEPS);
static int msm_route_ec_ref_rx;
+static char channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL];
/* Equal to Frontend after last of the MULTIMEDIA SESSIONS */
#define MAX_EQ_SESSIONS MSM_FRONTEND_DAI_CS_VOICE
@@ -182,6 +183,7 @@
{ MI2S_RX, 0, 0, 0, 0, 0},
{ MI2S_TX, 0, 0, 0, 0},
{ SECONDARY_I2S_RX, 0, 0, 0, 0, 0},
+ { SECONDARY_I2S_TX, 0, 0, 0, 0, 0},
{ SLIMBUS_1_RX, 0, 0, 0, 0, 0},
{ SLIMBUS_1_TX, 0, 0, 0, 0, 0},
{ SLIMBUS_4_RX, 0, 0, 0, 0, 0},
@@ -862,6 +864,27 @@
return 0;
}
+static int msm_routing_get_channel_map_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i;
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+ ucontrol->value.integer.value[i] = channel_mapping[i];
+ return 0;
+}
+
+static int msm_routing_put_channel_map_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i;
+
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+ channel_mapping[i] = (char)(ucontrol->value.integer.value[i]);
+ multi_ch_pcm_set_channel_map(channel_mapping);
+
+ return 0;
+}
+
static int msm_routing_set_compressed_vol_mixer(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1395,6 +1418,9 @@
SOC_SINGLE_EXT("PRI_TX", MSM_BACKEND_DAI_PRI_I2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("SEC_TX", MSM_BACKEND_DAI_SEC_I2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
@@ -1464,7 +1490,13 @@
msm_routing_put_audio_mixer),
SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
- msm_routing_put_audio_mixer)
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("VOC_REC_DL", MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("VOC_REC_UL", MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = {
@@ -1639,6 +1671,9 @@
SOC_SINGLE_EXT("PRI_TX_Voice", MSM_BACKEND_DAI_PRI_I2S_TX,
MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SEC_TX_Voice", MSM_BACKEND_DAI_SEC_I2S_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("MI2S_TX_Voice", MSM_BACKEND_DAI_MI2S_TX,
MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -1663,6 +1698,9 @@
SOC_SINGLE_EXT("PRI_TX_VoLTE", MSM_BACKEND_DAI_PRI_I2S_TX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SEC_TX_VoLTE", MSM_BACKEND_DAI_SEC_I2S_TX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("SLIM_0_TX_VoLTE", MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -1684,6 +1722,9 @@
SOC_SINGLE_EXT("PRI_TX_SGLTE", MSM_BACKEND_DAI_PRI_I2S_TX,
MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SEC_TX_SGLTE", MSM_BACKEND_DAI_SEC_I2S_TX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("MI2S_TX_SGLTE", MSM_BACKEND_DAI_MI2S_TX,
MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -1707,6 +1748,9 @@
SOC_SINGLE_EXT("PRI_TX_Voip", MSM_BACKEND_DAI_PRI_I2S_TX,
MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SEC_TX_Voip", MSM_BACKEND_DAI_SEC_I2S_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("MI2S_TX_Voip", MSM_BACKEND_DAI_MI2S_TX,
MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -1749,6 +1793,9 @@
SOC_SINGLE_EXT("PRIMARY_I2S_TX", MSM_BACKEND_DAI_PRI_I2S_TX,
MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("SECONDARY_I2S_TX", MSM_BACKEND_DAI_SEC_I2S_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX,
MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
msm_routing_put_voice_stub_mixer),
@@ -1891,6 +1938,12 @@
msm_routing_set_compressed2_vol_mixer, compressed2_rx_vol_gain),
};
+static const struct snd_kcontrol_new multi_ch_channel_map_mixer_controls[] = {
+ SOC_SINGLE_MULTI_EXT("Playback Channel Map", SND_SOC_NOPM, 0, 8,
+ 0, 8, msm_routing_get_channel_map_mixer,
+ msm_routing_put_channel_map_mixer),
+};
+
static const struct snd_kcontrol_new lpa_SRS_trumedia_controls[] = {
{.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "SRS TruMedia",
@@ -2178,6 +2231,7 @@
SND_SOC_DAPM_AIF_OUT("HDMI", "HDMI Playback", 0, 0, 0 , 0),
SND_SOC_DAPM_AIF_OUT("MI2S_RX", "MI2S Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("PRI_I2S_TX", "Primary I2S Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_I2S_TX", "Secondary I2S Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("MI2S_TX", "MI2S Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("SLIMBUS_0_TX", "Slimbus Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("INT_BT_SCO_RX", "Internal BT-SCO Playback",
@@ -2404,6 +2458,8 @@
{"MultiMedia1 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"},
{"MultiMedia1 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"},
+ {"MultiMedia4 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"},
+ {"MultiMedia4 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"},
{"MultiMedia1 Mixer", "SLIM_4_TX", "SLIMBUS_4_TX"},
{"MultiMedia5 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -2414,6 +2470,7 @@
{"MI2S_RX", NULL, "MI2S_RX Audio Mixer"},
{"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"},
+ {"MultiMedia1 Mixer", "SEC_TX", "SEC_I2S_TX"},
{"MultiMedia1 Mixer", "MI2S_TX", "MI2S_TX"},
{"MultiMedia2 Mixer", "MI2S_TX", "MI2S_TX"},
{"MultiMedia4 Mixer", "MI2S_TX", "MI2S_TX"},
@@ -2519,6 +2576,7 @@
{"HDMI", NULL, "HDMI_DL_HL"},
{"Voice_Tx Mixer", "PRI_TX_Voice", "PRI_I2S_TX"},
+ {"Voice_Tx Mixer", "SEC_TX_Voice", "SEC_I2S_TX"},
{"Voice_Tx Mixer", "MI2S_TX_Voice", "MI2S_TX"},
{"Voice_Tx Mixer", "SLIM_0_TX_Voice", "SLIMBUS_0_TX"},
{"Voice_Tx Mixer", "INTERNAL_BT_SCO_TX_Voice", "INT_BT_SCO_TX"},
@@ -2527,6 +2585,7 @@
{"Voice_Tx Mixer", "SEC_AUX_PCM_TX_Voice", "SEC_AUX_PCM_TX"},
{"CS-VOICE_UL1", NULL, "Voice_Tx Mixer"},
{"VoLTE_Tx Mixer", "PRI_TX_VoLTE", "PRI_I2S_TX"},
+ {"VoLTE_Tx Mixer", "SEC_TX_VoLTE", "SEC_I2S_TX"},
{"VoLTE_Tx Mixer", "SLIM_0_TX_VoLTE", "SLIMBUS_0_TX"},
{"VoLTE_Tx Mixer", "INTERNAL_BT_SCO_TX_VoLTE", "INT_BT_SCO_TX"},
{"VoLTE_Tx Mixer", "AFE_PCM_TX_VoLTE", "PCM_TX"},
@@ -2534,6 +2593,7 @@
{"VoLTE_Tx Mixer", "SEC_AUX_PCM_TX_VoLTE", "SEC_AUX_PCM_TX"},
{"VoLTE_UL", NULL, "VoLTE_Tx Mixer"},
{"SGLTE_Tx Mixer", "PRI_TX_SGLTE", "PRI_I2S_TX"},
+ {"SGLTE_Tx Mixer", "SEC_TX_SGLTE", "SEC_I2S_TX"},
{"SGLTE_Tx Mixer", "MI2S_TX_SGLTE", "MI2S_TX"},
{"SGLTE_Tx Mixer", "SLIM_0_TX_SGLTE", "SLIMBUS_0_TX"},
{"SGLTE_Tx Mixer", "INTERNAL_BT_SCO_TX_SGLTE", "INT_BT_SCO_TX"},
@@ -2542,6 +2602,7 @@
{"SGLTE_Tx Mixer", "SEC_AUX_PCM_TX_SGLTE", "SEC_AUX_PCM_TX"},
{"SGLTE_UL", NULL, "SGLTE_Tx Mixer"},
{"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"},
+ {"Voip_Tx Mixer", "SEC_TX_Voip", "SEC_I2S_TX"},
{"Voip_Tx Mixer", "MI2S_TX_Voip", "MI2S_TX"},
{"Voip_Tx Mixer", "SLIM_0_TX_Voip", "SLIMBUS_0_TX"},
{"Voip_Tx Mixer", "INTERNAL_BT_SCO_TX_Voip", "INT_BT_SCO_TX"},
@@ -2581,6 +2642,7 @@
{"Voice Stub Tx Mixer", "MI2S_TX", "MI2S_TX"},
{"Voice Stub Tx Mixer", "SLIM_3_TX", "SLIMBUS_3_TX"},
{"Voice Stub Tx Mixer", "PRIMARY_I2S_TX", "PRI_I2S_TX"},
+ {"Voice Stub Tx Mixer", "SECONDARY_I2S_TX", "SEC_I2S_TX"},
{"Voice Stub Tx Mixer", "AFE_PCM_TX", "PCM_TX"},
{"VOICE_STUB_UL", NULL, "Voice Stub Tx Mixer"},
@@ -2633,6 +2695,7 @@
{"BE_OUT", NULL, "HDMI"},
{"BE_OUT", NULL, "MI2S_RX"},
{"PRI_I2S_TX", NULL, "BE_IN"},
+ {"SEC_I2S_TX", NULL, "BE_IN"},
{"MI2S_TX", NULL, "BE_IN"},
{"SLIMBUS_0_TX", NULL, "BE_IN" },
{"SLIMBUS_1_TX", NULL, "BE_IN" },
@@ -2867,6 +2930,10 @@
snd_soc_add_platform_controls(platform,
ec_ref_rx_mixer_controls,
ARRAY_SIZE(ec_ref_rx_mixer_controls));
+
+ snd_soc_add_platform_controls(platform,
+ multi_ch_channel_map_mixer_controls,
+ ARRAY_SIZE(multi_ch_channel_map_mixer_controls));
return 0;
}
diff --git a/sound/soc/msm/msm-pcm-routing.h b/sound/soc/msm/msm-pcm-routing.h
index 6b87475..e11133e 100644
--- a/sound/soc/msm/msm-pcm-routing.h
+++ b/sound/soc/msm/msm-pcm-routing.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -32,6 +32,7 @@
#define LPASS_BE_INCALL_RECORD_RX "INCALL_RECORD_TX"
#define LPASS_BE_INCALL_RECORD_TX "INCALL_RECORD_RX"
#define LPASS_BE_SEC_I2S_RX "SECONDARY_I2S_RX"
+#define LPASS_BE_SEC_I2S_TX "SECONDARY_I2S_TX"
#define LPASS_BE_MI2S_RX "MI2S_RX"
#define LPASS_BE_MI2S_TX "MI2S_TX"
@@ -93,6 +94,7 @@
MSM_BACKEND_DAI_MI2S_RX,
MSM_BACKEND_DAI_MI2S_TX,
MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_BACKEND_DAI_SEC_I2S_TX,
MSM_BACKEND_DAI_SLIMBUS_1_RX,
MSM_BACKEND_DAI_SLIMBUS_1_TX,
MSM_BACKEND_DAI_SLIMBUS_4_RX,
@@ -126,4 +128,6 @@
int compressed_set_volume(unsigned volume);
+void multi_ch_pcm_set_channel_map(char *channel_mapping);
+
#endif /*_MSM_PCM_H*/
diff --git a/sound/soc/msm/msm8930.c b/sound/soc/msm/msm8930.c
index 4725e8e..b3db9e1 100644
--- a/sound/soc/msm/msm8930.c
+++ b/sound/soc/msm/msm8930.c
@@ -621,13 +621,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
- msm8930_slim_0_rx_ch, rx_ch);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
} else {
ret = snd_soc_dai_get_channel_map(codec_dai,
&tx_ch_cnt, tx_ch, &rx_ch_cnt , rx_ch);
@@ -641,14 +634,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai,
- msm8930_slim_0_tx_ch, tx_ch, 0, 0);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
-
}
end:
return ret;
@@ -660,6 +645,14 @@
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ /* Tabla SLIMBUS configuration
+ * RX1, RX2, RX3, RX4, RX5
+ * TX1, TX2, TX3, TX4, TX5
+ */
+ unsigned int rx_ch[SITAR_RX_MAX] = {138, 139, 140, 141, 142};
+ unsigned int tx_ch[SITAR_TX_MAX] = {128, 129, 130, 131, 132};
pr_debug("%s()\n", __func__);
@@ -690,6 +683,9 @@
}
codec_clk = clk_get(cpu_dai->dev, "osr_clk");
+ snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+ tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
+
mbhc_cfg.gpio = 37;
mbhc_cfg.gpio_irq = gpio_to_irq(mbhc_cfg.gpio);
sitar_hs_detect(codec, &mbhc_cfg);
@@ -1311,7 +1307,7 @@
{
int ret;
- if (!cpu_is_msm8930() && !cpu_is_msm8930aa() && !cpu_is_msm8627()) {
+ if (!soc_class_is_msm8930()) {
pr_err("%s: Not the right machine type\n", __func__);
return -ENODEV ;
}
@@ -1350,7 +1346,7 @@
static void __exit msm8930_audio_exit(void)
{
- if (!cpu_is_msm8930() && !cpu_is_msm8930aa() && !cpu_is_msm8627()) {
+ if (!soc_class_is_msm8930()) {
pr_err("%s: Not the right machine type\n", __func__);
return ;
}
diff --git a/sound/soc/msm/msm8960.c b/sound/soc/msm/msm8960.c
index 59d118e..ad78255 100644
--- a/sound/soc/msm/msm8960.c
+++ b/sound/soc/msm/msm8960.c
@@ -777,13 +777,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
- msm8960_slim_0_rx_ch, rx_ch);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
} else {
pr_debug("%s: %s tx_dai_id = %d num_ch = %d\n", __func__,
@@ -801,13 +794,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai,
- msm8960_slim_0_tx_ch, tx_ch, 0, 0);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
}
end:
return ret;
@@ -845,13 +831,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
- num_rx_ch, rx_ch);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
} else {
num_tx_ch = params_channels(params);
@@ -871,13 +850,6 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai,
- num_tx_ch, tx_ch, 0, 0);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
}
end:
return ret;
@@ -896,6 +868,15 @@
.vin_sel = 2,
.inv_int_pol = 0,
};
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ /* Tabla SLIMBUS configuration
+ * RX1, RX2, RX3, RX4, RX5, RX6, RX7
+ * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8
+ */
+ unsigned int rx_ch[TABLA_RX_MAX] = {138, 139, 140, 141, 142, 143, 144};
+ unsigned int tx_ch[TABLA_TX_MAX] = {128, 129, 130, 131, 132, 133, 134,
+ 135, 136, 137};
pr_debug("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
@@ -958,6 +939,8 @@
mbhc_cfg.read_fw_bin = hs_detect_use_firmware;
err = tabla_hs_detect(codec, &mbhc_cfg);
+ snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+ tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
return err;
}
@@ -1018,6 +1001,8 @@
pr_debug("%s channels->min %u channels->max %u ()\n", __func__,
channels->min, channels->max);
+ if (channels->max < 2)
+ channels->min = channels->max = 2;
rate->min = rate->max = 48000;
return 0;
@@ -1760,7 +1745,7 @@
{
int ret;
- if (!cpu_is_msm8960() && !cpu_is_msm8960ab()) {
+ if (!soc_class_is_msm8960()) {
pr_debug("%s: Not the right machine type\n", __func__);
return -ENODEV ;
}
@@ -1833,7 +1818,7 @@
static void __exit msm8960_audio_exit(void)
{
- if (!cpu_is_msm8960() && !cpu_is_msm8960ab()) {
+ if (!soc_class_is_msm8960()) {
pr_debug("%s: Not the right machine type\n", __func__);
return ;
}
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
index f462299..e8ea058 100644
--- a/sound/soc/msm/msm8974.c
+++ b/sound/soc/msm/msm8974.c
@@ -52,10 +52,27 @@
#define GPIO_AUX_PCM_SYNC 45
#define GPIO_AUX_PCM_CLK 46
-#define TABLA_EXT_CLK_RATE 12288000
+#define WCD9XXX_MBHC_DEF_BUTTONS 8
+#define WCD9XXX_MBHC_DEF_RLOADS 5
+#define TAIKO_EXT_CLK_RATE 9600000
-#define TABLA_MBHC_DEF_BUTTONS 8
-#define TABLA_MBHC_DEF_RLOADS 5
+void *def_taiko_mbhc_cal(void);
+static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
+ bool dapm);
+
+static struct wcd9xxx_mbhc_config mbhc_cfg = {
+ .read_fw_bin = false,
+ .calibration = NULL,
+ .micbias = MBHC_MICBIAS2,
+ .mclk_cb_fn = msm_snd_enable_codec_ext_clk,
+ .mclk_rate = TAIKO_EXT_CLK_RATE,
+ .gpio = 0,
+ .gpio_irq = 0,
+ .gpio_level_insert = 1,
+ .detect_extn_cable = true,
+ .insert_detect = true,
+ .swap_gnd_mic = NULL,
+};
struct msm8974_asoc_mach_data {
int mclk_gpio;
@@ -83,9 +100,6 @@
static int msm_btsco_rate = BTSCO_RATE_8KHZ;
static int msm_btsco_ch = 1;
-static struct snd_soc_jack hs_jack;
-static struct snd_soc_jack button_jack;
-
static struct mutex cdc_mclk_mutex;
static struct q_clkdiv *codec_clk;
static int clk_users;
@@ -317,7 +331,7 @@
return 0;
}
-static int msm8974_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
+static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
bool dapm)
{
int ret = 0;
@@ -366,9 +380,9 @@
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- return msm8974_enable_codec_ext_clk(w->codec, 1, true);
+ return msm_snd_enable_codec_ext_clk(w->codec, 1, true);
case SND_SOC_DAPM_POST_PMD:
- return msm8974_enable_codec_ext_clk(w->codec, 0, true);
+ return msm_snd_enable_codec_ext_clk(w->codec, 0, true);
}
return 0;
@@ -524,6 +538,23 @@
return 0;
}
+static int msm8974_hdmi_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s channels->min %u channels->max %u ()\n", __func__,
+ channels->min, channels->max);
+
+ rate->min = rate->max = 48000;
+
+ return 0;
+}
+
static int msm_aux_pcm_get_gpios(void)
{
int ret = 0;
@@ -639,6 +670,18 @@
return 0;
}
+static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ pr_debug("%s()\n", __func__);
+ rate->min = rate->max = 48000;
+
+ return 0;
+}
+
static const struct soc_enum msm_snd_enum[] = {
SOC_ENUM_SINGLE_EXT(2, spk_function),
SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
@@ -660,6 +703,19 @@
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ /* Taiko SLIMBUS configuration
+ * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13
+ * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13
+ * TX14, TX15, TX16
+ */
+ unsigned int rx_ch[TAIKO_RX_MAX] = {144, 145, 146, 147, 148, 149, 150,
+ 151, 152, 153, 154, 155, 156};
+ unsigned int tx_ch[TAIKO_TX_MAX] = {128, 129, 130, 131, 132, 133,
+ 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143};
+
pr_info("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
@@ -685,21 +741,15 @@
snd_soc_dapm_sync(dapm);
- err = snd_soc_jack_new(codec, "Headset Jack",
- (SND_JACK_HEADSET | SND_JACK_OC_HPHL |
- SND_JACK_OC_HPHR | SND_JACK_UNSUPPORTED),
- &hs_jack);
- if (err) {
- pr_err("failed to create new jack\n");
- return err;
- }
+ snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+ tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
- err = snd_soc_jack_new(codec, "Button Jack",
- TAIKO_JACK_BUTTON_MASK, &button_jack);
- if (err) {
- pr_err("failed to create new jack\n");
- return err;
- }
+ /* start mbhc */
+ mbhc_cfg.calibration = def_taiko_mbhc_cal();
+ if (mbhc_cfg.calibration)
+ err = taiko_hs_detect(codec, &mbhc_cfg);
+ else
+ err = -ENOMEM;
return err;
}
@@ -711,6 +761,84 @@
return 0;
}
+void *def_taiko_mbhc_cal(void)
+{
+ void *taiko_cal;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
+ u16 *btn_low, *btn_high;
+ u8 *n_ready, *n_cic, *gain;
+
+ taiko_cal = kzalloc(WCD9XXX_MBHC_CAL_SIZE(WCD9XXX_MBHC_DEF_BUTTONS,
+ WCD9XXX_MBHC_DEF_RLOADS),
+ GFP_KERNEL);
+ if (!taiko_cal) {
+ pr_err("%s: out of memory\n", __func__);
+ return NULL;
+ }
+
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_GENERAL_PTR(taiko_cal)->X) = (Y))
+ S(t_ldoh, 100);
+ S(t_bg_fast_settle, 100);
+ S(t_shutdown_plug_rem, 255);
+ S(mbhc_nsa, 4);
+ S(mbhc_navg, 4);
+#undef S
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_PLUG_DET_PTR(taiko_cal)->X) = (Y))
+ S(mic_current, TAIKO_PID_MIC_5_UA);
+ S(hph_current, TAIKO_PID_MIC_5_UA);
+ S(t_mic_pid, 100);
+ S(t_ins_complete, 250);
+ S(t_ins_retry, 200);
+#undef S
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(taiko_cal)->X) = (Y))
+ S(v_no_mic, 30);
+ S(v_hs_max, 2400);
+#undef S
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_BTN_DET_PTR(taiko_cal)->X) = (Y))
+ S(c[0], 62);
+ S(c[1], 124);
+ S(nc, 1);
+ S(n_meas, 3);
+ S(mbhc_nsc, 11);
+ S(n_btn_meas, 1);
+ S(n_btn_con, 2);
+ S(num_btn, WCD9XXX_MBHC_DEF_BUTTONS);
+ S(v_btn_press_delta_sta, 100);
+ S(v_btn_press_delta_cic, 50);
+#undef S
+ btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(taiko_cal);
+ btn_low = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_V_BTN_LOW);
+ btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg,
+ MBHC_BTN_DET_V_BTN_HIGH);
+ btn_low[0] = -50;
+ btn_high[0] = 34;
+ btn_low[1] = 35;
+ btn_high[1] = 52;
+ btn_low[2] = 53;
+ btn_high[2] = 94;
+ btn_low[3] = 95;
+ btn_high[3] = 133;
+ btn_low[4] = 134;
+ btn_high[4] = 171;
+ btn_low[5] = 172;
+ btn_high[5] = 208;
+ btn_low[6] = 209;
+ btn_high[6] = 244;
+ btn_low[7] = 245;
+ btn_high[7] = 330;
+ n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_N_READY);
+ n_ready[0] = 80;
+ n_ready[1] = 68;
+ n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_N_CIC);
+ n_cic[0] = 60;
+ n_cic[1] = 47;
+ gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_GAIN);
+ gain[0] = 11;
+ gain[1] = 9;
+
+ return taiko_cal;
+}
+
static int msm_snd_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -737,20 +865,8 @@
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
- msm_slim_0_rx_ch, rx_ch);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
} else {
- if (codec_dai->id == 2)
- user_set_tx_ch = msm_slim_0_tx_ch;
- else if (codec_dai->id == 4)
- user_set_tx_ch = params_channels(params);
-
pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__,
codec_dai->name, codec_dai->id, user_set_tx_ch);
@@ -760,19 +876,24 @@
pr_err("%s: failed to get codec chan map\n", __func__);
goto end;
}
+ /* For tabla_tx1 case */
+ if (codec_dai->id == 1)
+ user_set_tx_ch = msm_slim_0_tx_ch;
+ /* For tabla_tx2 case */
+ else if (codec_dai->id == 3)
+ user_set_tx_ch = params_channels(params);
+ else
+ user_set_tx_ch = tx_ch_cnt;
+
+ pr_debug("%s: msm_slim_0_tx_ch(%d)user_set_tx_ch(%d)tx_ch_cnt(%d)\n",
+ __func__, msm_slim_0_tx_ch, user_set_tx_ch, tx_ch_cnt);
+
ret = snd_soc_dai_set_channel_map(cpu_dai,
user_set_tx_ch, tx_ch, 0 , 0);
if (ret < 0) {
pr_err("%s: failed to set cpu chan map\n", __func__);
goto end;
}
- ret = snd_soc_dai_set_channel_map(codec_dai,
- user_set_tx_ch, tx_ch, 0, 0);
- if (ret < 0) {
- pr_err("%s: failed to set codec channel map\n",
- __func__);
- goto end;
- }
}
end:
return ret;
@@ -974,6 +1095,30 @@
.be_id = MSM_BACKEND_DAI_INT_BT_SCO_TX,
.be_hw_params_fixup = msm_btsco_be_hw_params_fixup,
},
+ {
+ .name = LPASS_BE_INT_FM_RX,
+ .stream_name = "Internal FM Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.12292",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INT_FM_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = LPASS_BE_INT_FM_TX,
+ .stream_name = "Internal FM Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.12293",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INT_FM_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ },
/* Backend AFE DAI Links */
{
.name = LPASS_BE_AFE_PCM_RX,
@@ -999,6 +1144,34 @@
.be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
.be_hw_params_fixup = msm_proxy_be_hw_params_fixup,
},
+ /* HDMI Hostless */
+ {
+ .name = "HDMI_RX_HOSTLESS",
+ .stream_name = "HDMI_RX_HOSTLESS",
+ .cpu_dai_name = "HDMI_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ /* HDMI BACK END DAI Link */
+ {
+ .name = LPASS_BE_HDMI,
+ .stream_name = "HDMI Playback",
+ .cpu_dai_name = "msm-dai-q6-hdmi.8",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_HDMI_RX,
+ .be_hw_params_fixup = msm8974_hdmi_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ },
/* AUX PCM Backend DAI Links */
{
.name = LPASS_BE_AUXPCM_RX,
diff --git a/sound/soc/msm/qdsp6/q6asm.c b/sound/soc/msm/qdsp6/q6asm.c
index 6865871..7e70d02 100644
--- a/sound/soc/msm/qdsp6/q6asm.c
+++ b/sound/soc/msm/qdsp6/q6asm.c
@@ -1462,6 +1462,9 @@
case FORMAT_MAT:
open.format = MAT;
break;
+ case FORMAT_MP2:
+ open.format = MP2;
+ break;
default:
pr_err("%s: Invalid format[%d]\n", __func__, format);
goto fail_cmd;
@@ -1555,6 +1558,9 @@
open.format = AMR_WB_PLUS;
pr_debug("q6asm_open_write FORMAT_AMR_WB_PLUS");
break;
+ case FORMAT_MP2:
+ open.format = MP2;
+ break;
default:
pr_err("%s: Invalid format[%d]\n", __func__, format);
goto fail_cmd;
@@ -1642,6 +1648,9 @@
case FORMAT_MP3:
open.write_format = MP3;
break;
+ case FORMAT_MP2:
+ open.write_format = MP2;
+ break;
default:
pr_err("Invalid format[%d]\n", wr_format);
goto fail_cmd;
@@ -2317,7 +2326,7 @@
}
int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
- uint32_t rate, uint32_t channels)
+ uint32_t rate, uint32_t channels, char *channel_map)
{
struct asm_stream_media_format_update fmt;
u8 *channel_mapping;
@@ -2340,39 +2349,7 @@
channel_mapping =
fmt.write_cfg.multi_ch_pcm_cfg.channel_mapping;
- memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
-
- if (channels == 1) {
- channel_mapping[0] = PCM_CHANNEL_FL;
- } else if (channels == 2) {
- channel_mapping[0] = PCM_CHANNEL_FL;
- channel_mapping[1] = PCM_CHANNEL_FR;
- } else if (channels == 4) {
- channel_mapping[0] = PCM_CHANNEL_FL;
- channel_mapping[1] = PCM_CHANNEL_FR;
- channel_mapping[1] = PCM_CHANNEL_LB;
- channel_mapping[1] = PCM_CHANNEL_RB;
- } else if (channels == 6) {
- channel_mapping[0] = PCM_CHANNEL_FC;
- channel_mapping[1] = PCM_CHANNEL_FL;
- channel_mapping[2] = PCM_CHANNEL_FR;
- channel_mapping[3] = PCM_CHANNEL_LB;
- channel_mapping[4] = PCM_CHANNEL_RB;
- channel_mapping[5] = PCM_CHANNEL_LFE;
- } else if (channels == 8) {
- channel_mapping[0] = PCM_CHANNEL_FC;
- channel_mapping[1] = PCM_CHANNEL_FL;
- channel_mapping[2] = PCM_CHANNEL_FR;
- channel_mapping[3] = PCM_CHANNEL_LB;
- channel_mapping[4] = PCM_CHANNEL_RB;
- channel_mapping[5] = PCM_CHANNEL_LFE;
- channel_mapping[6] = PCM_CHANNEL_FLC;
- channel_mapping[7] = PCM_CHANNEL_FRC;
- } else {
- pr_err("%s: ERROR.unsupported num_ch = %u\n", __func__,
- channels);
- return -EINVAL;
- }
+ memcpy(channel_mapping, channel_map, PCM_FORMAT_MAX_NUM_CHANNEL);
rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
if (rc < 0) {
@@ -2574,6 +2551,9 @@
case FORMAT_DTS_LBR:
fmt.format = DTS_LBR;
break;
+ case FORMAT_MP2:
+ fmt.format = MP2;
+ break;
default:
pr_err("Invalid format[%d]\n", format);
goto fail_cmd;
diff --git a/sound/soc/msm/qdsp6v2/Makefile b/sound/soc/msm/qdsp6v2/Makefile
index acb073d..1d11907 100644
--- a/sound/soc/msm/qdsp6v2/Makefile
+++ b/sound/soc/msm/qdsp6v2/Makefile
@@ -1,5 +1,5 @@
snd-soc-qdsp6v2-objs += msm-dai-q6-v2.o msm-pcm-q6-v2.o msm-pcm-routing-v2.o msm-compr-q6-v2.o msm-multi-ch-pcm-q6-v2.o
-snd-soc-qdsp6v2-objs += msm-pcm-lpa-v2.o msm-pcm-afe-v2.o msm-pcm-voip-v2.o msm-pcm-voice-v2.o
+snd-soc-qdsp6v2-objs += msm-pcm-lpa-v2.o msm-pcm-afe-v2.o msm-pcm-voip-v2.o msm-pcm-voice-v2.o msm-dai-q6-hdmi-v2.o
obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o
obj-y += q6adm.o q6afe.o q6asm.o q6audio-v2.o q6voice.o q6core.o
ocmem-audio-objs += audio_ocmem.o
diff --git a/sound/soc/msm/qdsp6v2/audio_ocmem.c b/sound/soc/msm/qdsp6v2/audio_ocmem.c
index 86a82e2..c046b63 100644
--- a/sound/soc/msm/qdsp6v2/audio_ocmem.c
+++ b/sound/soc/msm/qdsp6v2/audio_ocmem.c
@@ -106,9 +106,13 @@
break;
case OCMEM_ALLOC_GROW:
audio_ocmem_lcl.buf = data;
+ pr_debug("%s: Alloc grow request received buf->addr: 0x%ld\n",
+ __func__,
+ (audio_ocmem_lcl.buf)->addr);
atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_GROW);
break;
case OCMEM_ALLOC_SHRINK:
+ pr_debug("%s: Alloc shrink request received\n", __func__);
atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_SHRINK);
break;
default:
@@ -150,11 +154,14 @@
audio_ocmem_lcl.buf = buf;
atomic_set(&audio_ocmem_lcl.audio_exit, 0);
+ atomic_set(&audio_ocmem_lcl.audio_cond, 1);
+ pr_debug("%s: buf->len: %ld\n", __func__, buf->len);
if (!buf->len) {
+ pr_debug("%s: buf.len is 0, waiting for ocmem region\n",
+ __func__);
wait_event_interruptible(audio_ocmem_lcl.audio_wait,
(atomic_read(&audio_ocmem_lcl.audio_cond) == 0) ||
(atomic_read(&audio_ocmem_lcl.audio_exit) == 1));
-
if (atomic_read(&audio_ocmem_lcl.audio_exit)) {
pr_err("%s: audio playback ended while waiting for ocmem\n",
__func__);
@@ -162,6 +169,7 @@
goto fail_cmd;
}
}
+ pr_debug("%s: buf->len: %ld\n", __func__, (audio_ocmem_lcl.buf)->len);
if (audio_ocmem_lcl.lp_memseg_ptr == NULL) {
/* Retrieve low power segments */
ret = core_get_low_power_segments(
@@ -190,19 +198,28 @@
/* vote for ocmem bus bandwidth */
ret = msm_bus_scale_client_update_request(
audio_ocmem_lcl.audio_ocmem_bus_client,
- 0);
+ 1);
if (ret)
pr_err("%s: failed to vote for bus bandwidth\n", __func__);
atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_MAP_TRANSITION);
+ pr_debug("%s: buf->addr: 0x%ld, len: %ld, audio_state[0x%x]\n",
+ __func__,
+ audio_ocmem_lcl.buf->addr,
+ audio_ocmem_lcl.buf->len,
+ atomic_read(&audio_ocmem_lcl.audio_state));
+
+ atomic_set(&audio_ocmem_lcl.audio_cond, 1);
ret = ocmem_map(cid, audio_ocmem_lcl.buf, &audio_ocmem_lcl.mlist);
if (ret) {
pr_err("%s: ocmem_map failed\n", __func__);
goto fail_cmd;
}
-
+ pr_debug("%s: audio_cond[%d] audio_state[0x%x]\n", __func__,
+ atomic_read(&audio_ocmem_lcl.audio_cond),
+ atomic_read(&audio_ocmem_lcl.audio_state));
while ((atomic_read(&audio_ocmem_lcl.audio_state) !=
OCMEM_STATE_EXIT)) {
@@ -219,6 +236,8 @@
atomic_set(&audio_ocmem_lcl.audio_cond, 1);
break;
case OCMEM_STATE_SHRINK:
+ pr_debug("%s: ocmem shrink request process\n",
+ __func__);
atomic_set(&audio_ocmem_lcl.audio_cond, 1);
ret = ocmem_unmap(cid, audio_ocmem_lcl.buf,
&audio_ocmem_lcl.mlist);
@@ -242,9 +261,11 @@
atomic_read(&audio_ocmem_lcl.audio_state));
goto fail_cmd;
}
-
+ atomic_set(&audio_ocmem_lcl.audio_cond, 1);
break;
case OCMEM_STATE_GROW:
+ pr_debug("%s: ocmem grow request process\n",
+ __func__);
atomic_set(&audio_ocmem_lcl.audio_cond, 1);
ret = ocmem_map(cid, audio_ocmem_lcl.buf,
&audio_ocmem_lcl.mlist);
@@ -260,6 +281,7 @@
atomic_read(&audio_ocmem_lcl.audio_cond) == 0);
atomic_set(&audio_ocmem_lcl.audio_state,
OCMEM_STATE_MAP_COMPL);
+ atomic_set(&audio_ocmem_lcl.audio_cond, 1);
break;
}
}
@@ -280,8 +302,10 @@
{
int ret;
+ pr_debug("%s: disable\n", __func__);
if (atomic_read(&audio_ocmem_lcl.audio_cond))
atomic_set(&audio_ocmem_lcl.audio_cond, 0);
+
pr_debug("%s: audio_cond[0x%x], audio_state[0x%x]\n", __func__,
atomic_read(&audio_ocmem_lcl.audio_cond),
atomic_read(&audio_ocmem_lcl.audio_state));
@@ -309,6 +333,7 @@
atomic_read(&audio_ocmem_lcl.audio_state));
goto fail_cmd;
}
+ atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_EXIT);
pr_debug("%s: ocmem_free success\n", __func__);
default:
pr_debug("%s: state=%d", __func__,
@@ -316,6 +341,9 @@
break;
}
+ msm_bus_scale_client_update_request(
+ audio_ocmem_lcl.audio_ocmem_bus_client,
+ 0);
return 0;
fail_cmd:
return ret;
@@ -345,6 +373,7 @@
rc = -EINVAL;
}
+ return;
}
/**
* voice_ocmem_process_req() - disable/enable OCMEM during voice call
@@ -441,6 +470,7 @@
rc = -EINVAL;
}
+ return;
}
/**
@@ -484,86 +514,21 @@
static int audio_ocmem_platform_data_populate(struct platform_device *pdev)
{
- int ret;
- struct msm_bus_scale_pdata *audio_ocmem_bus_scale_pdata = NULL;
- struct msm_bus_vectors *audio_ocmem_bus_vectors = NULL;
- struct msm_bus_paths *ocmem_audio_bus_paths = NULL;
- u32 val;
+ struct msm_bus_scale_pdata *audio_ocmem_adata = NULL;
if (!pdev->dev.of_node) {
pr_err("%s: device tree information missing\n", __func__);
return -ENODEV;
}
-
- audio_ocmem_bus_vectors = kzalloc(sizeof(struct msm_bus_vectors),
- GFP_KERNEL);
- if (!audio_ocmem_bus_vectors) {
- dev_err(&pdev->dev, "Failed to allocate memory for platform data\n");
- return -ENOMEM;
+ audio_ocmem_adata = msm_bus_cl_get_pdata(pdev);
+ if (!audio_ocmem_adata) {
+ pr_err("%s: bus device tree allocation failed\n", __func__);
+ return -EINVAL;
}
- ret = of_property_read_u32(pdev->dev.of_node,
- "qcom,msm-ocmem-audio-src-id", &val);
- if (ret) {
- dev_err(&pdev->dev, "%s: qcom,msm-ocmem-audio-src-id missing in DT node\n",
- __func__);
- goto fail1;
- }
- audio_ocmem_bus_vectors->src = val;
- ret = of_property_read_u32(pdev->dev.of_node,
- "qcom,msm-ocmem-audio-dst-id", &val);
- if (ret) {
- dev_err(&pdev->dev, "%s: qcom,msm-ocmem-audio-dst-id missing in DT node\n",
- __func__);
- goto fail1;
- }
- audio_ocmem_bus_vectors->dst = val;
- ret = of_property_read_u32(pdev->dev.of_node,
- "qcom,msm-ocmem-audio-ab", &val);
- if (ret) {
- dev_err(&pdev->dev, "%s: qcom,msm-ocmem-audio-ab missing in DT node\n",
- __func__);
- goto fail1;
- }
- audio_ocmem_bus_vectors->ab = val;
- ret = of_property_read_u32(pdev->dev.of_node,
- "qcom,msm-ocmem-audio-ib", &val);
- if (ret) {
- dev_err(&pdev->dev, "%s: qcom,msm-ocmem-audio-ib missing in DT node\n",
- __func__);
- goto fail1;
- }
- audio_ocmem_bus_vectors->ib = val;
+ dev_set_drvdata(&pdev->dev, audio_ocmem_adata);
- ocmem_audio_bus_paths = kzalloc(sizeof(struct msm_bus_paths),
- GFP_KERNEL);
- if (!ocmem_audio_bus_paths) {
- dev_err(&pdev->dev, "Failed to allocate memory for platform data\n");
- goto fail1;
- }
- ocmem_audio_bus_paths->num_paths = 1;
- ocmem_audio_bus_paths->vectors = audio_ocmem_bus_vectors;
-
- audio_ocmem_bus_scale_pdata =
- kzalloc(sizeof(struct msm_bus_scale_pdata), GFP_KERNEL);
-
- if (!audio_ocmem_bus_scale_pdata) {
- dev_err(&pdev->dev, "Failed to allocate memory for platform data\n");
- goto fail2;
- }
-
- audio_ocmem_bus_scale_pdata->usecase = ocmem_audio_bus_paths;
- audio_ocmem_bus_scale_pdata->num_usecases = 1;
- audio_ocmem_bus_scale_pdata->name = "audio-ocmem";
-
- dev_set_drvdata(&pdev->dev, audio_ocmem_bus_scale_pdata);
- return ret;
-
-fail2:
- kfree(ocmem_audio_bus_paths);
-fail1:
- kfree(audio_ocmem_bus_vectors);
- return ret;
+ return 0;
}
static int ocmem_audio_client_probe(struct platform_device *pdev)
{
@@ -628,9 +593,7 @@
audio_ocmem_bus_scale_pdata = (struct msm_bus_scale_pdata *)
dev_get_drvdata(&pdev->dev);
- kfree(audio_ocmem_bus_scale_pdata->usecase->vectors);
- kfree(audio_ocmem_bus_scale_pdata->usecase);
- kfree(audio_ocmem_bus_scale_pdata);
+ msm_bus_cl_clear_pdata(audio_ocmem_bus_scale_pdata);
ocmem_notifier_unregister(audio_ocmem_lcl.audio_hdl,
&audio_ocmem_client_nb);
return 0;
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
index bbd43f7..d85bbbc 100644
--- a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
@@ -34,6 +34,7 @@
#include "msm-compr-q6-v2.h"
#include "msm-pcm-routing-v2.h"
+#include "audio_ocmem.h"
#define COMPRE_CAPTURE_NUM_PERIODS 16
/* Allocate the worst case frame size for compressed audio */
@@ -147,17 +148,14 @@
((unsigned int)buf[0].phys
+ (prtd->out_head * prtd->pcm_count)));
- if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
+ if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
time_stamp_flag = SET_TIMESTAMP;
- memcpy(&output_meta_data, (char *)(buf->data +
+ else
+ time_stamp_flag = NO_TIMESTAMP;
+ memcpy(&output_meta_data, (char *)(buf->data +
prtd->out_head * prtd->pcm_count),
COMPRE_OUTPUT_METADATA_SIZE);
- } else {
- time_stamp_flag = NO_TIMESTAMP;
- memset(&output_meta_data, 0,
- COMPRE_OUTPUT_METADATA_SIZE);
- output_meta_data.frame_size = prtd->pcm_count;
- }
+
buffer_length = output_meta_data.frame_size;
pr_debug("meta_data_length: %d, frame_length: %d\n",
output_meta_data.meta_data_length,
@@ -256,17 +254,13 @@
__func__, prtd->out_head,
((unsigned int)buf[0].phys
+ (prtd->out_head * prtd->pcm_count)));
- if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
+ if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
time_stamp_flag = SET_TIMESTAMP;
- memcpy(&output_meta_data, (char *)(buf->data +
+ else
+ time_stamp_flag = NO_TIMESTAMP;
+ memcpy(&output_meta_data, (char *)(buf->data +
prtd->out_head * prtd->pcm_count),
COMPRE_OUTPUT_METADATA_SIZE);
- } else {
- time_stamp_flag = NO_TIMESTAMP;
- memset(&output_meta_data, 0,
- COMPRE_OUTPUT_METADATA_SIZE);
- output_meta_data.frame_size = prtd->pcm_count;
- }
buffer_length = output_meta_data.frame_size;
pr_debug("meta_data_length: %d, frame_length: %d\n",
output_meta_data.meta_data_length,
@@ -440,6 +434,9 @@
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
prtd->pcm_irq_pos = 0;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ audio_ocmem_process_req(AUDIO, true);
+
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
switch (compr->info.codec_param.codec.id) {
case SND_AUDIOCODEC_AMRWB:
@@ -459,6 +456,9 @@
break;
case SNDRV_PCM_TRIGGER_STOP:
pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ audio_ocmem_process_req(AUDIO, false);
+
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
switch (compr->info.codec_param.codec.id) {
case SND_AUDIOCODEC_AMRWB:
@@ -767,7 +767,8 @@
}
}
- ret = q6asm_set_io_mode(prtd->audio_client, ASYNC_IO_MODE);
+ ret = q6asm_set_io_mode(prtd->audio_client,
+ (COMPRESSED_IO | ASYNC_IO_MODE));
if (ret < 0) {
pr_err("%s: Set IO mode failed\n", __func__);
return -ENOMEM;
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
new file mode 100644
index 0000000..f80b589
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
@@ -0,0 +1,320 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/msm-dai-q6-v2.h>
+#include <mach/msm_hdmi_audio.h>
+
+
+enum {
+ STATUS_PORT_STARTED, /* track if AFE port has started */
+ STATUS_MAX
+};
+
+struct msm_dai_q6_hdmi_dai_data {
+ DECLARE_BITMAP(status_mask, STATUS_MAX);
+ u32 rate;
+ u32 channels;
+ union afe_port_config port_config;
+};
+
+static int msm_dai_q6_hdmi_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
+ int value = ucontrol->value.integer.value[0];
+ dai_data->port_config.hdmi_multi_ch.datatype = value;
+ pr_debug("%s: value = %d\n", __func__, value);
+ return 0;
+}
+
+static int msm_dai_q6_hdmi_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
+ ucontrol->value.integer.value[0] =
+ dai_data->port_config.hdmi_multi_ch.datatype;
+ return 0;
+}
+
+
+/* HDMI format field for AFE_PORT_MULTI_CHAN_HDMI_AUDIO_IF_CONFIG command
+ * 0: linear PCM
+ * 1: non-linear PCM
+ */
+static const char * const hdmi_format[] = {
+ "LPCM",
+ "Compr"
+};
+
+static const struct soc_enum hdmi_config_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, hdmi_format),
+};
+
+static const struct snd_kcontrol_new hdmi_config_controls[] = {
+ SOC_ENUM_EXT("HDMI RX Format", hdmi_config_enum[0],
+ msm_dai_q6_hdmi_format_get,
+ msm_dai_q6_hdmi_format_put),
+};
+
+/* Current implementation assumes hw_param is called once
+ * This may not be the case but what to do when ADM and AFE
+ * port are already opened and parameter changes
+ */
+static int msm_dai_q6_hdmi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ u32 channel_allocation = 0;
+ u32 level_shift = 0; /* 0dB */
+ bool down_mix = FALSE;
+
+ dai_data->channels = params_channels(params);
+ dai_data->rate = params_rate(params);
+ dai_data->port_config.hdmi_multi_ch.reserved = 0;
+ dai_data->port_config.hdmi_multi_ch.hdmi_cfg_minor_version = 1;
+ dai_data->port_config.hdmi_multi_ch.sample_rate = dai_data->rate;
+ dai_data->port_config.hdmi_multi_ch.bit_width = 16;
+
+ switch (dai_data->channels) {
+ case 2:
+ channel_allocation = 0;
+ hdmi_msm_audio_info_setup(1, MSM_HDMI_AUDIO_CHANNEL_2,
+ channel_allocation, level_shift, down_mix);
+ dai_data->port_config.hdmi_multi_ch.channel_allocation =
+ channel_allocation;
+ break;
+ case 6:
+ channel_allocation = 0x0B;
+ hdmi_msm_audio_info_setup(1, MSM_HDMI_AUDIO_CHANNEL_6,
+ channel_allocation, level_shift, down_mix);
+ dai_data->port_config.hdmi_multi_ch.channel_allocation =
+ channel_allocation;
+ break;
+ case 8:
+ channel_allocation = 0x1F;
+ hdmi_msm_audio_info_setup(1, MSM_HDMI_AUDIO_CHANNEL_8,
+ channel_allocation, level_shift, down_mix);
+ dai_data->port_config.hdmi_multi_ch.channel_allocation =
+ channel_allocation;
+ break;
+ default:
+ dev_err(dai->dev, "invalid Channels = %u\n",
+ dai_data->channels);
+ return -EINVAL;
+ }
+ dev_dbg(dai->dev, "%s() minor version: %u samplerate: %u bitwidth: %u num_ch = %u channel_allocation = %u datatype = %d\n",
+ __func__,
+ dai_data->port_config.hdmi_multi_ch.hdmi_cfg_minor_version,
+ dai_data->port_config.hdmi_multi_ch.sample_rate,
+ dai_data->port_config.hdmi_multi_ch.bit_width,
+ dai_data->channels,
+ dai_data->port_config.hdmi_multi_ch.channel_allocation,
+ dai_data->port_config.hdmi_multi_ch.datatype);
+
+ return 0;
+}
+
+
+static void msm_dai_q6_hdmi_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc = 0;
+
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ pr_info("%s: afe port not started. dai_data->status_mask = %ld\n",
+ __func__, *dai_data->status_mask);
+ return;
+ }
+
+ rc = afe_close(dai->id); /* can block */
+
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AFE port\n");
+
+ pr_debug("%s: dai_data->status_mask = %ld\n", __func__,
+ *dai_data->status_mask);
+
+ clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+}
+
+
+static int msm_dai_q6_hdmi_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc = 0;
+
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ rc = afe_port_start(dai->id, &dai_data->port_config,
+ dai_data->rate);
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to open AFE port %x\n",
+ dai->id);
+ else
+ set_bit(STATUS_PORT_STARTED,
+ dai_data->status_mask);
+ }
+
+ return rc;
+}
+
+static int msm_dai_q6_hdmi_dai_probe(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data;
+ const struct snd_kcontrol_new *kcontrol;
+ int rc = 0;
+
+ dai_data = kzalloc(sizeof(struct msm_dai_q6_hdmi_dai_data),
+ GFP_KERNEL);
+
+ if (!dai_data) {
+ dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n",
+ dai->id);
+ rc = -ENOMEM;
+ } else
+ dev_set_drvdata(dai->dev, dai_data);
+
+ kcontrol = &hdmi_config_controls[0];
+
+ rc = snd_ctl_add(dai->card->snd_card,
+ snd_ctl_new1(kcontrol, dai_data));
+ return rc;
+}
+
+static int msm_dai_q6_hdmi_dai_remove(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data;
+ int rc;
+
+ dai_data = dev_get_drvdata(dai->dev);
+
+ /* If AFE port is still up, close it */
+ if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ rc = afe_close(dai->id); /* can block */
+
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AFE port\n");
+
+ clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+ }
+ kfree(dai_data);
+ snd_soc_unregister_dai(dai->dev);
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops msm_dai_q6_hdmi_ops = {
+ .prepare = msm_dai_q6_hdmi_prepare,
+ .hw_params = msm_dai_q6_hdmi_hw_params,
+ .shutdown = msm_dai_q6_hdmi_shutdown,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_hdmi_hdmi_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 2,
+ .channels_max = 6,
+ .rate_max = 48000,
+ .rate_min = 48000,
+ },
+ .ops = &msm_dai_q6_hdmi_ops,
+ .probe = msm_dai_q6_hdmi_dai_probe,
+ .remove = msm_dai_q6_hdmi_dai_remove,
+};
+
+
+/* To do: change to register DAIs as batch */
+static __devinit int msm_dai_q6_hdmi_dev_probe(struct platform_device *pdev)
+{
+ int rc, id;
+ const char *q6_dev_id = "qcom,msm-dai-q6-dev-id";
+
+ rc = of_property_read_u32(pdev->dev.of_node, q6_dev_id, &id);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "%s: missing %s in dt node\n", __func__, q6_dev_id);
+ return rc;
+ }
+
+ pdev->id = id;
+ dev_set_name(&pdev->dev, "%s.%d", "msm-dai-q6-hdmi", id);
+
+ pr_debug("%s: dev name %s, id:%d\n", __func__,
+ dev_name(&pdev->dev), pdev->id);
+
+ switch (pdev->id) {
+ case HDMI_RX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_hdmi_hdmi_rx_dai);
+ break;
+ default:
+ dev_err(&pdev->dev, "invalid device ID %d\n", pdev->id);
+ rc = -ENODEV;
+ break;
+ }
+ return rc;
+}
+
+static __devexit int msm_dai_q6_hdmi_dev_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_dai(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id msm_dai_q6_hdmi_dt_match[] = {
+ {.compatible = "qcom,msm-dai-q6-hdmi"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, msm_dai_q6_hdmi_dt_match);
+
+static struct platform_driver msm_dai_q6_hdmi_driver = {
+ .probe = msm_dai_q6_hdmi_dev_probe,
+ .remove = msm_dai_q6_hdmi_dev_remove,
+ .driver = {
+ .name = "msm-dai-q6-hdmi",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_dai_q6_hdmi_dt_match,
+ },
+};
+
+static int __init msm_dai_q6_hdmi_init(void)
+{
+ return platform_driver_register(&msm_dai_q6_hdmi_driver);
+}
+module_init(msm_dai_q6_hdmi_init);
+
+static void __exit msm_dai_q6_hdmi_exit(void)
+{
+ platform_driver_unregister(&msm_dai_q6_hdmi_driver);
+}
+module_exit(msm_dai_q6_hdmi_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MSM DSP HDMI DAI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
index a6cdad2..354dece 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -899,6 +899,36 @@
.remove = msm_dai_q6_dai_remove,
};
+static struct snd_soc_dai_driver msm_dai_q6_fm_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_fm_tx_dai = {
+ .capture = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
static int __devinit msm_auxpcm_dev_probe(struct platform_device *pdev)
{
int id;
@@ -1091,7 +1121,7 @@
SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rate_min = 8000,
.rate_max = 48000,
},
@@ -1106,7 +1136,7 @@
SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rate_min = 8000,
.rate_max = 48000,
},
@@ -1158,6 +1188,12 @@
rc = snd_soc_register_dai(&pdev->dev,
&msm_dai_q6_bt_sco_tx_dai);
break;
+ case INT_FM_RX:
+ rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_fm_rx_dai);
+ break;
+ case INT_FM_TX:
+ rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_fm_tx_dai);
+ break;
case RT_PROXY_DAI_001_RX:
case RT_PROXY_DAI_002_RX:
rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_afe_rx_dai);
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
index 7483bb6..af1e19c 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
@@ -56,7 +56,7 @@
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 4,
.buffer_bytes_max = CAPTURE_NUM_PERIODS * CAPTURE_PERIOD_SIZE,
.period_bytes_min = CAPTURE_PERIOD_SIZE,
.period_bytes_max = CAPTURE_PERIOD_SIZE,
@@ -297,7 +297,6 @@
static int msm_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
struct msm_audio *prtd;
int ret = 0;
@@ -315,36 +314,14 @@
kfree(prtd);
return -ENOMEM;
}
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- runtime->hw = msm_pcm_hardware_playback;
- ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM);
- if (ret < 0) {
- pr_err("%s: pcm out open failed\n", __func__);
- q6asm_audio_client_free(prtd->audio_client);
- kfree(prtd);
- return -ENOMEM;
- }
- }
- /* Capture path */
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- runtime->hw = msm_pcm_hardware_capture;
- ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM);
- if (ret < 0) {
- pr_err("%s: pcm in open failed\n", __func__);
- q6asm_audio_client_free(prtd->audio_client);
- kfree(prtd);
- return -ENOMEM;
- }
- }
-
- pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
-
- prtd->session_id = prtd->audio_client->session;
- msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
- prtd->session_id, substream->stream);
-
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- prtd->cmd_ack = 1;
+ runtime->hw = msm_pcm_hardware_playback;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ runtime->hw = msm_pcm_hardware_capture;
+ else {
+ pr_err("Invalid Stream type %d\n", substream->stream);
+ return -EINVAL;
+ }
ret = snd_pcm_hw_constraint_list(runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
@@ -530,12 +507,15 @@
int dir = OUT;
pr_debug("%s\n", __func__);
- q6asm_cmd(prtd->audio_client, CMD_CLOSE);
- q6asm_audio_client_buf_free_contiguous(dir,
+ if (prtd->audio_client) {
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ q6asm_audio_client_buf_free_contiguous(dir,
prtd->audio_client);
+ q6asm_audio_client_free(prtd->audio_client);
+ }
+
msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
- SNDRV_PCM_STREAM_CAPTURE);
- q6asm_audio_client_free(prtd->audio_client);
+ SNDRV_PCM_STREAM_CAPTURE);
kfree(prtd);
return 0;
@@ -617,13 +597,43 @@
struct snd_pcm_runtime *runtime = substream->runtime;
struct msm_audio *prtd = runtime->private_data;
struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
struct audio_buffer *buf;
int dir, ret;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
dir = IN;
- else
+ pr_debug("%s Opening %d-ch PCM Write stream\n",
+ __func__, params_channels(params));
+
+ ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM);
+ if (ret < 0) {
+ pr_err("%s: pcm out open failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ } else {
dir = OUT;
+ pr_debug("%s Opening %d-ch PCM read stream\n",
+ __func__, params_channels(params));
+ ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM);
+ if (ret < 0) {
+ pr_err("%s: pcm in open failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ prtd->audio_client = NULL;
+ return -ENOMEM;
+ }
+ }
+
+ pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
+ prtd->session_id = prtd->audio_client->session;
+ msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
+ prtd->session_id, substream->stream);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ prtd->cmd_ack = 1;
+
pr_debug("%s: before buf alloc\n", __func__);
ret = q6asm_audio_client_buf_alloc_contiguous(dir,
prtd->audio_client,
diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c
index 62257b4..6acc136 100644
--- a/sound/soc/msm/qdsp6v2/q6adm.c
+++ b/sound/soc/msm/qdsp6v2/q6adm.c
@@ -51,6 +51,24 @@
static struct adm_ctl this_adm;
+static void adm_callback_debug_print(struct apr_client_data *data)
+{
+ uint32_t *payload;
+ payload = data->payload;
+
+ if (data->payload_size >= 8)
+ pr_debug("%s: code = 0x%x PL#0[%x], PL#1[%x], size = %d\n",
+ __func__, data->opcode, payload[0], payload[1],
+ data->payload_size);
+ else if (data->payload_size >= 4)
+ pr_debug("%s: code = 0x%x PL#0[%x], size = %d\n",
+ __func__, data->opcode, payload[0],
+ data->payload_size);
+ else
+ pr_debug("%s: code = 0x%x, size = %d\n",
+ __func__, data->opcode, data->payload_size);
+}
+
static int32_t adm_callback(struct apr_client_data *data, void *priv)
{
uint32_t *payload;
@@ -86,10 +104,7 @@
return 0;
}
- pr_debug("%s: code = 0x%x PL#0[%x], PL#1[%x], size = %d\n", __func__,
- data->opcode, payload[0], payload[1],
- data->payload_size);
-
+ adm_callback_debug_print(data);
if (data->payload_size) {
index = q6audio_get_port_index(data->token);
if (index < 0 || index >= Q6_AFE_MAX_PORTS) {
@@ -99,6 +114,10 @@
}
if (data->opcode == APR_BASIC_RSP_RESULT) {
pr_debug("APR_BASIC_RSP_RESULT id %x\n", payload[0]);
+ if (payload[1] != 0) {
+ pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
+ __func__, payload[0], payload[1]);
+ }
switch (payload[0]) {
case ADM_CMD_SET_PP_PARAMS_V5:
if (rtac_make_adm_callback(
@@ -116,8 +135,18 @@
wake_up(&this_adm.wait[index]);
break;
case ADM_CMD_SHARED_MEM_MAP_REGIONS:
- /* Block until memory handle comes back */
- /* via ADM_CMDRSP_SHARED_MEM_MAP_REGIONS */
+ pr_debug("%s: ADM_CMD_SHARED_MEM_MAP_REGIONS\n",
+ __func__);
+ /* Should only come here if there is an APR */
+ /* error or malformed APR packet. Otherwise */
+ /* response will be returned as */
+ /* ADM_CMDRSP_SHARED_MEM_MAP_REGIONS */
+ if (payload[1] != 0) {
+ pr_err("%s: ADM map error, resuming\n",
+ __func__);
+ atomic_set(&this_adm.copp_stat[0], 1);
+ wake_up(&this_adm.wait[index]);
+ }
break;
default:
pr_err("%s: Unknown Cmd: 0x%x\n", __func__,
@@ -247,24 +276,27 @@
get_audproc_cal(acdb_path, &aud_cal);
/* map & cache buffers used */
+ atomic_set(&mem_map_index, acdb_path);
if (((mem_addr_audproc[acdb_path].cal_paddr != aud_cal.cal_paddr) &&
(aud_cal.cal_size > 0)) ||
(aud_cal.cal_size > mem_addr_audproc[acdb_path].cal_size)) {
- atomic_set(&mem_map_index, acdb_path);
if (mem_addr_audproc[acdb_path].cal_paddr != 0)
adm_memory_unmap_regions(port_id,
&mem_addr_audproc[acdb_path].cal_paddr,
&size, 1);
result = adm_memory_map_regions(port_id, &aud_cal.cal_paddr,
- 0, &aud_cal.cal_size, 1);
- if (result < 0)
+ 0, &size, 1);
+ if (result < 0) {
pr_err("ADM audproc mmap did not work! path = %d, addr = 0x%x, size = %d\n",
acdb_path, aud_cal.cal_paddr,
aud_cal.cal_size);
- else
- mem_addr_audproc[acdb_path] = aud_cal;
+ } else {
+ mem_addr_audproc[acdb_path].cal_paddr =
+ aud_cal.cal_paddr;
+ mem_addr_audproc[acdb_path].cal_size = size;
+ }
}
if (!send_adm_cal_block(port_id, &aud_cal))
@@ -278,24 +310,27 @@
get_audvol_cal(acdb_path, &aud_cal);
/* map & cache buffers used */
+ atomic_set(&mem_map_index, (acdb_path + MAX_AUDPROC_TYPES));
if (((mem_addr_audvol[acdb_path].cal_paddr != aud_cal.cal_paddr) &&
(aud_cal.cal_size > 0)) ||
(aud_cal.cal_size > mem_addr_audvol[acdb_path].cal_size)) {
- atomic_set(&mem_map_index, (acdb_path + MAX_AUDPROC_TYPES));
if (mem_addr_audvol[acdb_path].cal_paddr != 0)
adm_memory_unmap_regions(port_id,
&mem_addr_audvol[acdb_path].cal_paddr,
&size, 1);
result = adm_memory_map_regions(port_id, &aud_cal.cal_paddr,
- 0, &aud_cal.cal_size, 1);
- if (result < 0)
+ 0, &size, 1);
+ if (result < 0) {
pr_err("ADM audvol mmap did not work! path = %d, addr = 0x%x, size = %d\n",
acdb_path, aud_cal.cal_paddr,
aud_cal.cal_size);
- else
- mem_addr_audvol[acdb_path] = aud_cal;
+ } else {
+ mem_addr_audvol[acdb_path].cal_paddr =
+ aud_cal.cal_paddr;
+ mem_addr_audvol[acdb_path].cal_size = size;
+ }
}
if (!send_adm_cal_block(port_id, &aud_cal))
@@ -454,6 +489,21 @@
} else if (channel_mode == 2) {
open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ } else if (channel_mode == 3) {
+ open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open.dev_channel_mapping[0] = PCM_CHANNEL_FR;
+ open.dev_channel_mapping[1] = PCM_CHANNEL_FC;
+ } else if (channel_mode == 4) {
+ open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ open.dev_channel_mapping[2] = PCM_CHANNEL_RB;
+ open.dev_channel_mapping[3] = PCM_CHANNEL_LB;
+ } else if (channel_mode == 5) {
+ open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ open.dev_channel_mapping[2] = PCM_CHANNEL_FC;
+ open.dev_channel_mapping[3] = PCM_CHANNEL_LB;
+ open.dev_channel_mapping[4] = PCM_CHANNEL_RB;
} else if (channel_mode == 6) {
open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
@@ -461,6 +511,15 @@
open.dev_channel_mapping[3] = PCM_CHANNEL_FC;
open.dev_channel_mapping[4] = PCM_CHANNEL_LB;
open.dev_channel_mapping[5] = PCM_CHANNEL_RB;
+ } else if (channel_mode == 8) {
+ open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ open.dev_channel_mapping[2] = PCM_CHANNEL_LFE;
+ open.dev_channel_mapping[3] = PCM_CHANNEL_FC;
+ open.dev_channel_mapping[4] = PCM_CHANNEL_LB;
+ open.dev_channel_mapping[5] = PCM_CHANNEL_RB;
+ open.dev_channel_mapping[6] = PCM_CHANNEL_FLC;
+ open.dev_channel_mapping[7] = PCM_CHANNEL_FRC;
} else {
pr_err("%s invalid num_chan %d\n", __func__,
channel_mode);
@@ -664,8 +723,8 @@
APR_PKT_VER);
mmap_regions->hdr.pkt_size = cmd_size;
mmap_regions->hdr.src_port = 0;
- mmap_regions->hdr.dest_port = 0;
- mmap_regions->hdr.token = 0;
+ mmap_regions->hdr.dest_port = atomic_read(&this_adm.copp_id[index]);
+ mmap_regions->hdr.token = port_id;
mmap_regions->hdr.opcode = ADM_CMD_SHARED_MEM_MAP_REGIONS;
mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL & 0x00ff;
mmap_regions->num_regions = bufcnt & 0x00ff;
@@ -733,8 +792,8 @@
APR_PKT_VER);
unmap_regions.hdr.pkt_size = cmd_size;
unmap_regions.hdr.src_port = 0;
- unmap_regions.hdr.dest_port = 0;
- unmap_regions.hdr.token = 0;
+ unmap_regions.hdr.dest_port = atomic_read(&this_adm.copp_id[index]);
+ unmap_regions.hdr.token = port_id;
unmap_regions.hdr.opcode = ADM_CMD_SHARED_MEM_UNMAP_REGIONS;
unmap_regions.mem_map_handle = atomic_read(&mem_map_handles[
atomic_read(&mem_map_index)]);
diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c
index f0465a5..4819e0a 100644
--- a/sound/soc/msm/qdsp6v2/q6afe.c
+++ b/sound/soc/msm/qdsp6v2/q6afe.c
@@ -344,6 +344,8 @@
break;
case INT_BT_SCO_RX:
case INT_BT_SCO_TX:
+ case INT_FM_RX:
+ case INT_FM_TX:
cfg_type = AFE_PARAM_ID_INTERNAL_BT_FM_CONFIG;
break;
default:
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index 9f98c1b..875bf47 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -476,9 +476,13 @@
return;
}
-int q6asm_set_io_mode(struct audio_client *ac, uint32_t mode)
+int q6asm_set_io_mode(struct audio_client *ac, uint32_t mode1)
{
+ uint32_t mode;
+
ac->io_mode &= 0xFF00;
+ mode = (mode1 & 0xF);
+
pr_debug("%s ac->mode after anding with FF00:0x[%x],\n",
__func__, ac->io_mode);
if (ac == NULL) {
@@ -486,7 +490,7 @@
return -EINVAL;
}
if ((mode == ASYNC_IO_MODE) || (mode == SYNC_IO_MODE)) {
- ac->io_mode |= mode;
+ ac->io_mode |= mode1;
pr_debug("%s:Set Mode to 0x[%x]\n", __func__, ac->io_mode);
return 0;
} else {
@@ -819,6 +823,10 @@
}
sid = (data->token >> 8) & 0x0F;
ac = q6asm_get_audio_client(sid);
+ if (!ac) {
+ pr_debug("%s: session[%d] already freed\n", __func__, sid);
+ return 0;
+ }
pr_debug("%s:ptr0[0x%x]ptr1[0x%x]opcode[0x%x] token[0x%x]payload_s[%d] src[%d] dest[%d]sid[%d]dir[%d]\n",
__func__, payload[0], payload[1], data->opcode, data->token,
data->payload_size, data->src_port, data->dest_port, sid, dir);
@@ -1689,7 +1697,7 @@
enc_cfg.bits_per_sample = 16;
enc_cfg.sample_rate = rate;
enc_cfg.is_signed = 1;
- channel_mapping = enc_cfg.channel_mapping; /* ??? PHANI */
+ channel_mapping = enc_cfg.channel_mapping;
memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
@@ -1739,7 +1747,8 @@
enc_cfg.bits_per_sample = 16;
enc_cfg.sample_rate = 0;/*rate;*/
enc_cfg.is_signed = 1;
- channel_mapping = enc_cfg.channel_mapping; /* ??? PHANI */
+ channel_mapping = enc_cfg.channel_mapping;
+
memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
@@ -1774,27 +1783,36 @@
lchannel_mapping[0] = PCM_CHANNEL_FL;
lchannel_mapping[1] = PCM_CHANNEL_FR;
} else if (channels == 3) {
- lchannel_mapping[0] = PCM_CHANNEL_FC;
- lchannel_mapping[1] = PCM_CHANNEL_FL;
- lchannel_mapping[2] = PCM_CHANNEL_FR;
+ lchannel_mapping[0] = PCM_CHANNEL_FL;
+ lchannel_mapping[1] = PCM_CHANNEL_FR;
+ lchannel_mapping[2] = PCM_CHANNEL_FC;
} else if (channels == 4) {
- lchannel_mapping[0] = PCM_CHANNEL_FC;
- lchannel_mapping[1] = PCM_CHANNEL_FL;
- lchannel_mapping[2] = PCM_CHANNEL_FR;
+ lchannel_mapping[0] = PCM_CHANNEL_FL;
+ lchannel_mapping[1] = PCM_CHANNEL_FR;
+ lchannel_mapping[2] = PCM_CHANNEL_RB;
lchannel_mapping[3] = PCM_CHANNEL_LB;
} else if (channels == 5) {
- lchannel_mapping[0] = PCM_CHANNEL_FC;
- lchannel_mapping[1] = PCM_CHANNEL_FL;
- lchannel_mapping[2] = PCM_CHANNEL_FR;
+ lchannel_mapping[0] = PCM_CHANNEL_FL;
+ lchannel_mapping[1] = PCM_CHANNEL_FR;
+ lchannel_mapping[2] = PCM_CHANNEL_FC;
lchannel_mapping[3] = PCM_CHANNEL_LB;
lchannel_mapping[4] = PCM_CHANNEL_RB;
} else if (channels == 6) {
- lchannel_mapping[0] = PCM_CHANNEL_FC;
- lchannel_mapping[1] = PCM_CHANNEL_FL;
- lchannel_mapping[2] = PCM_CHANNEL_FR;
- lchannel_mapping[3] = PCM_CHANNEL_LB;
- lchannel_mapping[4] = PCM_CHANNEL_RB;
- lchannel_mapping[5] = PCM_CHANNEL_LFE;
+ lchannel_mapping[0] = PCM_CHANNEL_FL;
+ lchannel_mapping[1] = PCM_CHANNEL_FR;
+ lchannel_mapping[2] = PCM_CHANNEL_LFE;
+ lchannel_mapping[3] = PCM_CHANNEL_FC;
+ lchannel_mapping[4] = PCM_CHANNEL_LB;
+ lchannel_mapping[5] = PCM_CHANNEL_RB;
+ } else if (channels == 6) {
+ lchannel_mapping[0] = PCM_CHANNEL_FL;
+ lchannel_mapping[1] = PCM_CHANNEL_FR;
+ lchannel_mapping[2] = PCM_CHANNEL_LFE;
+ lchannel_mapping[3] = PCM_CHANNEL_FC;
+ lchannel_mapping[4] = PCM_CHANNEL_LB;
+ lchannel_mapping[5] = PCM_CHANNEL_RB;
+ lchannel_mapping[6] = PCM_CHANNEL_FLC;
+ lchannel_mapping[7] = PCM_CHANNEL_FRC;
} else {
pr_err("%s: ERROR.unsupported num_ch = %u\n",
__func__, channels);
@@ -2947,6 +2965,7 @@
struct audio_port_data *port;
u32 lbuf_addr_lsw;
u32 liomode;
+ u32 io_compressed;
if (!ac || ac->apr == NULL) {
pr_err("%s: APR handle NULL\n", __func__);
@@ -2967,9 +2986,12 @@
write.timestamp_msw = param->msw_ts;
write.timestamp_lsw = param->lsw_ts;
liomode = (ASYNC_IO_MODE | NT_MODE);
+ io_compressed = (ASYNC_IO_MODE | COMPRESSED_IO);
if (ac->io_mode == liomode)
lbuf_addr_lsw = (write.buf_addr_lsw - 32);
+ else if (ac->io_mode == io_compressed)
+ lbuf_addr_lsw = (write.buf_addr_lsw - 0x40);
else
lbuf_addr_lsw = write.buf_addr_lsw;
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index f989b17..dc851ce 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -2582,6 +2582,7 @@
rtd->ops.silence = platform->driver->ops->silence;
rtd->ops.page = platform->driver->ops->page;
rtd->ops.mmap = platform->driver->ops->mmap;
+ rtd->ops.restart = platform->driver->ops->restart;
}
if (playback)