summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/arm/coresight.txt3
-rw-r--r--Documentation/devicetree/bindings/fb/mdss-dp.txt19
-rw-r--r--Documentation/devicetree/bindings/gpu/adreno.txt5
-rw-r--r--Documentation/devicetree/bindings/platform/msm/ipa.txt8
-rw-r--r--Documentation/devicetree/bindings/usb/dwc3.txt9
-rw-r--r--Documentation/devicetree/bindings/usb/msm-phy.txt40
-rw-r--r--Documentation/devicetree/bindings/usb/msm-ssusb.txt21
-rw-r--r--arch/arm/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi80
-rw-r--r--arch/arm/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi80
-rw-r--r--arch/arm/boot/dts/qcom/msm-gdsc-cobalt.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-gpu.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-mtp.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/msm8996.dtsi17
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-cdp.dts15
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-coresight.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-mtp.dts15
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi8
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt.dtsi19
-rw-r--r--arch/arm64/configs/msmcortex-perf_defconfig19
-rw-r--r--arch/arm64/configs/msmcortex_defconfig1
-rw-r--r--arch/arm64/kernel/perf_event.c2
-rw-r--r--arch/um/configs/x86_64_defconfig2
-rw-r--r--drivers/clk/msm/clock-gcc-cobalt.c31
-rw-r--r--drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c2
-rw-r--r--drivers/clk/msm/vdd-level-cobalt.h10
-rw-r--r--drivers/clk/qcom/mdss/Kconfig6
-rw-r--r--drivers/clk/qcom/mdss/Makefile5
-rw-r--r--drivers/clk/qcom/mdss/mdss-dsi-pll-8996-util.c1137
-rw-r--r--drivers/clk/qcom/mdss/mdss-dsi-pll-8996.c566
-rw-r--r--drivers/clk/qcom/mdss/mdss-dsi-pll-8996.h221
-rw-r--r--drivers/clk/qcom/mdss/mdss-dsi-pll.h110
-rw-r--r--drivers/clk/qcom/mdss/mdss-hdmi-pll-8996.c2686
-rw-r--r--drivers/clk/qcom/mdss/mdss-hdmi-pll.h61
-rw-r--r--drivers/clk/qcom/mdss/mdss-pll-util.c438
-rw-r--r--drivers/clk/qcom/mdss/mdss-pll.c437
-rw-r--r--drivers/clk/qcom/mdss/mdss-pll.h233
-rw-r--r--drivers/crypto/Kconfig8
-rw-r--r--drivers/crypto/msm/qce50.c113
-rw-r--r--drivers/crypto/msm/qce50.h3
-rw-r--r--drivers/crypto/msm/qcrypto.c303
-rw-r--r--drivers/gpu/msm/adreno.c24
-rw-r--r--drivers/gpu/msm/adreno_compat.c24
-rw-r--r--drivers/gpu/msm/kgsl_iommu.c49
-rw-r--r--drivers/gpu/msm/kgsl_mmu.c11
-rw-r--r--drivers/gpu/msm/kgsl_mmu.h3
-rw-r--r--drivers/hwmon/qpnp-adc-common.c270
-rw-r--r--drivers/hwmon/qpnp-adc-voltage.c6
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x.c18
-rw-r--r--drivers/hwtracing/coresight/coresight-tmc.c6
-rw-r--r--drivers/input/touchscreen/Kconfig13
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/ft5x06_ts.c654
-rw-r--r--drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.c6
-rw-r--r--drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.h4
-rw-r--r--drivers/misc/qseecom.c73
-rw-r--r--drivers/misc/uid_stat.c105
-rw-r--r--drivers/platform/msm/ipa/ipa_clients/ipa_usb.c89
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa.c26
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa.c65
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_client.c77
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_dp.c13
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_i.h3
-rw-r--r--drivers/power/qcom-charger/fg-util.c10
-rw-r--r--drivers/power/qcom-charger/qpnp-smb2.c79
-rw-r--r--drivers/power/qcom-charger/smb-lib.c84
-rw-r--r--drivers/power/qcom-charger/smb-lib.h2
-rw-r--r--drivers/scsi/ufs/ufshcd.c3
-rw-r--r--drivers/slimbus/slim-msm-ngd.c10
-rw-r--r--drivers/slimbus/slim-msm.h3
-rw-r--r--drivers/soc/qcom/Makefile2
-rw-r--r--drivers/soc/qcom/service-locator.c215
-rw-r--r--drivers/thermal/msm-tsens.c11
-rw-r--r--drivers/thermal/msm_thermal.c5
-rw-r--r--drivers/thermal/qpnp-adc-tm.c407
-rw-r--r--drivers/usb/dwc3/dwc3-msm.c77
-rw-r--r--drivers/usb/dwc3/gadget.c2
-rw-r--r--drivers/usb/gadget/function/f_gsi.c14
-rw-r--r--drivers/usb/phy/phy-msm-qusb-v2.c158
-rw-r--r--drivers/usb/phy/phy-msm-qusb.c66
-rw-r--r--drivers/usb/phy/phy-msm-ssusb-qmp.c17
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.c678
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.h114
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_aux.c104
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.c94
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.h18
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.h4
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_ctl.c2
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_layer.c49
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_overlay.c3
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp.c46
-rw-r--r--include/dt-bindings/clock/msm-clocks-cobalt.h1
-rw-r--r--include/dt-bindings/clock/msm-clocks-hwio-cobalt.h1
-rw-r--r--include/linux/input/ft5x06_ts.h31
-rw-r--r--include/linux/qpnp/qpnp-adc.h3
-rw-r--r--include/linux/usb/msm_hsusb.h2
-rw-r--r--include/net/cfg80211.h28
-rw-r--r--include/soc/qcom/service-locator.h23
-rw-r--r--include/sound/cpe_cmi.h19
-rw-r--r--include/sound/cpe_core.h13
-rw-r--r--include/uapi/linux/msm_kgsl.h6
-rw-r--r--include/uapi/linux/msm_mdp.h9
-rw-r--r--include/uapi/linux/nl80211.h30
-rw-r--r--include/uapi/media/msmb_isp.h1
-rw-r--r--kernel/drivers/input/touchscreen/synaptics_fw_update.c521
-rw-r--r--kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.c282
-rw-r--r--kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.h6
-rw-r--r--net/activity_stats.c39
-rw-r--r--net/wireless/core.c30
-rw-r--r--net/wireless/nl80211.c43
-rw-r--r--sound/core/control.c2
-rw-r--r--sound/core/pcm.c30
-rwxr-xr-xsound/soc/codecs/audio-ext-clk.c7
-rw-r--r--sound/soc/codecs/wcd_cpe_core.c147
-rw-r--r--sound/soc/msm/qdsp6v2/audio_calibration.c10
-rw-r--r--sound/usb/card.c1
115 files changed, 10436 insertions, 1404 deletions
diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt
index 3435d138955c..45d052ad0ada 100644
--- a/Documentation/devicetree/bindings/arm/coresight.txt
+++ b/Documentation/devicetree/bindings/arm/coresight.txt
@@ -80,6 +80,9 @@ its hardware characteristcs.
* qcom,force-reg-dump: enables TMC reg dump support
+ * arm,sg-enable : indicates whether scatter gather feature is enabled
+ by default for TMC ETR configuration.
+
* Required property for TPDAs:
* qcom,tpda-atid: must be present. Specifies the ATID for TPDA.
diff --git a/Documentation/devicetree/bindings/fb/mdss-dp.txt b/Documentation/devicetree/bindings/fb/mdss-dp.txt
index 0b65b776645b..85656e312acc 100644
--- a/Documentation/devicetree/bindings/fb/mdss-dp.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-dp.txt
@@ -24,6 +24,9 @@ Required properties
- clocks: List of Phandles for clock device nodes
needed by the device.
- clock-names: List of clock names needed by the device.
+- qcom,aux-en-gpio: Specifies the aux-channel enable gpio.
+- qcom,aux-sel-gpio: Specifies the aux-channel select gpio.
+- qcom,usbplug-cc-gpio: Specifies the usbplug orientation gpio.
Optional properties:
- qcom,<type>-supply-entries: A node that lists the elements of the supply used by the
@@ -42,6 +45,12 @@ Optional properties:
-- qcom,supply-post-on-sleep: time to sleep (ms) after turning on
-- qcom,supply-pre-off-sleep: time to sleep (ms) before turning off
-- qcom,supply-post-off-sleep: time to sleep (ms) after turning off
+- qcom,hpd-gpio: Specifies the HPD gpio.
+- pinctrl-names: List of names to assign mdss pin states defined in pinctrl device node
+ Refer to pinctrl-bindings.txt
+- pinctrl-<0..n>: Lists phandles each pointing to the pin configuration node within a pin
+ controller. These pin configurations are installed in the pinctrl
+ device node. Refer to pinctrl-bindings.txt
Example:
mdss_dp_ctrl: qcom,dp_ctrl@c990000 {
@@ -115,5 +124,15 @@ Example:
qcom,supply-disable-load = <32>;
};
};
+
+ pinctrl-names = "mdss_dp_active", "mdss_dp_sleep";
+ pinctrl-0 = <&mdss_dp_aux_active &mdss_dp_usbplug_cc_active
+ &mdss_dp_hpd_active>;
+ pinctrl-1 = <&mdss_dp_aux_suspend &mdss_dp_usbplug_cc_suspend
+ &mdss_dp_hpd_suspend>;
+ qcom,aux-en-gpio = <&tlmm 77 0>;
+ qcom,aux-sel-gpio = <&tlmm 78 0>;
+ qcom,usbplug-cc-gpio = <&tlmm 38 0>;
+ qcom,hpd-gpio = <&tlmm 34 0>;
};
diff --git a/Documentation/devicetree/bindings/gpu/adreno.txt b/Documentation/devicetree/bindings/gpu/adreno.txt
index ce2e38b905a1..06b8c71effcc 100644
--- a/Documentation/devicetree/bindings/gpu/adreno.txt
+++ b/Documentation/devicetree/bindings/gpu/adreno.txt
@@ -138,6 +138,11 @@ Optional Properties:
Specify the size of snapshot in bytes. This will override
snapshot size defined in the driver code.
+- qcom,gpu-qdss-stm:
+ <baseAddr size>
+ baseAddr - base address of the gpu channels in the qdss stm memory region
+ size - size of the gpu stm region
+
GPU Quirks:
- qcom,gpu-quirk-two-pass-use-wfi:
Signal the GPU to set Set TWOPASSUSEWFI bit in
diff --git a/Documentation/devicetree/bindings/platform/msm/ipa.txt b/Documentation/devicetree/bindings/platform/msm/ipa.txt
index 222b4fe66697..e09f12737ed9 100644
--- a/Documentation/devicetree/bindings/platform/msm/ipa.txt
+++ b/Documentation/devicetree/bindings/platform/msm/ipa.txt
@@ -119,6 +119,9 @@ IPA SMMU sub nodes
- qcom,iova-mapping: specifies the start address and size of iova space.
+- qcom,additional-mapping: specifies any addtional mapping needed for this
+ context bank. The format is <iova pa size>
+
IPA SMP2P sub nodes
-compatible: "qcom,smp2pgpio-map-ipa-1-out" - represents the out gpio from
@@ -195,7 +198,10 @@ qcom,ipa@fd4c0000 {
ipa_smmu_ap: ipa_smmu_ap {
compatible = "qcom,ipa-smmu-ap-cb";
iommus = <&anoc2_smmu 0x30>;
- qcom,iova-mapping = <0x10000000 0x40000000>;
+ qcom,iova-mapping = <0x20000000 0x40000000>;
+ qcom,additional-mapping =
+ /* modem tables in IMEM */
+ <0x146BD000 0x146BD000 0x2000>;
};
ipa_smmu_wlan: ipa_smmu_wlan {
diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt
index a38104faf261..3136687adb57 100644
--- a/Documentation/devicetree/bindings/usb/dwc3.txt
+++ b/Documentation/devicetree/bindings/usb/dwc3.txt
@@ -6,6 +6,10 @@ DWC3- USB3 CONTROLLER. Complies to the generic USB binding properties
Required properties:
- compatible: must be "snps,dwc3"
- reg : Address and length of the register set for the device
+ Required regs are:
+ - "core_base" : USB DWC3 controller register set.
+ - "ahb2phy_base" : AHB2PHY register base. It is used to update read/write
+ wait cycle for accessing PHY.
- interrupts: Interrupts used by the dwc3 controller.
Optional properties:
@@ -61,7 +65,10 @@ This is usually a subnode to DWC3 glue to which it is connected.
dwc3@4a030000 {
compatible = "snps,dwc3";
- reg = <0x4a030000 0xcfff>;
+ reg = <0x07600000 0xfc000>,
+ <0x7416000 0x400>;
+ reg-names = "core_base",
+ "ahb2phy_base";
interrupts = <0 92 4>
usb-phy = <&usb2_phy>, <&usb3,phy>;
tx-fifo-resize;
diff --git a/Documentation/devicetree/bindings/usb/msm-phy.txt b/Documentation/devicetree/bindings/usb/msm-phy.txt
index 35bb94b2ef70..dd9c13b4b5ff 100644
--- a/Documentation/devicetree/bindings/usb/msm-phy.txt
+++ b/Documentation/devicetree/bindings/usb/msm-phy.txt
@@ -101,6 +101,10 @@ Required properties:
Required "supply-name" examples are:
"vdd" : vdd supply for SSPHY digital circuit operation
"core" : high-voltage analog supply for SSPHY
+ - clocks: a list of phandles to the PHY clocks. Use as per
+ Documentation/devicetree/bindings/clock/clock-bindings.txt
+ - clock-names: Names of the clocks in 1-1 correspondence with the "clocks"
+ property. Required clocks are "aux_clk" and "pipe_clk".
- qcom,vdd-voltage-level: This property must be a list of three integer
values (no, min, max) where each value represents either a voltage in
microvolts or a value corresponding to voltage corner
@@ -119,6 +123,10 @@ Optional properties:
- reg: Additional register set of address and length to control QMP PHY are:
"tcsr_usb3_dp_phymode" : top-level CSR register to be written to select
super speed usb qmp phy.
+ - clocks: a list of phandles to the PHY clocks. Use as per
+ Documentation/devicetree/bindings/clock/clock-bindings.txt
+ - clock-names: Names of the clocks in 1-1 correspondence with the "clocks"
+ property. Required clocks are "cfg_ahb_clk", "phy_reset" and "phy_phy_reset".
- qcom,vbus-valid-override: If present, indicates VBUS pin is not connected to
the USB PHY and the controller must rely on external VBUS notification in
order to manually relay the notification to the SSPHY.
@@ -138,6 +146,17 @@ Example:
vdda18-supply = <&pmd9635_l8>;
qcom,vdd-voltage-level = <0 900000 1050000>;
qcom,vbus-valid-override;
+
+ clocks = <&clock_gcc clk_gcc_usb3_phy_aux_clk>,
+ <&clock_gcc clk_gcc_usb3_phy_pipe_clk>,
+ <&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>,
+ <&clock_gcc clk_gcc_usb3_phy_reset>,
+ <&clock_gcc clk_gcc_usb3phy_phy_reset>,
+ <&clock_gcc clk_ln_bb_clk1>,
+ <&clock_gcc clk_gcc_usb3_clkref_clk>;
+
+ clock-names = "aux_clk", "pipe_clk", "cfg_ahb_clk", "phy_reset",
+ "phy_phy_reset", "ref_clk_src", "ref_clk";
};
QUSB2 High-Speed PHY
@@ -157,21 +176,22 @@ Required properties:
- clocks: a list of phandles to the PHY clocks. Use as per
Documentation/devicetree/bindings/clock/clock-bindings.txt
- clock-names: Names of the clocks in 1-1 correspondence with the "clocks"
- property. Required clocks are "cfg_ahb_clk" and "phy_reset".
+ property. Required clock is "phy_reset".
- phy_type: Should be one of "ulpi" or "utmi". ChipIdea core uses "ulpi" mode.
Optional properties:
- - reg: Address and length register set to control QUSB2 PHY
- "qscratch_base" : QSCRATCH base register set.
+ - reg-names: Additional registers corresponding with the following:
"tune2_efuse_addr": EFUSE based register address to read TUNE2 parameter.
via the QSCRATCH interface.
"emu_phy_base" : phy base address used for programming emulation target phy.
"ref_clk_addr" : ref_clk bcr address used for on/off ref_clk before reset.
- - reg-names: Should be "qscratch_base". The qscratch register bank
- allows us to manipulate QUSB PHY bits eg. to enable D+ pull-up using s/w
- control in device mode. The reg-names property is required if the
- reg property is specified.
+ - clocks: a list of phandles to the PHY clocks. Use as per
+ Documentation/devicetree/bindings/clock/clock-bindings.txt
+ - clock-names: Names of the clocks in 1-1 correspondence with the "clocks"
+ property. "cfg_ahb_clk", "ref_clk_src" and "ref_clk" are optional clocks.
- qcom,qusb-phy-init-seq: QUSB PHY initialization sequence with value,reg pair.
+ - qcom,qusb-phy-host-init-seq: QUSB PHY initialization sequence for host mode
+ with value,reg pair.
- qcom,emu-init-seq : emulation initialization sequence with value,reg pair.
- qcom,phy-pll-reset-seq : emulation PLL reset sequence with value,reg pair.
- qcom,emu-dcm-reset-seq : emulation DCM reset sequence with value,reg pair.
@@ -185,10 +205,8 @@ Optional properties:
Example:
qusb_phy: qusb@f9b39000 {
compatible = "qcom,qusb2phy";
- reg = <0x00079000 0x7000>,
- <0x08af8800 0x400>;
- reg-names = "qusb_phy_base",
- "qscratch_base";
+ reg = <0x00079000 0x7000>;
+ reg-names = "qusb_phy_base";
vdd-supply = <&pm8994_s2_corner>;
vdda18-supply = <&pm8994_l6>;
vdda33-supply = <&pm8994_l24>;
diff --git a/Documentation/devicetree/bindings/usb/msm-ssusb.txt b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
index 7d323b91f031..2b2bfe428c79 100644
--- a/Documentation/devicetree/bindings/usb/msm-ssusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
@@ -10,6 +10,11 @@ Required properties :
"hs_phy_irq" : Interrupt from HS PHY for asynchronous events in LPM.
"pwr_event_irq" : Interrupt to controller for asynchronous events in LPM.
Used for SS-USB power events.
+ - clocks: a list of phandles to the controller clocks. Use as per
+ Documentation/devicetree/bindings/clock/clock-bindings.txt
+ - clock-names: Names of the clocks in 1-1 correspondence with the "clocks"
+ property. Required clocks are "xo", "iface_clk", "core_clk", "sleep_clk"
+ and "utmi_clk".
Optional properties :
- reg: Additional registers
@@ -27,6 +32,10 @@ Optional properties :
- interrupt-names : Optional interrupt resource entries are:
"pmic_id_irq" : Interrupt from PMIC for external ID pin notification.
"ss_phy_irq" : Interrupt from super speed phy for wake up notification.
+ - clocks: a list of phandles to the controller clocks. Use as per
+ Documentation/devicetree/bindings/clock/clock-bindings.txt
+ - clock-names: Names of the clocks in 1-1 correspondence with the "clocks"
+ property. Optional clocks are "bus_aggr_clk" and "cfg_ahb_clk".
- qcom,charging-disabled: If present then battery charging using USB
is disabled.
- vbus_dwc3-supply: phandle to the 5V VBUS supply regulator used for host mode.
@@ -77,6 +86,18 @@ Example MSM USB3.0 controller device node :
qcom,msm_bus,vectors =
<61 512 0 0>,
<61 512 240000000 960000000>;
+
+ clocks = <&clock_gcc clk_gcc_usb30_master_clk>,
+ <&clock_gcc clk_gcc_cfg_noc_usb3_axi_clk>,
+ <&clock_gcc clk_gcc_aggre1_usb3_axi_clk>,
+ <&clock_gcc clk_gcc_usb30_mock_utmi_clk>,
+ <&clock_gcc clk_gcc_usb30_sleep_clk>,
+ <&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>,
+ <&clock_gcc clk_cxo_dwc3_clk>;
+
+ clock-names = "core_clk", "iface_clk", "bus_aggr_clk",
+ "utmi_clk", "sleep_clk", "cfg_ahb_clk", "xo";
+
dwc3@f9200000 {
compatible = "synopsys,dwc3";
reg = <0xf9200000 0xfc000>;
diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi
new file mode 100644
index 000000000000..90df1d0c1ac0
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi
@@ -0,0 +1,80 @@
+/* Copyright (c) 2016, 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,ascent_3450mah {
+ /* #Ascent_860_82209_0000_3450mAh_averaged_MasterSlave_Jul20th2016*/
+ qcom,max-voltage-uv = <4350000>;
+ qcom,nom-batt-capacity-mah = <3450>;
+ qcom,batt-id-kohm = <60>;
+ qcom,battery-beta = <3435>;
+ qcom,battery-type = "ascent_860_82209_0000_3450mah";
+ qcom,checksum = <0xD1D9>;
+ qcom,gui-version = "PMI8998GUI - 0.0.0.82";
+ qcom,fg-profile-data = [
+ 2C 1F 3F FC
+ E9 03 A1 FD
+ 58 1D FD F5
+ 27 12 2C 14
+ 3F 18 FF 22
+ 9B 45 A3 52
+ 55 00 00 00
+ 0E 00 00 00
+ 00 00 1C AC
+ F7 CD 71 B5
+ 1A 00 0C 00
+ 3C EB 54 E4
+ EC 05 7F FA
+ 76 05 F5 02
+ CA F3 82 3A
+ 2A 09 40 40
+ 07 00 05 00
+ 58 1F 42 06
+ 85 03 35 F4
+ 4D 1D 37 F2
+ 23 0A 79 15
+ B7 18 32 23
+ 26 45 72 53
+ 55 00 00 00
+ 0D 00 00 00
+ 00 00 13 CC
+ 03 00 98 BD
+ 16 00 00 00
+ 3C EB 54 E4
+ 9F FC A3 F3
+ 0F FC DF FA
+ FF E5 A9 23
+ CB 33 08 33
+ 07 10 00 00
+ 81 0D 99 45
+ 16 00 19 00
+ 75 01 0A FA
+ FF 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 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ ];
+};
diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi
new file mode 100644
index 000000000000..2c1edde56d6a
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi
@@ -0,0 +1,80 @@
+/* Copyright (c) 2016, 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,itech_3000mah {
+ /* #Itech_B00826LF_3000mAh_ver1660_averaged_MasterSlave_Jul20th2016*/
+ qcom,max-voltage-uv = <4350000>;
+ qcom,nom-batt-capacity-mah = <3000>;
+ qcom,batt-id-kohm = <100>;
+ qcom,battery-beta = <3450>;
+ qcom,battery-type = "itech_b00826lf_3000mah_ver1660";
+ qcom,checksum = <0xE06B>;
+ qcom,gui-version = "PMI8998GUI - 0.0.0.82";
+ qcom,fg-profile-data = [
+ A4 1F 6E 05
+ 9C 0A 16 06
+ 32 1D 24 E5
+ 61 0B 1B 15
+ AD 17 8C 22
+ EB 3C 87 4A
+ 5B 00 00 00
+ 12 00 00 00
+ 00 00 62 C2
+ 0C CD D8 C2
+ 19 00 0C 00
+ 7E 00 C7 EC
+ E3 05 5D FA
+ 97 F5 12 12
+ C2 05 90 3B
+ 22 09 40 40
+ 07 00 05 00
+ 7D 1F DE 05
+ 3F 0A 73 06
+ 72 1D E2 F5
+ 6F 12 BF 1D
+ 88 18 FB 22
+ 8D 45 C6 52
+ 54 00 00 00
+ 0F 00 00 00
+ 00 00 BD CD
+ 55 C2 5D C5
+ 14 00 00 00
+ 7E 00 C7 EC
+ 60 06 BB 00
+ B3 FC 61 03
+ 6A 06 78 1B
+ B3 33 08 33
+ 07 10 00 00
+ 3E 0B 99 45
+ 14 00 19 00
+ AE 01 0A FA
+ FF 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 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ ];
+};
diff --git a/arch/arm/boot/dts/qcom/msm-gdsc-cobalt.dtsi b/arch/arm/boot/dts/qcom/msm-gdsc-cobalt.dtsi
index a2c8c89b08a3..c86351e48d5f 100644
--- a/arch/arm/boot/dts/qcom/msm-gdsc-cobalt.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-gdsc-cobalt.dtsi
@@ -60,6 +60,7 @@
<0xc8ce024 0x4>;
reg-names = "base", "hw_ctrl_addr";
qcom,no-status-check-on-disable;
+ qcom,gds-timeout = <500>;
status = "disabled";
};
diff --git a/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi b/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi
index 25e0d99987db..07423a601b35 100644
--- a/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi
@@ -79,6 +79,8 @@
qcom,snapshot-size = <1048576>; //bytes
+ qcom,gpu-qdss-stm = <0x081c0000 0x40000>; // base addr, size
+
/* Trace bus */
coresight-id = <300>;
coresight-name = "coresight-gfx";
diff --git a/arch/arm/boot/dts/qcom/msm8996-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8996-mtp.dtsi
index 55fbca7dc9a1..96279288d336 100644
--- a/arch/arm/boot/dts/qcom/msm8996-mtp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-mtp.dtsi
@@ -719,7 +719,6 @@
"SpkrLeft IN", "SPK1 OUT",
"SpkrRight IN", "SPK2 OUT";
- qcom,hdmi-audio-rx;
asoc-codec = <&stub_codec>, <&hdmi_audio>;
asoc-codec-names = "msm-stub-codec.1", "msm-hdmi-audio-codec-rx";
qcom,hph-en1-gpio = <&pmi8994_gpios 10 0>;
diff --git a/arch/arm/boot/dts/qcom/msm8996.dtsi b/arch/arm/boot/dts/qcom/msm8996.dtsi
index 2da89bd9ac6e..9bcc375e275c 100644
--- a/arch/arm/boot/dts/qcom/msm8996.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996.dtsi
@@ -2059,11 +2059,9 @@
qusb_phy0: qusb@7411000 {
compatible = "qcom,qusb2phy";
reg = <0x07411000 0x180>,
- <0x06af8800 0x400>,
<0x0007024c 0x4>,
<0x00388018 0x4>;
reg-names = "qusb_phy_base",
- "qscratch_base",
"tune2_efuse_addr",
"ref_clk_addr";
vdd-supply = <&pm8994_s2_corner>;
@@ -2096,11 +2094,9 @@
qusb_phy1: qusb@7412000 {
compatible = "qcom,qusb2phy";
reg = <0x07412000 0x180>,
- <0x076f8800 0x400>,
<0x0007024c 0x4>,
<0x00388014 0x4>;
reg-names = "qusb_phy_base",
- "qscratch_base",
"tune2_efuse_addr",
"ref_clk_addr";
vdd-supply = <&pm8994_s2_corner>;
@@ -3004,13 +3000,14 @@
qcom,tasha-mclk-clk-freq = <9600000>;
asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>,
<&loopback>, <&compress>, <&hostless>,
- <&afe>, <&lsm>, <&routing>, <&cpe>, <&compr>,
- <&pcmnoirq>;
+ <&afe>, <&lsm>, <&routing>, <&cpe>,
+ <&compr>, <&pcmnoirq>, <&cpe3>;
asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", "msm-pcm-dsp.2",
"msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback",
"msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe",
"msm-lsm-client", "msm-pcm-routing", "msm-cpe-lsm",
- "msm-compr-dsp", "msm-pcm-dsp-noirq";
+ "msm-compr-dsp", "msm-pcm-dsp-noirq",
+ "msm-cpe-lsm.3";
asoc-cpu = <&dai_pri_auxpcm>, <&dai_sec_auxpcm>, <&dai_hdmi>, <&dai_mi2s>,
<&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
<&sb_2_rx>, <&sb_2_tx>, <&sb_3_rx>, <&sb_3_tx>,
@@ -3122,6 +3119,12 @@
cpe: qcom,msm-cpe-lsm {
compatible = "qcom,msm-cpe-lsm";
+ qcom,msm-cpe-lsm-id = <1>;
+ };
+
+ cpe3: qcom,msm-cpe-lsm@3 {
+ compatible = "qcom,msm-cpe-lsm";
+ qcom,msm-cpe-lsm-id = <3>;
};
qcom,msm-dai-q6 {
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-cdp.dts b/arch/arm/boot/dts/qcom/msmcobalt-cdp.dts
index aebd9a1440de..c8402ba5b69a 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-cdp.dts
+++ b/arch/arm/boot/dts/qcom/msmcobalt-cdp.dts
@@ -21,3 +21,18 @@
compatible = "qcom,msmcobalt-cdp", "qcom,msmcobalt", "qcom,cdp";
qcom,board-id = <1 0>;
};
+
+&qusb_phy0 {
+ qcom,qusb-phy-host-init-seq =
+ /* value reg_offsets> */
+ <0x63 0x210
+ 0x13 0x04
+ 0x7c 0x18c
+ 0x80 0x2c
+ 0x0a 0x184
+ 0x8c 0x21c
+ 0x05 0x23c
+ 0x03 0x240
+ 0xff 0x218
+ 0x62 0x210>;
+};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-coresight.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-coresight.dtsi
index 96698951bd6c..4afaa3aa51be 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-coresight.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-coresight.dtsi
@@ -20,6 +20,7 @@
reg-names = "tmc-base", "bam-base";
arm,buffer-size = <0x400000>;
+ arm,sg-enable;
coresight-ctis = <&cti0 &cti8>;
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dts b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dts
index e5708fc8d743..89ef72c104f5 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dts
+++ b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dts
@@ -21,3 +21,18 @@
compatible = "qcom,msmcobalt-mtp", "qcom,msmcobalt", "qcom,mtp";
qcom,board-id = <8 0>;
};
+
+&qusb_phy0 {
+ qcom,qusb-phy-host-init-seq =
+ /* value reg_offsets> */
+ <0x63 0x210
+ 0x13 0x04
+ 0x7c 0x18c
+ 0x80 0x2c
+ 0x0a 0x184
+ 0x8c 0x21c
+ 0x05 0x23c
+ 0x03 0x240
+ 0xff 0x218
+ 0x62 0x210>;
+};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi
index 6833bd1d7f4a..50924b1667a4 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi
@@ -501,3 +501,11 @@
};
};
};
+
+/{
+ mtp_batterydata: qcom,battery-data {
+ qcom,batt-id-range-pct = <15>;
+ #include "fg-gen3-batterydata-itech-3000mah.dtsi"
+ #include "fg-gen3-batterydata-ascent-3450mah.dtsi"
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt.dtsi b/arch/arm/boot/dts/qcom/msmcobalt.dtsi
index 5dc530ea8494..aa9390de6525 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt.dtsi
@@ -716,6 +716,7 @@
reg = <0x100000 0xb0000>;
reg-names = "cc_base";
vdd_dig-supply = <&pmcobalt_s1_level>;
+ vdd_dig_ao-supply = <&pmcobalt_s1_level_ao>;
#clock-cells = <1>;
};
@@ -1726,11 +1727,10 @@
<&clock_gcc clk_gcc_aggre1_usb3_axi_clk>,
<&clock_gcc clk_gcc_usb30_mock_utmi_clk>,
<&clock_gcc clk_gcc_usb30_sleep_clk>,
- <&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>,
<&clock_gcc clk_cxo_dwc3_clk>;
clock-names = "core_clk", "iface_clk", "bus_aggr_clk",
- "utmi_clk", "sleep_clk", "cfg_ahb_clk", "xo";
+ "utmi_clk", "sleep_clk", "xo";
dwc3@a800000 {
compatible = "snps,dwc3";
@@ -1778,11 +1778,9 @@
};
qusb_phy0: qusb@c012000 {
- compatible = "qcom,qusb2phy";
- reg = <0x0c012000 0x2a8>,
- <0x0a8f8800 0x400>;
- reg-names = "qusb_phy_base",
- "qscratch_base";
+ compatible = "qcom,qusb2phy-v2";
+ reg = <0x0c012000 0x2a8>;
+ reg-names = "qusb_phy_base";
vdd-supply = <&pmcobalt_l1>;
vdda18-supply = <&pmcobalt_l12>;
vdda33-supply = <&pmcobalt_l24>;
@@ -1798,11 +1796,9 @@
clocks = <&clock_gcc clk_ln_bb_clk1>,
<&clock_gcc clk_gcc_rx1_usb2_clkref_clk>,
- <&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>,
<&clock_gcc clk_gcc_qusb2phy_prim_reset>;
- clock-names = "ref_clk_src", "ref_clk", "cfg_ahb_clk",
- "phy_reset";
+ clock-names = "ref_clk_src", "ref_clk", "phy_reset";
};
ssphy: ssphy@c010000 {
@@ -1950,13 +1946,12 @@
clocks = <&clock_gcc clk_gcc_usb3_phy_aux_clk>,
<&clock_gcc clk_gcc_usb3_phy_pipe_clk>,
- <&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>,
<&clock_gcc clk_gcc_usb3_phy_reset>,
<&clock_gcc clk_gcc_usb3phy_phy_reset>,
<&clock_gcc clk_ln_bb_clk1>,
<&clock_gcc clk_gcc_usb3_clkref_clk>;
- clock-names = "aux_clk", "pipe_clk", "cfg_ahb_clk", "phy_reset",
+ clock-names = "aux_clk", "pipe_clk", "phy_reset",
"phy_phy_reset", "ref_clk_src", "ref_clk";
};
diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig
index 742ab830a812..239baffa6cdb 100644
--- a/arch/arm64/configs/msmcortex-perf_defconfig
+++ b/arch/arm64/configs/msmcortex-perf_defconfig
@@ -57,6 +57,8 @@ CONFIG_FORCE_ALLOC_FROM_DMA_ZONE=y
CONFIG_SECCOMP=y
CONFIG_ARMV8_DEPRECATED=y
CONFIG_SWP_EMULATION=y
+CONFIG_CP15_BARRIER_EMULATION=y
+CONFIG_SETEND_EMULATION=y
# CONFIG_EFI is not set
CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
@@ -302,6 +304,7 @@ CONFIG_MSM_PM=y
CONFIG_APSS_CORE_EA=y
CONFIG_MSM_APM=y
CONFIG_QPNP_SMBCHARGER=y
+CONFIG_QPNP_FG_GEN3=y
CONFIG_SMB135X_CHARGER=y
CONFIG_SMB1351_USB_CHARGER=y
CONFIG_MSM_BCL_CTL=y
@@ -468,6 +471,9 @@ CONFIG_MSM_MDSS_PLL=y
CONFIG_REMOTE_SPINLOCK_MSM=y
CONFIG_IOMMU_IO_PGTABLE_FAST=y
CONFIG_ARM_SMMU=y
+CONFIG_IOMMU_DEBUG=y
+CONFIG_IOMMU_DEBUG_TRACKING=y
+CONFIG_IOMMU_TESTS=y
CONFIG_MSM_SMEM=y
CONFIG_QPNP_HAPTIC=y
CONFIG_MSM_SMD=y
@@ -547,6 +553,19 @@ CONFIG_CPU_FREQ_SWITCH_PROFILER=y
CONFIG_DEBUG_SET_MODULE_RONX=y
CONFIG_DEBUG_RODATA=y
CONFIG_DEBUG_ALIGN_RODATA=y
+CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
+CONFIG_CORESIGHT_SOURCE_ETM4X=y
+CONFIG_CORESIGHT_REMOTE_ETM=y
+CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0
+CONFIG_CORESIGHT_QCOM_REPLICATOR=y
+CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_HWEVENT=y
+CONFIG_CORESIGHT_CTI=y
+CONFIG_CORESIGHT_TPDA=y
+CONFIG_CORESIGHT_TPDM=y
+CONFIG_CORESIGHT_QPDI=y
+CONFIG_CORESIGHT_SOURCE_DUMMY=y
CONFIG_PFK=y
CONFIG_SECURITY=y
CONFIG_SECURITY_SELINUX=y
diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig
index afa39ce02376..8b9da2d6b0d3 100644
--- a/arch/arm64/configs/msmcortex_defconfig
+++ b/arch/arm64/configs/msmcortex_defconfig
@@ -307,6 +307,7 @@ CONFIG_MSM_PM=y
CONFIG_APSS_CORE_EA=y
CONFIG_MSM_APM=y
CONFIG_QPNP_SMBCHARGER=y
+CONFIG_QPNP_FG_GEN3=y
CONFIG_SMB135X_CHARGER=y
CONFIG_SMB1351_USB_CHARGER=y
CONFIG_MSM_BCL_CTL=y
diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index 8fa6f87cc31d..52d046b6a920 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -674,7 +674,9 @@ static const struct of_device_id armv8_pmu_of_device_ids[] = {
{.compatible = "arm,armv8-pmuv3", .data = armv8_pmuv3_init},
{.compatible = "arm,cortex-a53-pmu", .data = armv8_a53_pmu_init},
{.compatible = "arm,cortex-a57-pmu", .data = armv8_a57_pmu_init},
+#ifdef CONFIG_ARCH_MSM8996
{.compatible = "qcom,kryo-pmuv3", .data = kryo_pmu_init},
+#endif
{},
};
diff --git a/arch/um/configs/x86_64_defconfig b/arch/um/configs/x86_64_defconfig
index 3aab117bd553..f9ecc8270f54 100644
--- a/arch/um/configs/x86_64_defconfig
+++ b/arch/um/configs/x86_64_defconfig
@@ -15,7 +15,6 @@ CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CPUSETS=y
CONFIG_CGROUP_CPUACCT=y
-CONFIG_RESOURCE_COUNTERS=y
CONFIG_CGROUP_SCHED=y
CONFIG_BLK_CGROUP=y
# CONFIG_PID_NS is not set
@@ -54,6 +53,7 @@ CONFIG_UNIX=y
CONFIG_INET=y
# CONFIG_INET_LRO is not set
# CONFIG_IPV6 is not set
+# CONFIG_NET_ACTIVITY_STATS is not set
CONFIG_UML_NET=y
CONFIG_UML_NET_ETHERTAP=y
CONFIG_UML_NET_TUNTAP=y
diff --git a/drivers/clk/msm/clock-gcc-cobalt.c b/drivers/clk/msm/clock-gcc-cobalt.c
index 1524d3d8ed46..7299863ff42b 100644
--- a/drivers/clk/msm/clock-gcc-cobalt.c
+++ b/drivers/clk/msm/clock-gcc-cobalt.c
@@ -56,6 +56,7 @@ static void __iomem *virt_dbgbase;
}
static DEFINE_VDD_REGULATORS(vdd_dig, VDD_DIG_NUM, 1, vdd_corner, NULL);
+static DEFINE_VDD_REGULATORS(vdd_dig_ao, VDD_DIG_NUM, 1, vdd_corner, NULL);
DEFINE_CLK_RPM_SMD_BRANCH(cxo_clk_src, cxo_clk_src_ao, RPM_MISC_CLK_TYPE,
CXO_CLK_SRC_ID, 19200000);
@@ -211,7 +212,7 @@ static struct rcg_clk hmss_ahb_clk_src = {
.c = {
.dbg_name = "hmss_ahb_clk_src",
.ops = &clk_ops_rcg,
- VDD_DIG_FMAX_MAP3(LOWER, 19200000, LOW, 50000000,
+ VDD_DIG_FMAX_MAP3_AO(LOWER, 19200000, LOW, 50000000,
NOMINAL, 100000000),
CLK_INIT(hmss_ahb_clk_src.c),
},
@@ -1029,7 +1030,7 @@ static struct rcg_clk hmss_gpll0_clk_src = {
.c = {
.dbg_name = "hmss_gpll0_clk_src",
.ops = &clk_ops_rcg,
- VDD_DIG_FMAX_MAP1(LOWER, 600000000),
+ VDD_DIG_FMAX_MAP1_AO(LOWER, 600000000),
CLK_INIT(hmss_gpll0_clk_src.c),
},
};
@@ -2184,17 +2185,6 @@ static struct reset_clk gcc_qusb2phy_sec_reset = {
},
};
-static struct branch_clk gcc_usb_phy_cfg_ahb2phy_clk = {
- .cbcr_reg = GCC_USB_PHY_CFG_AHB2PHY_CBCR,
- .has_sibling = 1,
- .base = &virt_base,
- .c = {
- .dbg_name = "gcc_usb_phy_cfg_ahb2phy_clk",
- .ops = &clk_ops_branch,
- CLK_INIT(gcc_usb_phy_cfg_ahb2phy_clk.c),
- },
-};
-
static struct branch_clk gcc_wcss_ahb_s0_clk = {
.cbcr_reg = GCC_WCSS_AHB_S0_CBCR,
.has_sibling = 1,
@@ -2380,7 +2370,6 @@ static struct mux_clk gcc_debug_mux = {
{ &gcc_usb30_mock_utmi_clk.c, 0x0040 },
{ &gcc_usb3_phy_aux_clk.c, 0x0041 },
{ &gcc_usb3_phy_pipe_clk.c, 0x0042 },
- { &gcc_usb_phy_cfg_ahb2phy_clk.c, 0x0045 },
{ &gcc_sdcc2_apps_clk.c, 0x0046 },
{ &gcc_sdcc2_ahb_clk.c, 0x0047 },
{ &gcc_sdcc4_apps_clk.c, 0x0048 },
@@ -2687,7 +2676,6 @@ static struct clk_lookup msm_clocks_gcc_cobalt[] = {
CLK_LIST(gcc_usb30_sleep_clk),
CLK_LIST(gcc_usb3_phy_aux_clk),
CLK_LIST(gcc_usb3_phy_pipe_clk),
- CLK_LIST(gcc_usb_phy_cfg_ahb2phy_clk),
CLK_LIST(gcc_prng_ahb_clk),
CLK_LIST(gcc_boot_rom_ahb_clk),
CLK_LIST(gcc_wcss_ahb_s0_clk),
@@ -2748,11 +2736,6 @@ static int msm_gcc_cobalt_probe(struct platform_device *pdev)
return -ENOMEM;
}
- /* Set the HMSS_AHB_CLK_ENA bit to enable the hmss_ahb_clk */
- regval = readl_relaxed(virt_base + GCC_APCS_CLOCK_BRANCH_ENA_VOTE);
- regval |= BIT(21);
- writel_relaxed(regval, virt_base + GCC_APCS_CLOCK_BRANCH_ENA_VOTE);
-
/*
* Set the HMSS_AHB_CLK_SLEEP_ENA bit to allow the hmss_ahb_clk to be
* turned off by hardware during certain apps low power modes.
@@ -2769,6 +2752,14 @@ static int msm_gcc_cobalt_probe(struct platform_device *pdev)
return PTR_ERR(vdd_dig.regulator[0]);
}
+ vdd_dig_ao.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_dig_ao");
+ if (IS_ERR(vdd_dig_ao.regulator[0])) {
+ if (!(PTR_ERR(vdd_dig_ao.regulator[0]) == -EPROBE_DEFER))
+ dev_err(&pdev->dev,
+ "Unable to get vdd_dig_ao regulator\n");
+ return PTR_ERR(vdd_dig_ao.regulator[0]);
+ }
+
bimc_clk.c.parent = &cxo_clk_src.c;
ret = of_msm_clock_register(pdev->dev.of_node, msm_clocks_rpm_cobalt,
ARRAY_SIZE(msm_clocks_rpm_cobalt));
diff --git a/drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c b/drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c
index eac501e28d7b..d5d55a58bf7f 100644
--- a/drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c
+++ b/drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c
@@ -82,7 +82,7 @@
#define HDMI_HZ_TO_MHZ 1000000
#define HDMI_REF_CLOCK_MHZ 19.2
#define HDMI_REF_CLOCK_HZ (HDMI_REF_CLOCK_MHZ * 1000000)
-#define HDMI_VCO_MIN_RATE_HZ 30000000
+#define HDMI_VCO_MIN_RATE_HZ 25000000
#define HDMI_VCO_MAX_RATE_HZ 600000000
struct cobalt_reg_cfg {
diff --git a/drivers/clk/msm/vdd-level-cobalt.h b/drivers/clk/msm/vdd-level-cobalt.h
index 2cb40afafe3f..f847a4104d4d 100644
--- a/drivers/clk/msm/vdd-level-cobalt.h
+++ b/drivers/clk/msm/vdd-level-cobalt.h
@@ -50,11 +50,19 @@
}, \
.num_fmax = VDD_DIG_NUM
-#define VDD_DIG_FMAX_MAP2_AO(l1, f1, l2, f2) \
+#define VDD_DIG_FMAX_MAP1_AO(l1, f1) \
+ .vdd_class = &vdd_dig_ao, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
+
+#define VDD_DIG_FMAX_MAP3_AO(l1, f1, l2, f2, l3, f3) \
.vdd_class = &vdd_dig_ao, \
.fmax = (unsigned long[VDD_DIG_NUM]) { \
[VDD_DIG_##l1] = (f1), \
[VDD_DIG_##l2] = (f2), \
+ [VDD_DIG_##l3] = (f3), \
}, \
.num_fmax = VDD_DIG_NUM
diff --git a/drivers/clk/qcom/mdss/Kconfig b/drivers/clk/qcom/mdss/Kconfig
new file mode 100644
index 000000000000..229780e45bb8
--- /dev/null
+++ b/drivers/clk/qcom/mdss/Kconfig
@@ -0,0 +1,6 @@
+config MSM_MDSS_PLL
+ bool "MDSS pll programming"
+ ---help---
+ It provides support for DSI, eDP and HDMI interface pll programming on MDSS
+ hardware. It also handles the pll specific resources and turn them on/off when
+ mdss pll client tries to enable/disable pll clocks.
diff --git a/drivers/clk/qcom/mdss/Makefile b/drivers/clk/qcom/mdss/Makefile
new file mode 100644
index 000000000000..75891dc10dda
--- /dev/null
+++ b/drivers/clk/qcom/mdss/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_MSM_MDSS_PLL) += mdss-pll-util.o
+obj-$(CONFIG_MSM_MDSS_PLL) += mdss-pll.o
+obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-8996.o
+obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-8996-util.o
+obj-$(CONFIG_MSM_MDSS_PLL) += mdss-hdmi-pll-8996.o
diff --git a/drivers/clk/qcom/mdss/mdss-dsi-pll-8996-util.c b/drivers/clk/qcom/mdss/mdss-dsi-pll-8996-util.c
new file mode 100644
index 000000000000..6d2694d5a2e9
--- /dev/null
+++ b/drivers/clk/qcom/mdss/mdss-dsi-pll-8996-util.c
@@ -0,0 +1,1137 @@
+/* Copyright (c) 2015-2016, 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/err.h>
+#include <linux/iopoll.h>
+#include <linux/delay.h>
+#include <linux/clk/msm-clock-generic.h>
+
+#include "mdss-pll.h"
+#include "mdss-dsi-pll.h"
+#include "mdss-dsi-pll-8996.h"
+
+#define DSI_PLL_POLL_MAX_READS 15
+#define DSI_PLL_POLL_TIMEOUT_US 1000
+#define MSM8996_DSI_PLL_REVISION_2 2
+
+#define CEIL(x, y) (((x) + ((y)-1)) / (y))
+
+int set_mdss_byte_mux_sel_8996(struct mux_clk *clk, int sel)
+{
+ return 0;
+}
+
+int get_mdss_byte_mux_sel_8996(struct mux_clk *clk)
+{
+ return 0;
+}
+
+int set_mdss_pixel_mux_sel_8996(struct mux_clk *clk, int sel)
+{
+ return 0;
+}
+
+int get_mdss_pixel_mux_sel_8996(struct mux_clk *clk)
+{
+ return 0;
+}
+
+static int mdss_pll_read_stored_trim_codes(
+ struct mdss_pll_resources *dsi_pll_res, s64 vco_clk_rate)
+{
+ int i;
+ int rc = 0;
+ bool found = false;
+
+ if (!dsi_pll_res->dfps) {
+ rc = -EINVAL;
+ goto end_read;
+ }
+
+ for (i = 0; i < dsi_pll_res->dfps->panel_dfps.frame_rate_cnt; i++) {
+ struct dfps_codes_info *codes_info =
+ &dsi_pll_res->dfps->codes_dfps[i];
+
+ pr_debug("valid=%d frame_rate=%d, vco_rate=%d, code %d %d\n",
+ codes_info->is_valid, codes_info->frame_rate,
+ codes_info->clk_rate, codes_info->pll_codes.pll_codes_1,
+ codes_info->pll_codes.pll_codes_2);
+
+ if (vco_clk_rate != codes_info->clk_rate &&
+ codes_info->is_valid)
+ continue;
+
+ dsi_pll_res->cache_pll_trim_codes[0] =
+ codes_info->pll_codes.pll_codes_1;
+ dsi_pll_res->cache_pll_trim_codes[1] =
+ codes_info->pll_codes.pll_codes_2;
+ found = true;
+ break;
+ }
+
+ if (!found) {
+ rc = -EINVAL;
+ goto end_read;
+ }
+
+ pr_debug("core_kvco_code=0x%x core_vco_tune=0x%x\n",
+ dsi_pll_res->cache_pll_trim_codes[0],
+ dsi_pll_res->cache_pll_trim_codes[1]);
+
+end_read:
+ return rc;
+}
+
+int post_n1_div_set_div(struct div_clk *clk, int div)
+{
+ struct mdss_pll_resources *pll = clk->priv;
+ struct dsi_pll_db *pdb;
+ struct dsi_pll_output *pout;
+ int rc;
+ u32 n1div = 0;
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return rc;
+ }
+
+ pdb = (struct dsi_pll_db *)pll->priv;
+ pout = &pdb->out;
+
+ /*
+ * vco rate = bit_clk * postdiv * n1div
+ * vco range from 1300 to 2600 Mhz
+ * postdiv = 1
+ * n1div = 1 to 15
+ * n1div = roundup(1300Mhz / bit_clk)
+ * support bit_clk above 86.67Mhz
+ */
+
+ /* this is for vco/bit clock */
+ pout->pll_postdiv = 1; /* fixed, divided by 1 */
+ pout->pll_n1div = div;
+
+ n1div = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_CMN_CLK_CFG0);
+ n1div &= ~0xf;
+ n1div |= (div & 0xf);
+ MDSS_PLL_REG_W(pll->pll_base, DSIPHY_CMN_CLK_CFG0, n1div);
+ /* ensure n1 divider is programed */
+ wmb();
+ pr_debug("ndx=%d div=%d postdiv=%x n1div=%x\n",
+ pll->index, div, pout->pll_postdiv, pout->pll_n1div);
+
+ mdss_pll_resource_enable(pll, false);
+
+ return 0;
+}
+
+int post_n1_div_get_div(struct div_clk *clk)
+{
+ u32 div;
+ int rc;
+ struct mdss_pll_resources *pll = clk->priv;
+
+ if (is_gdsc_disabled(pll))
+ return 0;
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return rc;
+ }
+
+ /*
+ * postdiv = 1/2/4/8
+ * n1div = 1 - 15
+ * fot the time being, assume postdiv = 1
+ */
+
+ div = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_CMN_CLK_CFG0);
+ div &= 0xF;
+ pr_debug("n1 div = %d\n", div);
+
+ mdss_pll_resource_enable(pll, false);
+
+ return div;
+}
+
+int n2_div_set_div(struct div_clk *clk, int div)
+{
+ int rc;
+ u32 n2div;
+ struct mdss_pll_resources *pll = clk->priv;
+ struct dsi_pll_db *pdb;
+ struct dsi_pll_output *pout;
+ struct mdss_pll_resources *slave;
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return rc;
+ }
+
+ pdb = (struct dsi_pll_db *)pll->priv;
+ pout = &pdb->out;
+
+ /* this is for pixel clock */
+ n2div = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_CMN_CLK_CFG0);
+ n2div &= ~0xf0; /* bits 4 to 7 */
+ n2div |= (div << 4);
+ MDSS_PLL_REG_W(pll->pll_base, DSIPHY_CMN_CLK_CFG0, n2div);
+
+ /* commit slave if split display is enabled */
+ slave = pll->slave;
+ if (slave)
+ MDSS_PLL_REG_W(slave->pll_base, DSIPHY_CMN_CLK_CFG0, n2div);
+
+ pout->pll_n2div = div;
+
+ /* set dsiclk_sel=1 so that n2div *= 2 */
+ MDSS_PLL_REG_W(pll->pll_base, DSIPHY_CMN_CLK_CFG1, 1);
+ pr_debug("ndx=%d div=%d n2div=%x\n", pll->index, div, n2div);
+
+ mdss_pll_resource_enable(pll, false);
+
+ return rc;
+}
+
+int shadow_n2_div_set_div(struct div_clk *clk, int div)
+{
+ struct mdss_pll_resources *pll = clk->priv;
+ struct dsi_pll_db *pdb;
+ struct dsi_pll_output *pout;
+ u32 data;
+
+ pdb = pll->priv;
+ pout = &pdb->out;
+
+ pout->pll_n2div = div;
+
+ data = (pout->pll_n1div | (pout->pll_n2div << 4));
+ MDSS_DYN_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_CTRL19,
+ DSIPHY_CMN_CLK_CFG0, DSIPHY_CMN_CLK_CFG1,
+ data, 1);
+ return 0;
+}
+
+int n2_div_get_div(struct div_clk *clk)
+{
+ int rc;
+ u32 n2div;
+ struct mdss_pll_resources *pll = clk->priv;
+
+ if (is_gdsc_disabled(pll))
+ return 0;
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll=%d resources\n",
+ pll->index);
+ return rc;
+ }
+
+ n2div = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_CMN_CLK_CFG0);
+ n2div >>= 4;
+ n2div &= 0x0f;
+
+ mdss_pll_resource_enable(pll, false);
+
+ pr_debug("ndx=%d div=%d\n", pll->index, n2div);
+
+ return n2div;
+}
+
+static bool pll_is_pll_locked_8996(struct mdss_pll_resources *pll)
+{
+ u32 status;
+ bool pll_locked;
+
+ /* poll for PLL ready status */
+ if (readl_poll_timeout_atomic((pll->pll_base +
+ DSIPHY_PLL_RESET_SM_READY_STATUS),
+ status,
+ ((status & BIT(5)) > 0),
+ DSI_PLL_POLL_MAX_READS,
+ DSI_PLL_POLL_TIMEOUT_US)) {
+ pr_err("DSI PLL ndx=%d status=%x failed to Lock\n",
+ pll->index, status);
+ pll_locked = false;
+ } else if (readl_poll_timeout_atomic((pll->pll_base +
+ DSIPHY_PLL_RESET_SM_READY_STATUS),
+ status,
+ ((status & BIT(0)) > 0),
+ DSI_PLL_POLL_MAX_READS,
+ DSI_PLL_POLL_TIMEOUT_US)) {
+ pr_err("DSI PLL ndx=%d status=%x PLl not ready\n",
+ pll->index, status);
+ pll_locked = false;
+ } else {
+ pll_locked = true;
+ }
+
+ return pll_locked;
+}
+
+static void dsi_pll_start_8996(void __iomem *pll_base)
+{
+ pr_debug("start PLL at base=%p\n", pll_base);
+
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_VREF_CFG1, 0x10);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_PLL_CNTRL, 1);
+}
+
+static void dsi_pll_stop_8996(void __iomem *pll_base)
+{
+ pr_debug("stop PLL at base=%p\n", pll_base);
+
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_PLL_CNTRL, 0);
+}
+
+int dsi_pll_enable_seq_8996(struct mdss_pll_resources *pll)
+{
+ int rc = 0;
+
+ if (!pll) {
+ pr_err("Invalid PLL resources\n");
+ return -EINVAL;
+ }
+
+ dsi_pll_start_8996(pll->pll_base);
+
+ /*
+ * both DSIPHY_PLL_CLKBUFLR_EN and DSIPHY_CMN_GLBL_TEST_CTRL
+ * enabled at mdss_dsi_8996_phy_config()
+ */
+
+ if (!pll_is_pll_locked_8996(pll)) {
+ pr_err("DSI PLL ndx=%d lock failed\n", pll->index);
+ rc = -EINVAL;
+ goto init_lock_err;
+ }
+
+ pr_debug("DSI PLL ndx=%d Lock success\n", pll->index);
+
+init_lock_err:
+ return rc;
+}
+
+static int dsi_pll_enable(struct clk *c)
+{
+ int i, rc = 0;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+
+ /* Try all enable sequences until one succeeds */
+ for (i = 0; i < vco->pll_en_seq_cnt; i++) {
+ rc = vco->pll_enable_seqs[i](pll);
+ pr_debug("DSI PLL %s after sequence #%d\n",
+ rc ? "unlocked" : "locked", i + 1);
+ if (!rc)
+ break;
+ }
+
+ if (rc)
+ pr_err("ndx=%d DSI PLL failed to lock\n", pll->index);
+ else
+ pll->pll_on = true;
+
+ return rc;
+}
+
+static void dsi_pll_disable(struct clk *c)
+{
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+ struct mdss_pll_resources *slave;
+
+ if (!pll->pll_on &&
+ mdss_pll_resource_enable(pll, true)) {
+ pr_err("Failed to enable mdss dsi pll=%d\n", pll->index);
+ return;
+ }
+
+ pll->handoff_resources = false;
+ slave = pll->slave;
+
+ dsi_pll_stop_8996(pll->pll_base);
+
+ mdss_pll_resource_enable(pll, false);
+
+ pll->pll_on = false;
+
+ pr_debug("DSI PLL ndx=%d Disabled\n", pll->index);
+}
+
+static void mdss_dsi_pll_8996_input_init(struct mdss_pll_resources *pll,
+ struct dsi_pll_db *pdb)
+{
+ pdb->in.fref = 19200000; /* 19.2 Mhz*/
+ pdb->in.fdata = 0; /* bit clock rate */
+ pdb->in.dsiclk_sel = 1; /* 1, reg: 0x0014 */
+ pdb->in.ssc_en = pll->ssc_en; /* 1, reg: 0x0494, bit 0 */
+ pdb->in.ldo_en = 0; /* 0, reg: 0x004c, bit 0 */
+
+ /* fixed input */
+ pdb->in.refclk_dbler_en = 0; /* 0, reg: 0x04c0, bit 1 */
+ pdb->in.vco_measure_time = 5; /* 5, unknown */
+ pdb->in.kvco_measure_time = 5; /* 5, unknown */
+ pdb->in.bandgap_timer = 4; /* 4, reg: 0x0430, bit 3 - 5 */
+ pdb->in.pll_wakeup_timer = 5; /* 5, reg: 0x043c, bit 0 - 2 */
+ pdb->in.plllock_cnt = 1; /* 1, reg: 0x0488, bit 1 - 2 */
+ pdb->in.plllock_rng = 0; /* 0, reg: 0x0488, bit 3 - 4 */
+ pdb->in.ssc_center = pll->ssc_center;/* 0, reg: 0x0494, bit 1 */
+ pdb->in.ssc_adj_period = 37; /* 37, reg: 0x498, bit 0 - 9 */
+ pdb->in.ssc_spread = pll->ssc_ppm / 1000;
+ pdb->in.ssc_freq = pll->ssc_freq;
+
+ pdb->in.pll_ie_trim = 4; /* 4, reg: 0x0400 */
+ pdb->in.pll_ip_trim = 4; /* 4, reg: 0x0404 */
+ pdb->in.pll_cpcset_cur = 1; /* 1, reg: 0x04f0, bit 0 - 2 */
+ pdb->in.pll_cpmset_cur = 1; /* 1, reg: 0x04f0, bit 3 - 5 */
+ pdb->in.pll_icpmset = 4; /* 4, reg: 0x04fc, bit 3 - 5 */
+ pdb->in.pll_icpcset = 4; /* 4, reg: 0x04fc, bit 0 - 2 */
+ pdb->in.pll_icpmset_p = 0; /* 0, reg: 0x04f4, bit 0 - 2 */
+ pdb->in.pll_icpmset_m = 0; /* 0, reg: 0x04f4, bit 3 - 5 */
+ pdb->in.pll_icpcset_p = 0; /* 0, reg: 0x04f8, bit 0 - 2 */
+ pdb->in.pll_icpcset_m = 0; /* 0, reg: 0x04f8, bit 3 - 5 */
+ pdb->in.pll_lpf_res1 = 3; /* 3, reg: 0x0504, bit 0 - 3 */
+ pdb->in.pll_lpf_cap1 = 11; /* 11, reg: 0x0500, bit 0 - 3 */
+ pdb->in.pll_lpf_cap2 = 1; /* 1, reg: 0x0500, bit 4 - 7 */
+ pdb->in.pll_iptat_trim = 7;
+ pdb->in.pll_c3ctrl = 2; /* 2 */
+ pdb->in.pll_r3ctrl = 1; /* 1 */
+}
+
+static void pll_8996_ssc_calc(struct mdss_pll_resources *pll,
+ struct dsi_pll_db *pdb)
+{
+ u32 period, ssc_period;
+ u32 ref, rem;
+ s64 step_size;
+
+ pr_debug("%s: vco=%lld ref=%lld\n", __func__,
+ pll->vco_current_rate, pll->vco_ref_clk_rate);
+
+ ssc_period = pdb->in.ssc_freq / 500;
+ period = (unsigned long)pll->vco_ref_clk_rate / 1000;
+ ssc_period = CEIL(period, ssc_period);
+ ssc_period -= 1;
+ pdb->out.ssc_period = ssc_period;
+
+ pr_debug("%s: ssc, freq=%d spread=%d period=%d\n", __func__,
+ pdb->in.ssc_freq, pdb->in.ssc_spread, pdb->out.ssc_period);
+
+ step_size = (u32)pll->vco_current_rate;
+ ref = pll->vco_ref_clk_rate;
+ ref /= 1000;
+ step_size = div_s64(step_size, ref);
+ step_size <<= 20;
+ step_size = div_s64(step_size, 1000);
+ step_size *= pdb->in.ssc_spread;
+ step_size = div_s64(step_size, 1000);
+ step_size *= (pdb->in.ssc_adj_period + 1);
+
+ rem = 0;
+ step_size = div_s64_rem(step_size, ssc_period + 1, &rem);
+ if (rem)
+ step_size++;
+
+ pr_debug("%s: step_size=%lld\n", __func__, step_size);
+
+ step_size &= 0x0ffff; /* take lower 16 bits */
+
+ pdb->out.ssc_step_size = step_size;
+}
+
+static void pll_8996_dec_frac_calc(struct mdss_pll_resources *pll,
+ struct dsi_pll_db *pdb)
+{
+ struct dsi_pll_input *pin = &pdb->in;
+ struct dsi_pll_output *pout = &pdb->out;
+ s64 multiplier = BIT(20);
+ s64 dec_start_multiple, dec_start, pll_comp_val;
+ s32 duration, div_frac_start;
+ s64 vco_clk_rate = pll->vco_current_rate;
+ s64 fref = pll->vco_ref_clk_rate;
+
+ pr_debug("vco_clk_rate=%lld ref_clk_rate=%lld\n",
+ vco_clk_rate, fref);
+
+ dec_start_multiple = div_s64(vco_clk_rate * multiplier, fref);
+ div_s64_rem(dec_start_multiple, multiplier, &div_frac_start);
+
+ dec_start = div_s64(dec_start_multiple, multiplier);
+
+ pout->dec_start = (u32)dec_start;
+ pout->div_frac_start = div_frac_start;
+
+ if (pin->plllock_cnt == 0)
+ duration = 1024;
+ else if (pin->plllock_cnt == 1)
+ duration = 256;
+ else if (pin->plllock_cnt == 2)
+ duration = 128;
+ else
+ duration = 32;
+
+ pll_comp_val = duration * dec_start_multiple;
+ pll_comp_val = div_s64(pll_comp_val, multiplier);
+ do_div(pll_comp_val, 10);
+
+ pout->plllock_cmp = (u32)pll_comp_val;
+
+ pout->pll_txclk_en = 1;
+ if (pll->revision == MSM8996_DSI_PLL_REVISION_2)
+ pout->cmn_ldo_cntrl = 0x3c;
+ else
+ pout->cmn_ldo_cntrl = 0x1c;
+}
+
+static u32 pll_8996_kvco_slop(u32 vrate)
+{
+ u32 slop = 0;
+
+ if (vrate > 1300000000UL && vrate <= 1800000000UL)
+ slop = 600;
+ else if (vrate > 1800000000UL && vrate < 2300000000UL)
+ slop = 400;
+ else if (vrate > 2300000000UL && vrate < 2600000000UL)
+ slop = 280;
+
+ return slop;
+}
+
+static void pll_8996_calc_vco_count(struct dsi_pll_db *pdb,
+ s64 vco_clk_rate, s64 fref)
+{
+ struct dsi_pll_input *pin = &pdb->in;
+ struct dsi_pll_output *pout = &pdb->out;
+ s64 data;
+ u32 cnt;
+
+ data = fref * pin->vco_measure_time;
+ do_div(data, 1000000);
+ data &= 0x03ff; /* 10 bits */
+ data -= 2;
+ pout->pll_vco_div_ref = data;
+
+ data = (unsigned long)vco_clk_rate / 1000000; /* unit is Mhz */
+ data *= pin->vco_measure_time;
+ do_div(data, 10);
+ pout->pll_vco_count = data; /* reg: 0x0474, 0x0478 */
+
+ data = fref * pin->kvco_measure_time;
+ do_div(data, 1000000);
+ data &= 0x03ff; /* 10 bits */
+ data -= 1;
+ pout->pll_kvco_div_ref = data;
+
+ cnt = pll_8996_kvco_slop(vco_clk_rate);
+ cnt *= 2;
+ do_div(cnt, 100);
+ cnt *= pin->kvco_measure_time;
+ pout->pll_kvco_count = cnt;
+
+ pout->pll_misc1 = 16;
+ pout->pll_resetsm_cntrl = 48;
+ pout->pll_resetsm_cntrl2 = pin->bandgap_timer << 3;
+ pout->pll_resetsm_cntrl5 = pin->pll_wakeup_timer;
+ pout->pll_kvco_code = 0;
+}
+
+static void pll_db_commit_ssc(struct mdss_pll_resources *pll,
+ struct dsi_pll_db *pdb)
+{
+ void __iomem *pll_base = pll->pll_base;
+ struct dsi_pll_input *pin = &pdb->in;
+ struct dsi_pll_output *pout = &pdb->out;
+ char data;
+
+ data = pin->ssc_adj_period;
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_ADJ_PER1, data);
+ data = (pin->ssc_adj_period >> 8);
+ data &= 0x03;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_ADJ_PER2, data);
+
+ data = pout->ssc_period;
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_PER1, data);
+ data = (pout->ssc_period >> 8);
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_PER2, data);
+
+ data = pout->ssc_step_size;
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_STEP_SIZE1, data);
+ data = (pout->ssc_step_size >> 8);
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_STEP_SIZE2, data);
+
+ data = (pin->ssc_center & 0x01);
+ data <<= 1;
+ data |= 0x01; /* enable */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_EN_CENTER, data);
+
+ wmb(); /* make sure register committed */
+}
+
+static void pll_db_commit_common(struct mdss_pll_resources *pll,
+ struct dsi_pll_db *pdb)
+{
+ void __iomem *pll_base = pll->pll_base;
+ struct dsi_pll_input *pin = &pdb->in;
+ struct dsi_pll_output *pout = &pdb->out;
+ char data;
+
+ /* confgiure the non frequency dependent pll registers */
+ data = 0;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SYSCLK_EN_RESET, data);
+
+ /* DSIPHY_PLL_CLKBUFLR_EN updated at dsi phy */
+
+ data = pout->pll_txclk_en;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_TXCLK_EN, data);
+
+ data = pout->pll_resetsm_cntrl;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_RESETSM_CNTRL, data);
+ data = pout->pll_resetsm_cntrl2;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_RESETSM_CNTRL2, data);
+ data = pout->pll_resetsm_cntrl5;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_RESETSM_CNTRL5, data);
+
+ data = pout->pll_vco_div_ref;
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_VCO_DIV_REF1, data);
+ data = (pout->pll_vco_div_ref >> 8);
+ data &= 0x03;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_VCO_DIV_REF2, data);
+
+ data = pout->pll_kvco_div_ref;
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_KVCO_DIV_REF1, data);
+ data = (pout->pll_kvco_div_ref >> 8);
+ data &= 0x03;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_KVCO_DIV_REF2, data);
+
+ data = pout->pll_misc1;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_MISC1, data);
+
+ data = pin->pll_ie_trim;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_IE_TRIM, data);
+
+ data = pin->pll_ip_trim;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_IP_TRIM, data);
+
+ data = ((pin->pll_cpmset_cur << 3) | pin->pll_cpcset_cur);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_CP_SET_CUR, data);
+
+ data = ((pin->pll_icpcset_p << 3) | pin->pll_icpcset_m);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_ICPCSET, data);
+
+ data = ((pin->pll_icpmset_p << 3) | pin->pll_icpcset_m);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_ICPMSET, data);
+
+ data = ((pin->pll_icpmset << 3) | pin->pll_icpcset);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_ICP_SET, data);
+
+ data = ((pdb->in.pll_lpf_cap2 << 4) | pdb->in.pll_lpf_cap1);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_LPF1, data);
+
+ data = pin->pll_iptat_trim;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_IPTAT_TRIM, data);
+
+ data = (pdb->in.pll_c3ctrl | (pdb->in.pll_r3ctrl << 4));
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_CRCTRL, data);
+}
+
+static void pll_db_commit_8996(struct mdss_pll_resources *pll,
+ struct dsi_pll_db *pdb)
+{
+ void __iomem *pll_base = pll->pll_base;
+ struct dsi_pll_input *pin = &pdb->in;
+ struct dsi_pll_output *pout = &pdb->out;
+ char data;
+
+ data = pout->cmn_ldo_cntrl;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_LDO_CNTRL, data);
+
+ pll_db_commit_common(pll, pdb);
+
+ /* de assert pll start and apply pll sw reset */
+ /* stop pll */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_PLL_CNTRL, 0);
+
+ /* pll sw reset */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CTRL_1, 0x20);
+ wmb(); /* make sure register committed */
+ udelay(10);
+
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CTRL_1, 0);
+ wmb(); /* make sure register committed */
+
+ data = pdb->in.dsiclk_sel; /* set dsiclk_sel = 1 */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CLK_CFG1, data);
+
+ data = 0xff; /* data, clk, pll normal operation */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CTRL_0, data);
+
+ /* confgiure the frequency dependent pll registers */
+ data = pout->dec_start;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_DEC_START, data);
+
+ data = pout->div_frac_start;
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_DIV_FRAC_START1, data);
+ data = (pout->div_frac_start >> 8);
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_DIV_FRAC_START2, data);
+ data = (pout->div_frac_start >> 16);
+ data &= 0x0f;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_DIV_FRAC_START3, data);
+
+ data = pout->plllock_cmp;
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLLLOCK_CMP1, data);
+ data = (pout->plllock_cmp >> 8);
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLLLOCK_CMP2, data);
+ data = (pout->plllock_cmp >> 16);
+ data &= 0x03;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLLLOCK_CMP3, data);
+
+ data = ((pin->plllock_cnt << 1) | (pin->plllock_rng << 3));
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLLLOCK_CMP_EN, data);
+
+ data = pout->pll_vco_count;
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_VCO_COUNT1, data);
+ data = (pout->pll_vco_count >> 8);
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_VCO_COUNT2, data);
+
+ data = pout->pll_kvco_count;
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_KVCO_COUNT1, data);
+ data = (pout->pll_kvco_count >> 8);
+ data &= 0x03;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_KVCO_COUNT2, data);
+
+ /*
+ * tx_band = pll_postdiv
+ * 0: divided by 1 <== for now
+ * 1: divided by 2
+ * 2: divided by 4
+ * 3: divided by 8
+ */
+ data = (((pout->pll_postdiv - 1) << 4) | pdb->in.pll_lpf_res1);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_LPF2_POSTDIV, data);
+
+ data = (pout->pll_n1div | (pout->pll_n2div << 4));
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CLK_CFG0, data);
+
+ if (pll->ssc_en)
+ pll_db_commit_ssc(pll, pdb);
+
+ wmb(); /* make sure register committed */
+}
+
+/*
+ * pll_source_finding:
+ * Both GLBL_TEST_CTRL and CLKBUFLR_EN are configured
+ * at mdss_dsi_8996_phy_config()
+ */
+static int pll_source_finding(struct mdss_pll_resources *pll)
+{
+ u32 clk_buf_en;
+ u32 glbl_test_ctrl;
+
+ glbl_test_ctrl = MDSS_PLL_REG_R(pll->pll_base,
+ DSIPHY_CMN_GLBL_TEST_CTRL);
+ clk_buf_en = MDSS_PLL_REG_R(pll->pll_base,
+ DSIPHY_PLL_CLKBUFLR_EN);
+
+ glbl_test_ctrl &= BIT(2);
+ glbl_test_ctrl >>= 2;
+
+ pr_debug("%s: pll=%d clk_buf_en=%x glbl_test_ctrl=%x\n",
+ __func__, pll->index, clk_buf_en, glbl_test_ctrl);
+
+ clk_buf_en &= (PLL_OUTPUT_RIGHT | PLL_OUTPUT_LEFT);
+
+ if ((glbl_test_ctrl == PLL_SOURCE_FROM_LEFT) &&
+ (clk_buf_en == PLL_OUTPUT_BOTH))
+ return PLL_MASTER;
+
+ if ((glbl_test_ctrl == PLL_SOURCE_FROM_RIGHT) &&
+ (clk_buf_en == PLL_OUTPUT_NONE))
+ return PLL_SLAVE;
+
+ if ((glbl_test_ctrl == PLL_SOURCE_FROM_LEFT) &&
+ (clk_buf_en == PLL_OUTPUT_RIGHT))
+ return PLL_STANDALONE;
+
+ pr_debug("%s: Error pll setup, clk_buf_en=%x glbl_test_ctrl=%x\n",
+ __func__, clk_buf_en, glbl_test_ctrl);
+
+ return PLL_UNKNOWN;
+}
+
+static void pll_source_setup(struct mdss_pll_resources *pll)
+{
+ int status;
+ struct dsi_pll_db *pdb = (struct dsi_pll_db *)pll->priv;
+ struct mdss_pll_resources *other;
+
+ if (pdb->source_setup_done)
+ return;
+
+ pdb->source_setup_done++;
+
+ status = pll_source_finding(pll);
+
+ if (status == PLL_STANDALONE || status == PLL_UNKNOWN)
+ return;
+
+ other = pdb->next->pll;
+ if (!other)
+ return;
+
+ pr_debug("%s: status=%d pll=%d other=%d\n", __func__,
+ status, pll->index, other->index);
+
+ if (status == PLL_MASTER)
+ pll->slave = other;
+ else
+ other->slave = pll;
+}
+
+int pll_vco_set_rate_8996(struct clk *c, unsigned long rate)
+{
+ int rc;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+ struct mdss_pll_resources *slave;
+ struct dsi_pll_db *pdb;
+
+ pdb = (struct dsi_pll_db *)pll->priv;
+ if (!pdb) {
+ pr_err("No prov found\n");
+ return -EINVAL;
+ }
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi plla=%d\n", pll->index);
+ return rc;
+ }
+
+ pll_source_setup(pll);
+
+ pr_debug("%s: ndx=%d base=%p rate=%lu slave=%p\n", __func__,
+ pll->index, pll->pll_base, rate, pll->slave);
+
+ pll->vco_current_rate = rate;
+ pll->vco_ref_clk_rate = vco->ref_clk_rate;
+
+ mdss_dsi_pll_8996_input_init(pll, pdb);
+
+ pll_8996_dec_frac_calc(pll, pdb);
+
+ if (pll->ssc_en)
+ pll_8996_ssc_calc(pll, pdb);
+
+ pll_8996_calc_vco_count(pdb, pll->vco_current_rate,
+ pll->vco_ref_clk_rate);
+
+ /* commit slave if split display is enabled */
+ slave = pll->slave;
+ if (slave)
+ pll_db_commit_8996(slave, pdb);
+
+ /* commit master itself */
+ pll_db_commit_8996(pll, pdb);
+
+ mdss_pll_resource_enable(pll, false);
+
+ return rc;
+}
+
+static void shadow_pll_dynamic_refresh_8996(struct mdss_pll_resources *pll,
+ struct dsi_pll_db *pdb)
+{
+ struct dsi_pll_output *pout = &pdb->out;
+
+ MDSS_DYN_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_CTRL20,
+ DSIPHY_CMN_CTRL_0, DSIPHY_PLL_SYSCLK_EN_RESET,
+ 0xFF, 0x0);
+ MDSS_DYN_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_CTRL21,
+ DSIPHY_PLL_DEC_START, DSIPHY_PLL_DIV_FRAC_START1,
+ pout->dec_start, (pout->div_frac_start & 0x0FF));
+ MDSS_DYN_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_CTRL22,
+ DSIPHY_PLL_DIV_FRAC_START2, DSIPHY_PLL_DIV_FRAC_START3,
+ ((pout->div_frac_start >> 8) & 0x0FF),
+ ((pout->div_frac_start >> 16) & 0x0F));
+ MDSS_DYN_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_CTRL23,
+ DSIPHY_PLL_PLLLOCK_CMP1, DSIPHY_PLL_PLLLOCK_CMP2,
+ (pout->plllock_cmp & 0x0FF),
+ ((pout->plllock_cmp >> 8) & 0x0FF));
+ MDSS_DYN_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_CTRL24,
+ DSIPHY_PLL_PLLLOCK_CMP3, DSIPHY_PLL_PLL_VCO_TUNE,
+ ((pout->plllock_cmp >> 16) & 0x03),
+ (pll->cache_pll_trim_codes[1] | BIT(7))); /* VCO tune*/
+ MDSS_DYN_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_CTRL25,
+ DSIPHY_PLL_KVCO_CODE, DSIPHY_PLL_RESETSM_CNTRL,
+ (pll->cache_pll_trim_codes[0] | BIT(5)), 0x38);
+ MDSS_DYN_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_CTRL26,
+ DSIPHY_PLL_PLL_LPF2_POSTDIV, DSIPHY_CMN_PLL_CNTRL,
+ (((pout->pll_postdiv - 1) << 4) | pdb->in.pll_lpf_res1), 0x01);
+ MDSS_DYN_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_CTRL27,
+ DSIPHY_CMN_PLL_CNTRL, DSIPHY_CMN_PLL_CNTRL,
+ 0x01, 0x01);
+ MDSS_DYN_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_CTRL28,
+ DSIPHY_CMN_PLL_CNTRL, DSIPHY_CMN_PLL_CNTRL,
+ 0x01, 0x01);
+ MDSS_DYN_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_CTRL29,
+ DSIPHY_CMN_PLL_CNTRL, DSIPHY_CMN_PLL_CNTRL,
+ 0x01, 0x01);
+ MDSS_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR, 0x0000001E);
+ MDSS_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR2, 0x001FFE00);
+
+ /*
+ * Ensure all the dynamic refresh registers are written before
+ * dynamic refresh to change the fps is triggered
+ */
+ wmb();
+}
+
+int shadow_pll_vco_set_rate_8996(struct clk *c, unsigned long rate)
+{
+ int rc;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+ struct dsi_pll_db *pdb;
+ s64 vco_clk_rate = (s64)rate;
+
+ if (!pll) {
+ pr_err("PLL data not found\n");
+ return -EINVAL;
+ }
+
+ pdb = pll->priv;
+ if (!pdb) {
+ pr_err("No priv data found\n");
+ return -EINVAL;
+ }
+
+ rc = mdss_pll_read_stored_trim_codes(pll, vco_clk_rate);
+ if (rc) {
+ pr_err("cannot find pll codes rate=%lld\n", vco_clk_rate);
+ return -EINVAL;
+ }
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi plla=%d\n", pll->index);
+ return rc;
+ }
+
+ pr_debug("%s: ndx=%d base=%p rate=%lu\n", __func__,
+ pll->index, pll->pll_base, rate);
+
+ pll->vco_current_rate = rate;
+ pll->vco_ref_clk_rate = vco->ref_clk_rate;
+
+ mdss_dsi_pll_8996_input_init(pll, pdb);
+
+ pll_8996_dec_frac_calc(pll, pdb);
+
+ pll_8996_calc_vco_count(pdb, pll->vco_current_rate,
+ pll->vco_ref_clk_rate);
+
+ shadow_pll_dynamic_refresh_8996(pll, pdb);
+
+ rc = mdss_pll_resource_enable(pll, false);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi plla=%d\n", pll->index);
+ return rc;
+ }
+
+ return rc;
+}
+
+unsigned long pll_vco_get_rate_8996(struct clk *c)
+{
+ u64 vco_rate, multiplier = BIT(20);
+ s32 div_frac_start;
+ u32 dec_start;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ u64 ref_clk = vco->ref_clk_rate;
+ int rc;
+ struct mdss_pll_resources *pll = vco->priv;
+
+ if (is_gdsc_disabled(pll))
+ return 0;
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll=%d\n", pll->index);
+ return rc;
+ }
+
+ dec_start = MDSS_PLL_REG_R(pll->pll_base,
+ DSIPHY_PLL_DEC_START);
+ dec_start &= 0x0ff;
+ pr_debug("dec_start = 0x%x\n", dec_start);
+
+ div_frac_start = (MDSS_PLL_REG_R(pll->pll_base,
+ DSIPHY_PLL_DIV_FRAC_START3) & 0x0f) << 16;
+ div_frac_start |= (MDSS_PLL_REG_R(pll->pll_base,
+ DSIPHY_PLL_DIV_FRAC_START2) & 0x0ff) << 8;
+ div_frac_start |= MDSS_PLL_REG_R(pll->pll_base,
+ DSIPHY_PLL_DIV_FRAC_START1) & 0x0ff;
+ pr_debug("div_frac_start = 0x%x\n", div_frac_start);
+
+ vco_rate = ref_clk * dec_start;
+ vco_rate += ((ref_clk * div_frac_start) / multiplier);
+
+ pr_debug("returning vco rate = %lu\n", (unsigned long)vco_rate);
+
+ mdss_pll_resource_enable(pll, false);
+
+ return (unsigned long)vco_rate;
+}
+
+long pll_vco_round_rate_8996(struct clk *c, unsigned long rate)
+{
+ unsigned long rrate = rate;
+ u32 div;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+
+ div = vco->min_rate / rate;
+ if (div > 15) {
+ /* rate < 86.67 Mhz */
+ pr_err("rate=%lu NOT supportted\n", rate);
+ return -EINVAL;
+ }
+
+ if (rate < vco->min_rate)
+ rrate = vco->min_rate;
+ if (rate > vco->max_rate)
+ rrate = vco->max_rate;
+
+ return rrate;
+}
+
+enum handoff pll_vco_handoff_8996(struct clk *c)
+{
+ int rc;
+ enum handoff ret = HANDOFF_DISABLED_CLK;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+
+ if (is_gdsc_disabled(pll))
+ return HANDOFF_DISABLED_CLK;
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll=%d\n", pll->index);
+ return ret;
+ }
+
+ if (pll_is_pll_locked_8996(pll)) {
+ pll->handoff_resources = true;
+ pll->pll_on = true;
+ c->rate = pll_vco_get_rate_8996(c);
+ ret = HANDOFF_ENABLED_CLK;
+ } else {
+ mdss_pll_resource_enable(pll, false);
+ }
+
+ return ret;
+}
+
+enum handoff shadow_pll_vco_handoff_8996(struct clk *c)
+{
+ return HANDOFF_DISABLED_CLK;
+}
+
+int pll_vco_prepare_8996(struct clk *c)
+{
+ int rc = 0;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+
+ if (!pll) {
+ pr_err("Dsi pll resources are not available\n");
+ return -EINVAL;
+ }
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("ndx=%d Failed to enable mdss dsi pll resources\n",
+ pll->index);
+ return rc;
+ }
+
+ if ((pll->vco_cached_rate != 0)
+ && (pll->vco_cached_rate == c->rate)) {
+ rc = c->ops->set_rate(c, pll->vco_cached_rate);
+ if (rc) {
+ pr_err("index=%d vco_set_rate failed. rc=%d\n",
+ rc, pll->index);
+ mdss_pll_resource_enable(pll, false);
+ goto error;
+ }
+ }
+
+ rc = dsi_pll_enable(c);
+
+ if (rc) {
+ mdss_pll_resource_enable(pll, false);
+ pr_err("ndx=%d failed to enable dsi pll\n", pll->index);
+ }
+
+error:
+ return rc;
+}
+
+void pll_vco_unprepare_8996(struct clk *c)
+{
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+
+ if (!pll) {
+ pr_err("Dsi pll resources are not available\n");
+ return;
+ }
+
+ pll->vco_cached_rate = c->rate;
+ dsi_pll_disable(c);
+}
diff --git a/drivers/clk/qcom/mdss/mdss-dsi-pll-8996.c b/drivers/clk/qcom/mdss/mdss-dsi-pll-8996.c
new file mode 100644
index 000000000000..1de1b997a041
--- /dev/null
+++ b/drivers/clk/qcom/mdss/mdss-dsi-pll-8996.c
@@ -0,0 +1,566 @@
+/* Copyright (c) 2015-2016, 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/err.h>
+#include <linux/delay.h>
+#include <linux/clk/msm-clk-provider.h>
+#include <linux/clk/msm-clk.h>
+#include <linux/workqueue.h>
+#include <linux/clk/msm-clock-generic.h>
+#include <dt-bindings/clock/msm-clocks-8996.h>
+
+#include "mdss-pll.h"
+#include "mdss-dsi-pll.h"
+#include "mdss-dsi-pll-8996.h"
+
+#define VCO_DELAY_USEC 1
+
+static struct dsi_pll_db pll_db[DSI_PLL_NUM];
+
+static struct clk_ops n2_clk_src_ops;
+static struct clk_ops shadow_n2_clk_src_ops;
+static struct clk_ops byte_clk_src_ops;
+static struct clk_ops post_n1_div_clk_src_ops;
+static struct clk_ops shadow_post_n1_div_clk_src_ops;
+
+static struct clk_ops clk_ops_gen_mux_dsi;
+
+/* Op structures */
+static struct clk_ops clk_ops_dsi_vco = {
+ .set_rate = pll_vco_set_rate_8996,
+ .round_rate = pll_vco_round_rate_8996,
+ .handoff = pll_vco_handoff_8996,
+ .prepare = pll_vco_prepare_8996,
+ .unprepare = pll_vco_unprepare_8996,
+};
+
+static struct clk_div_ops post_n1_div_ops = {
+ .set_div = post_n1_div_set_div,
+ .get_div = post_n1_div_get_div,
+};
+
+static struct clk_div_ops n2_div_ops = { /* hr_oclk3 */
+ .set_div = n2_div_set_div,
+ .get_div = n2_div_get_div,
+};
+
+static struct clk_mux_ops mdss_byte_mux_ops = {
+ .set_mux_sel = set_mdss_byte_mux_sel_8996,
+ .get_mux_sel = get_mdss_byte_mux_sel_8996,
+};
+
+static struct clk_mux_ops mdss_pixel_mux_ops = {
+ .set_mux_sel = set_mdss_pixel_mux_sel_8996,
+ .get_mux_sel = get_mdss_pixel_mux_sel_8996,
+};
+
+/* Shadow ops for dynamic refresh */
+static struct clk_ops clk_ops_shadow_dsi_vco = {
+ .set_rate = shadow_pll_vco_set_rate_8996,
+ .round_rate = pll_vco_round_rate_8996,
+ .handoff = shadow_pll_vco_handoff_8996,
+};
+
+static struct clk_div_ops shadow_post_n1_div_ops = {
+ .set_div = post_n1_div_set_div,
+};
+
+static struct clk_div_ops shadow_n2_div_ops = {
+ .set_div = shadow_n2_div_set_div,
+};
+
+static struct dsi_pll_vco_clk dsi0pll_vco_clk = {
+ .ref_clk_rate = 19200000UL,
+ .min_rate = 1300000000UL,
+ .max_rate = 2600000000UL,
+ .pll_en_seq_cnt = 1,
+ .pll_enable_seqs[0] = dsi_pll_enable_seq_8996,
+ .c = {
+ .dbg_name = "dsi0pll_vco_clk_8996",
+ .ops = &clk_ops_dsi_vco,
+ CLK_INIT(dsi0pll_vco_clk.c),
+ },
+};
+
+static struct dsi_pll_vco_clk dsi0pll_shadow_vco_clk = {
+ .ref_clk_rate = 19200000u,
+ .min_rate = 1300000000u,
+ .max_rate = 2600000000u,
+ .c = {
+ .dbg_name = "dsi0pll_shadow_vco_clk",
+ .ops = &clk_ops_shadow_dsi_vco,
+ CLK_INIT(dsi0pll_shadow_vco_clk.c),
+ },
+};
+
+static struct dsi_pll_vco_clk dsi1pll_vco_clk = {
+ .ref_clk_rate = 19200000UL,
+ .min_rate = 1300000000UL,
+ .max_rate = 2600000000UL,
+ .pll_en_seq_cnt = 1,
+ .pll_enable_seqs[0] = dsi_pll_enable_seq_8996,
+ .c = {
+ .dbg_name = "dsi1pll_vco_clk_8996",
+ .ops = &clk_ops_dsi_vco,
+ CLK_INIT(dsi1pll_vco_clk.c),
+ },
+};
+
+static struct dsi_pll_vco_clk dsi1pll_shadow_vco_clk = {
+ .ref_clk_rate = 19200000u,
+ .min_rate = 1300000000u,
+ .max_rate = 2600000000u,
+ .pll_en_seq_cnt = 1,
+ .pll_enable_seqs[0] = dsi_pll_enable_seq_8996,
+ .c = {
+ .dbg_name = "dsi1pll_shadow_vco_clk",
+ .ops = &clk_ops_shadow_dsi_vco,
+ CLK_INIT(dsi1pll_shadow_vco_clk.c),
+ },
+};
+
+static struct div_clk dsi0pll_post_n1_div_clk = {
+ .data = {
+ .max_div = 15,
+ .min_div = 1,
+ },
+ .ops = &post_n1_div_ops,
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_post_n1_div_clk",
+ .ops = &post_n1_div_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_post_n1_div_clk.c),
+ },
+};
+
+static struct div_clk dsi0pll_shadow_post_n1_div_clk = {
+ .data = {
+ .max_div = 15,
+ .min_div = 1,
+ },
+ .ops = &shadow_post_n1_div_ops,
+ .c = {
+ .parent = &dsi0pll_shadow_vco_clk.c,
+ .dbg_name = "dsi0pll_shadow_post_n1_div_clk",
+ .ops = &shadow_post_n1_div_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_shadow_post_n1_div_clk.c),
+ },
+};
+
+static struct div_clk dsi1pll_post_n1_div_clk = {
+ .data = {
+ .max_div = 15,
+ .min_div = 1,
+ },
+ .ops = &post_n1_div_ops,
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_post_n1_div_clk",
+ .ops = &post_n1_div_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_post_n1_div_clk.c),
+ },
+};
+
+static struct div_clk dsi1pll_shadow_post_n1_div_clk = {
+ .data = {
+ .max_div = 15,
+ .min_div = 1,
+ },
+ .ops = &shadow_post_n1_div_ops,
+ .c = {
+ .parent = &dsi1pll_shadow_vco_clk.c,
+ .dbg_name = "dsi1pll_shadow_post_n1_div_clk",
+ .ops = &shadow_post_n1_div_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_shadow_post_n1_div_clk.c),
+ },
+};
+
+static struct div_clk dsi0pll_n2_div_clk = {
+ .data = {
+ .max_div = 15,
+ .min_div = 1,
+ },
+ .ops = &n2_div_ops,
+ .c = {
+ .parent = &dsi0pll_post_n1_div_clk.c,
+ .dbg_name = "dsi0pll_n2_div_clk",
+ .ops = &n2_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_n2_div_clk.c),
+ },
+};
+
+static struct div_clk dsi0pll_shadow_n2_div_clk = {
+ .data = {
+ .max_div = 15,
+ .min_div = 1,
+ },
+ .ops = &shadow_n2_div_ops,
+ .c = {
+ .parent = &dsi0pll_shadow_post_n1_div_clk.c,
+ .dbg_name = "dsi0pll_shadow_n2_div_clk",
+ .ops = &shadow_n2_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_shadow_n2_div_clk.c),
+ },
+};
+
+static struct div_clk dsi1pll_n2_div_clk = {
+ .data = {
+ .max_div = 15,
+ .min_div = 1,
+ },
+ .ops = &n2_div_ops,
+ .c = {
+ .parent = &dsi1pll_post_n1_div_clk.c,
+ .dbg_name = "dsi1pll_n2_div_clk",
+ .ops = &n2_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_n2_div_clk.c),
+ },
+};
+
+static struct div_clk dsi1pll_shadow_n2_div_clk = {
+ .data = {
+ .max_div = 15,
+ .min_div = 1,
+ },
+ .ops = &shadow_n2_div_ops,
+ .c = {
+ .parent = &dsi1pll_shadow_post_n1_div_clk.c,
+ .dbg_name = "dsi1pll_shadow_n2_div_clk",
+ .ops = &shadow_n2_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_shadow_n2_div_clk.c),
+ },
+};
+
+static struct div_clk dsi0pll_pixel_clk_src = {
+ .data = {
+ .div = 2,
+ .min_div = 2,
+ .max_div = 2,
+ },
+ .c = {
+ .parent = &dsi0pll_n2_div_clk.c,
+ .dbg_name = "dsi0pll_pixel_clk_src",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_pixel_clk_src.c),
+ },
+};
+
+static struct div_clk dsi0pll_shadow_pixel_clk_src = {
+ .data = {
+ .div = 2,
+ .min_div = 2,
+ .max_div = 2,
+ },
+ .c = {
+ .parent = &dsi0pll_shadow_n2_div_clk.c,
+ .dbg_name = "dsi0pll_shadow_pixel_clk_src",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_shadow_pixel_clk_src.c),
+ },
+};
+
+static struct div_clk dsi1pll_pixel_clk_src = {
+ .data = {
+ .div = 2,
+ .min_div = 2,
+ .max_div = 2,
+ },
+ .c = {
+ .parent = &dsi1pll_n2_div_clk.c,
+ .dbg_name = "dsi1pll_pixel_clk_src",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_pixel_clk_src.c),
+ },
+};
+
+static struct div_clk dsi1pll_shadow_pixel_clk_src = {
+ .data = {
+ .div = 2,
+ .min_div = 2,
+ .max_div = 2,
+ },
+ .c = {
+ .parent = &dsi1pll_shadow_n2_div_clk.c,
+ .dbg_name = "dsi1pll_shadow_pixel_clk_src",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_shadow_pixel_clk_src.c),
+ },
+};
+
+static struct mux_clk dsi0pll_pixel_clk_mux = {
+ .num_parents = 2,
+ .parents = (struct clk_src[]) {
+ {&dsi0pll_pixel_clk_src.c, 0},
+ {&dsi0pll_shadow_pixel_clk_src.c, 1},
+ },
+ .ops = &mdss_pixel_mux_ops,
+ .c = {
+ .parent = &dsi0pll_pixel_clk_src.c,
+ .dbg_name = "dsi0pll_pixel_clk_mux",
+ .ops = &clk_ops_gen_mux_dsi,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_pixel_clk_mux.c),
+ }
+};
+
+static struct mux_clk dsi1pll_pixel_clk_mux = {
+ .num_parents = 2,
+ .parents = (struct clk_src[]) {
+ {&dsi1pll_pixel_clk_src.c, 0},
+ {&dsi1pll_shadow_pixel_clk_src.c, 1},
+ },
+ .ops = &mdss_pixel_mux_ops,
+ .c = {
+ .parent = &dsi1pll_pixel_clk_src.c,
+ .dbg_name = "dsi1pll_pixel_clk_mux",
+ .ops = &clk_ops_gen_mux_dsi,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_pixel_clk_mux.c),
+ }
+};
+
+static struct div_clk dsi0pll_byte_clk_src = {
+ .data = {
+ .div = 8,
+ .min_div = 8,
+ .max_div = 8,
+ },
+ .c = {
+ .parent = &dsi0pll_post_n1_div_clk.c,
+ .dbg_name = "dsi0pll_byte_clk_src",
+ .ops = &clk_ops_div,
+ CLK_INIT(dsi0pll_byte_clk_src.c),
+ },
+};
+
+static struct div_clk dsi0pll_shadow_byte_clk_src = {
+ .data = {
+ .div = 8,
+ .min_div = 8,
+ .max_div = 8,
+ },
+ .c = {
+ .parent = &dsi0pll_shadow_post_n1_div_clk.c,
+ .dbg_name = "dsi0pll_shadow_byte_clk_src",
+ .ops = &clk_ops_div,
+ CLK_INIT(dsi0pll_shadow_byte_clk_src.c),
+ },
+};
+
+static struct div_clk dsi1pll_byte_clk_src = {
+ .data = {
+ .div = 8,
+ .min_div = 8,
+ .max_div = 8,
+ },
+ .c = {
+ .parent = &dsi1pll_post_n1_div_clk.c,
+ .dbg_name = "dsi1pll_byte_clk_src",
+ .ops = &clk_ops_div,
+ CLK_INIT(dsi1pll_byte_clk_src.c),
+ },
+};
+
+static struct div_clk dsi1pll_shadow_byte_clk_src = {
+ .data = {
+ .div = 8,
+ .min_div = 8,
+ .max_div = 8,
+ },
+ .c = {
+ .parent = &dsi1pll_shadow_post_n1_div_clk.c,
+ .dbg_name = "dsi1pll_shadow_byte_clk_src",
+ .ops = &clk_ops_div,
+ CLK_INIT(dsi1pll_shadow_byte_clk_src.c),
+ },
+};
+
+static struct mux_clk dsi0pll_byte_clk_mux = {
+ .num_parents = 2,
+ .parents = (struct clk_src[]) {
+ {&dsi0pll_byte_clk_src.c, 0},
+ {&dsi0pll_shadow_byte_clk_src.c, 1},
+ },
+ .ops = &mdss_byte_mux_ops,
+ .c = {
+ .parent = &dsi0pll_byte_clk_src.c,
+ .dbg_name = "dsi0pll_byte_clk_mux",
+ .ops = &clk_ops_gen_mux_dsi,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_byte_clk_mux.c),
+ }
+};
+static struct mux_clk dsi1pll_byte_clk_mux = {
+ .num_parents = 2,
+ .parents = (struct clk_src[]) {
+ {&dsi1pll_byte_clk_src.c, 0},
+ {&dsi1pll_shadow_byte_clk_src.c, 1},
+ },
+ .ops = &mdss_byte_mux_ops,
+ .c = {
+ .parent = &dsi1pll_byte_clk_src.c,
+ .dbg_name = "dsi1pll_byte_clk_mux",
+ .ops = &clk_ops_gen_mux_dsi,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_byte_clk_mux.c),
+ }
+};
+
+static struct clk_lookup mdss_dsi_pllcc_8996[] = {
+ CLK_LIST(dsi0pll_byte_clk_mux),
+ CLK_LIST(dsi0pll_byte_clk_src),
+ CLK_LIST(dsi0pll_pixel_clk_mux),
+ CLK_LIST(dsi0pll_pixel_clk_src),
+ CLK_LIST(dsi0pll_n2_div_clk),
+ CLK_LIST(dsi0pll_post_n1_div_clk),
+ CLK_LIST(dsi0pll_vco_clk),
+ CLK_LIST(dsi0pll_shadow_byte_clk_src),
+ CLK_LIST(dsi0pll_shadow_pixel_clk_src),
+ CLK_LIST(dsi0pll_shadow_n2_div_clk),
+ CLK_LIST(dsi0pll_shadow_post_n1_div_clk),
+ CLK_LIST(dsi0pll_shadow_vco_clk),
+};
+
+static struct clk_lookup mdss_dsi_pllcc_8996_1[] = {
+ CLK_LIST(dsi1pll_byte_clk_mux),
+ CLK_LIST(dsi1pll_byte_clk_src),
+ CLK_LIST(dsi1pll_pixel_clk_mux),
+ CLK_LIST(dsi1pll_pixel_clk_src),
+ CLK_LIST(dsi1pll_n2_div_clk),
+ CLK_LIST(dsi1pll_post_n1_div_clk),
+ CLK_LIST(dsi1pll_vco_clk),
+ CLK_LIST(dsi1pll_shadow_byte_clk_src),
+ CLK_LIST(dsi1pll_shadow_pixel_clk_src),
+ CLK_LIST(dsi1pll_shadow_n2_div_clk),
+ CLK_LIST(dsi1pll_shadow_post_n1_div_clk),
+ CLK_LIST(dsi1pll_shadow_vco_clk),
+};
+
+int dsi_pll_clock_register_8996(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ int rc = 0, ndx;
+ int const ssc_freq_default = 31500; /* default h/w recommended value */
+ int const ssc_ppm_default = 5000; /* default h/w recommended value */
+ struct dsi_pll_db *pdb;
+
+ if (!pdev || !pdev->dev.of_node) {
+ pr_err("Invalid input parameters\n");
+ return -EINVAL;
+ }
+
+ if (!pll_res || !pll_res->pll_base) {
+ pr_err("Invalid PLL resources\n");
+ return -EPROBE_DEFER;
+ }
+
+ if (pll_res->index >= DSI_PLL_NUM) {
+ pr_err("pll ndx=%d is NOT supported\n", pll_res->index);
+ return -EINVAL;
+ }
+
+ ndx = pll_res->index;
+ pdb = &pll_db[ndx];
+ pll_res->priv = pdb;
+ pdb->pll = pll_res;
+ ndx++;
+ ndx %= DSI_PLL_NUM;
+ pdb->next = &pll_db[ndx];
+
+ /* Set clock source operations */
+
+ /* hr_oclk3, pixel */
+ n2_clk_src_ops = clk_ops_slave_div;
+ n2_clk_src_ops.prepare = mdss_pll_div_prepare;
+
+ shadow_n2_clk_src_ops = clk_ops_slave_div;
+
+ /* hr_ockl2, byte, vco pll */
+ post_n1_div_clk_src_ops = clk_ops_div;
+ post_n1_div_clk_src_ops.prepare = mdss_pll_div_prepare;
+
+ shadow_post_n1_div_clk_src_ops = clk_ops_div;
+
+ byte_clk_src_ops = clk_ops_div;
+ byte_clk_src_ops.prepare = mdss_pll_div_prepare;
+
+ clk_ops_gen_mux_dsi = clk_ops_gen_mux;
+ clk_ops_gen_mux_dsi.round_rate = parent_round_rate;
+ clk_ops_gen_mux_dsi.set_rate = parent_set_rate;
+
+ if (pll_res->ssc_en) {
+ if (!pll_res->ssc_freq)
+ pll_res->ssc_freq = ssc_freq_default;
+ if (!pll_res->ssc_ppm)
+ pll_res->ssc_ppm = ssc_ppm_default;
+ }
+
+ /* Set client data to mux, div and vco clocks. */
+ if (pll_res->index == DSI_PLL_1) {
+ dsi1pll_byte_clk_src.priv = pll_res;
+ dsi1pll_pixel_clk_src.priv = pll_res;
+ dsi1pll_post_n1_div_clk.priv = pll_res;
+ dsi1pll_n2_div_clk.priv = pll_res;
+ dsi1pll_vco_clk.priv = pll_res;
+
+ dsi1pll_shadow_byte_clk_src.priv = pll_res;
+ dsi1pll_shadow_pixel_clk_src.priv = pll_res;
+ dsi1pll_shadow_post_n1_div_clk.priv = pll_res;
+ dsi1pll_shadow_n2_div_clk.priv = pll_res;
+ dsi1pll_shadow_vco_clk.priv = pll_res;
+
+ pll_res->vco_delay = VCO_DELAY_USEC;
+ rc = of_msm_clock_register(pdev->dev.of_node,
+ mdss_dsi_pllcc_8996_1,
+ ARRAY_SIZE(mdss_dsi_pllcc_8996_1));
+ } else {
+ dsi0pll_byte_clk_src.priv = pll_res;
+ dsi0pll_pixel_clk_src.priv = pll_res;
+ dsi0pll_post_n1_div_clk.priv = pll_res;
+ dsi0pll_n2_div_clk.priv = pll_res;
+ dsi0pll_vco_clk.priv = pll_res;
+
+ dsi0pll_shadow_byte_clk_src.priv = pll_res;
+ dsi0pll_shadow_pixel_clk_src.priv = pll_res;
+ dsi0pll_shadow_post_n1_div_clk.priv = pll_res;
+ dsi0pll_shadow_n2_div_clk.priv = pll_res;
+ dsi0pll_shadow_vco_clk.priv = pll_res;
+
+ pll_res->vco_delay = VCO_DELAY_USEC;
+ rc = of_msm_clock_register(pdev->dev.of_node,
+ mdss_dsi_pllcc_8996,
+ ARRAY_SIZE(mdss_dsi_pllcc_8996));
+ }
+
+ if (!rc) {
+ pr_info("Registered DSI PLL ndx=%d clocks successfully\n",
+ pll_res->index);
+ }
+
+ return rc;
+}
diff --git a/drivers/clk/qcom/mdss/mdss-dsi-pll-8996.h b/drivers/clk/qcom/mdss/mdss-dsi-pll-8996.h
new file mode 100644
index 000000000000..611e79101d4f
--- /dev/null
+++ b/drivers/clk/qcom/mdss/mdss-dsi-pll-8996.h
@@ -0,0 +1,221 @@
+/* Copyright (c) 2015-2016, 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_DSI_PLL_8996_H
+#define MDSS_DSI_PLL_8996_H
+
+#define DSIPHY_CMN_CLK_CFG0 0x0010
+#define DSIPHY_CMN_CLK_CFG1 0x0014
+#define DSIPHY_CMN_GLBL_TEST_CTRL 0x0018
+
+#define DSIPHY_CMN_PLL_CNTRL 0x0048
+#define DSIPHY_CMN_CTRL_0 0x001c
+#define DSIPHY_CMN_CTRL_1 0x0020
+
+#define DSIPHY_CMN_LDO_CNTRL 0x004c
+
+#define DSIPHY_PLL_IE_TRIM 0x0400
+#define DSIPHY_PLL_IP_TRIM 0x0404
+
+#define DSIPHY_PLL_IPTAT_TRIM 0x0410
+
+#define DSIPHY_PLL_CLKBUFLR_EN 0x041c
+
+#define DSIPHY_PLL_SYSCLK_EN_RESET 0x0428
+#define DSIPHY_PLL_RESETSM_CNTRL 0x042c
+#define DSIPHY_PLL_RESETSM_CNTRL2 0x0430
+#define DSIPHY_PLL_RESETSM_CNTRL3 0x0434
+#define DSIPHY_PLL_RESETSM_CNTRL4 0x0438
+#define DSIPHY_PLL_RESETSM_CNTRL5 0x043c
+#define DSIPHY_PLL_KVCO_DIV_REF1 0x0440
+#define DSIPHY_PLL_KVCO_DIV_REF2 0x0444
+#define DSIPHY_PLL_KVCO_COUNT1 0x0448
+#define DSIPHY_PLL_KVCO_COUNT2 0x044c
+#define DSIPHY_PLL_VREF_CFG1 0x045c
+
+#define DSIPHY_PLL_KVCO_CODE 0x0458
+
+#define DSIPHY_PLL_VCO_DIV_REF1 0x046c
+#define DSIPHY_PLL_VCO_DIV_REF2 0x0470
+#define DSIPHY_PLL_VCO_COUNT1 0x0474
+#define DSIPHY_PLL_VCO_COUNT2 0x0478
+#define DSIPHY_PLL_PLLLOCK_CMP1 0x047c
+#define DSIPHY_PLL_PLLLOCK_CMP2 0x0480
+#define DSIPHY_PLL_PLLLOCK_CMP3 0x0484
+#define DSIPHY_PLL_PLLLOCK_CMP_EN 0x0488
+#define DSIPHY_PLL_PLL_VCO_TUNE 0x048C
+#define DSIPHY_PLL_DEC_START 0x0490
+#define DSIPHY_PLL_SSC_EN_CENTER 0x0494
+#define DSIPHY_PLL_SSC_ADJ_PER1 0x0498
+#define DSIPHY_PLL_SSC_ADJ_PER2 0x049c
+#define DSIPHY_PLL_SSC_PER1 0x04a0
+#define DSIPHY_PLL_SSC_PER2 0x04a4
+#define DSIPHY_PLL_SSC_STEP_SIZE1 0x04a8
+#define DSIPHY_PLL_SSC_STEP_SIZE2 0x04ac
+#define DSIPHY_PLL_DIV_FRAC_START1 0x04b4
+#define DSIPHY_PLL_DIV_FRAC_START2 0x04b8
+#define DSIPHY_PLL_DIV_FRAC_START3 0x04bc
+#define DSIPHY_PLL_TXCLK_EN 0x04c0
+#define DSIPHY_PLL_PLL_CRCTRL 0x04c4
+
+#define DSIPHY_PLL_RESET_SM_READY_STATUS 0x04cc
+
+#define DSIPHY_PLL_PLL_MISC1 0x04e8
+
+#define DSIPHY_PLL_CP_SET_CUR 0x04f0
+#define DSIPHY_PLL_PLL_ICPMSET 0x04f4
+#define DSIPHY_PLL_PLL_ICPCSET 0x04f8
+#define DSIPHY_PLL_PLL_ICP_SET 0x04fc
+#define DSIPHY_PLL_PLL_LPF1 0x0500
+#define DSIPHY_PLL_PLL_LPF2_POSTDIV 0x0504
+#define DSIPHY_PLL_PLL_BANDGAP 0x0508
+
+#define DSI_DYNAMIC_REFRESH_PLL_CTRL15 0x050
+#define DSI_DYNAMIC_REFRESH_PLL_CTRL19 0x060
+#define DSI_DYNAMIC_REFRESH_PLL_CTRL20 0x064
+#define DSI_DYNAMIC_REFRESH_PLL_CTRL21 0x068
+#define DSI_DYNAMIC_REFRESH_PLL_CTRL22 0x06C
+#define DSI_DYNAMIC_REFRESH_PLL_CTRL23 0x070
+#define DSI_DYNAMIC_REFRESH_PLL_CTRL24 0x074
+#define DSI_DYNAMIC_REFRESH_PLL_CTRL25 0x078
+#define DSI_DYNAMIC_REFRESH_PLL_CTRL26 0x07C
+#define DSI_DYNAMIC_REFRESH_PLL_CTRL27 0x080
+#define DSI_DYNAMIC_REFRESH_PLL_CTRL28 0x084
+#define DSI_DYNAMIC_REFRESH_PLL_CTRL29 0x088
+#define DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR 0x094
+#define DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR2 0x098
+
+struct dsi_pll_input {
+ u32 fref; /* 19.2 Mhz, reference clk */
+ u32 fdata; /* bit clock rate */
+ u32 dsiclk_sel; /* 1, reg: 0x0014 */
+ u32 n2div; /* 1, reg: 0x0010, bit 4-7 */
+ u32 ssc_en; /* 1, reg: 0x0494, bit 0 */
+ u32 ldo_en; /* 0, reg: 0x004c, bit 0 */
+
+ /* fixed */
+ u32 refclk_dbler_en; /* 0, reg: 0x04c0, bit 1 */
+ u32 vco_measure_time; /* 5, unknown */
+ u32 kvco_measure_time; /* 5, unknown */
+ u32 bandgap_timer; /* 4, reg: 0x0430, bit 3 - 5 */
+ u32 pll_wakeup_timer; /* 5, reg: 0x043c, bit 0 - 2 */
+ u32 plllock_cnt; /* 1, reg: 0x0488, bit 1 - 2 */
+ u32 plllock_rng; /* 1, reg: 0x0488, bit 3 - 4 */
+ u32 ssc_center; /* 0, reg: 0x0494, bit 1 */
+ u32 ssc_adj_period; /* 37, reg: 0x498, bit 0 - 9 */
+ u32 ssc_spread; /* 0.005 */
+ u32 ssc_freq; /* unknown */
+ u32 pll_ie_trim; /* 4, reg: 0x0400 */
+ u32 pll_ip_trim; /* 4, reg: 0x0404 */
+ u32 pll_iptat_trim; /* reg: 0x0410 */
+ u32 pll_cpcset_cur; /* 1, reg: 0x04f0, bit 0 - 2 */
+ u32 pll_cpmset_cur; /* 1, reg: 0x04f0, bit 3 - 5 */
+
+ u32 pll_icpmset; /* 4, reg: 0x04fc, bit 3 - 5 */
+ u32 pll_icpcset; /* 4, reg: 0x04fc, bit 0 - 2 */
+
+ u32 pll_icpmset_p; /* 0, reg: 0x04f4, bit 0 - 2 */
+ u32 pll_icpmset_m; /* 0, reg: 0x04f4, bit 3 - 5 */
+
+ u32 pll_icpcset_p; /* 0, reg: 0x04f8, bit 0 - 2 */
+ u32 pll_icpcset_m; /* 0, reg: 0x04f8, bit 3 - 5 */
+
+ u32 pll_lpf_res1; /* 3, reg: 0x0504, bit 0 - 3 */
+ u32 pll_lpf_cap1; /* 11, reg: 0x0500, bit 0 - 3 */
+ u32 pll_lpf_cap2; /* 1, reg: 0x0500, bit 4 - 7 */
+ u32 pll_c3ctrl; /* 2, reg: 0x04c4 */
+ u32 pll_r3ctrl; /* 1, reg: 0x04c4 */
+};
+
+struct dsi_pll_output {
+ u32 pll_txclk_en; /* reg: 0x04c0 */
+ u32 dec_start; /* reg: 0x0490 */
+ u32 div_frac_start; /* reg: 0x04b4, 0x4b8, 0x04bc */
+ u32 ssc_period; /* reg: 0x04a0, 0x04a4 */
+ u32 ssc_step_size; /* reg: 0x04a8, 0x04ac */
+ u32 plllock_cmp; /* reg: 0x047c, 0x0480, 0x0484 */
+ u32 pll_vco_div_ref; /* reg: 0x046c, 0x0470 */
+ u32 pll_vco_count; /* reg: 0x0474, 0x0478 */
+ u32 pll_kvco_div_ref; /* reg: 0x0440, 0x0444 */
+ u32 pll_kvco_count; /* reg: 0x0448, 0x044c */
+ u32 pll_misc1; /* reg: 0x04e8 */
+ u32 pll_lpf2_postdiv; /* reg: 0x0504 */
+ u32 pll_resetsm_cntrl; /* reg: 0x042c */
+ u32 pll_resetsm_cntrl2; /* reg: 0x0430 */
+ u32 pll_resetsm_cntrl5; /* reg: 0x043c */
+ u32 pll_kvco_code; /* reg: 0x0458 */
+
+ u32 cmn_clk_cfg0; /* reg: 0x0010 */
+ u32 cmn_clk_cfg1; /* reg: 0x0014 */
+ u32 cmn_ldo_cntrl; /* reg: 0x004c */
+
+ u32 pll_postdiv; /* vco */
+ u32 pll_n1div; /* vco */
+ u32 pll_n2div; /* hr_oclk3, pixel */
+ u32 fcvo;
+};
+
+enum {
+ DSI_PLL_0,
+ DSI_PLL_1,
+ DSI_PLL_NUM
+};
+
+struct dsi_pll_db {
+ struct dsi_pll_db *next;
+ struct mdss_pll_resources *pll;
+ struct dsi_pll_input in;
+ struct dsi_pll_output out;
+ int source_setup_done;
+};
+
+enum {
+ PLL_OUTPUT_NONE,
+ PLL_OUTPUT_RIGHT,
+ PLL_OUTPUT_LEFT,
+ PLL_OUTPUT_BOTH
+};
+
+enum {
+ PLL_SOURCE_FROM_LEFT,
+ PLL_SOURCE_FROM_RIGHT
+};
+
+enum {
+ PLL_UNKNOWN,
+ PLL_STANDALONE,
+ PLL_SLAVE,
+ PLL_MASTER
+};
+
+int pll_vco_set_rate_8996(struct clk *c, unsigned long rate);
+long pll_vco_round_rate_8996(struct clk *c, unsigned long rate);
+enum handoff pll_vco_handoff_8996(struct clk *c);
+enum handoff shadow_pll_vco_handoff_8996(struct clk *c);
+int shadow_post_n1_div_set_div(struct div_clk *clk, int div);
+int shadow_post_n1_div_get_div(struct div_clk *clk);
+int shadow_n2_div_set_div(struct div_clk *clk, int div);
+int shadow_n2_div_get_div(struct div_clk *clk);
+int shadow_pll_vco_set_rate_8996(struct clk *c, unsigned long rate);
+int pll_vco_prepare_8996(struct clk *c);
+void pll_vco_unprepare_8996(struct clk *c);
+int set_mdss_byte_mux_sel_8996(struct mux_clk *clk, int sel);
+int get_mdss_byte_mux_sel_8996(struct mux_clk *clk);
+int set_mdss_pixel_mux_sel_8996(struct mux_clk *clk, int sel);
+int get_mdss_pixel_mux_sel_8996(struct mux_clk *clk);
+int post_n1_div_set_div(struct div_clk *clk, int div);
+int post_n1_div_get_div(struct div_clk *clk);
+int n2_div_set_div(struct div_clk *clk, int div);
+int n2_div_get_div(struct div_clk *clk);
+int dsi_pll_enable_seq_8996(struct mdss_pll_resources *pll);
+
+#endif /* MDSS_DSI_PLL_8996_H */
diff --git a/drivers/clk/qcom/mdss/mdss-dsi-pll.h b/drivers/clk/qcom/mdss/mdss-dsi-pll.h
new file mode 100644
index 000000000000..f88ae4d0eea1
--- /dev/null
+++ b/drivers/clk/qcom/mdss/mdss-dsi-pll.h
@@ -0,0 +1,110 @@
+/* Copyright (c) 2012-2016, 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_DSI_PLL_H
+#define __MDSS_DSI_PLL_H
+
+#define MAX_DSI_PLL_EN_SEQS 10
+
+#define DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG (0x0020)
+#define DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2 (0x0064)
+#define DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG (0x0068)
+#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1 (0x0070)
+
+/* Register offsets for 20nm PHY PLL */
+#define MMSS_DSI_PHY_PLL_PLL_CNTRL (0x0014)
+#define MMSS_DSI_PHY_PLL_PLL_BKG_KVCO_CAL_EN (0x002C)
+#define MMSS_DSI_PHY_PLL_PLLLOCK_CMP_EN (0x009C)
+
+struct lpfr_cfg {
+ unsigned long vco_rate;
+ u32 r;
+};
+
+struct dsi_pll_vco_clk {
+ unsigned long ref_clk_rate;
+ unsigned long min_rate;
+ unsigned long max_rate;
+ u32 pll_en_seq_cnt;
+ struct lpfr_cfg *lpfr_lut;
+ u32 lpfr_lut_size;
+ void *priv;
+
+ struct clk c;
+
+ int (*pll_enable_seqs[MAX_DSI_PLL_EN_SEQS])
+ (struct mdss_pll_resources *dsi_pll_Res);
+};
+
+static inline struct dsi_pll_vco_clk *to_vco_clk(struct clk *clk)
+{
+ return container_of(clk, struct dsi_pll_vco_clk, c);
+}
+
+int dsi_pll_clock_register_hpm(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+int dsi_pll_clock_register_20nm(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+int dsi_pll_clock_register_lpm(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+int dsi_pll_clock_register_8996(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+int dsi_pll_clock_register_cobalt(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+
+int set_byte_mux_sel(struct mux_clk *clk, int sel);
+int get_byte_mux_sel(struct mux_clk *clk);
+int dsi_pll_mux_prepare(struct clk *c);
+int fixed_4div_set_div(struct div_clk *clk, int div);
+int fixed_4div_get_div(struct div_clk *clk);
+int digital_set_div(struct div_clk *clk, int div);
+int digital_get_div(struct div_clk *clk);
+int analog_set_div(struct div_clk *clk, int div);
+int analog_get_div(struct div_clk *clk);
+int dsi_pll_lock_status(struct mdss_pll_resources *dsi_pll_res);
+int vco_set_rate(struct dsi_pll_vco_clk *vco, unsigned long rate);
+unsigned long vco_get_rate(struct clk *c);
+long vco_round_rate(struct clk *c, unsigned long rate);
+enum handoff vco_handoff(struct clk *c);
+int vco_prepare(struct clk *c);
+void vco_unprepare(struct clk *c);
+
+/* APIs for 20nm PHY PLL */
+int pll_20nm_vco_set_rate(struct dsi_pll_vco_clk *vco, unsigned long rate);
+int shadow_pll_20nm_vco_set_rate(struct dsi_pll_vco_clk *vco,
+ unsigned long rate);
+long pll_20nm_vco_round_rate(struct clk *c, unsigned long rate);
+enum handoff pll_20nm_vco_handoff(struct clk *c);
+int pll_20nm_vco_prepare(struct clk *c);
+void pll_20nm_vco_unprepare(struct clk *c);
+int pll_20nm_vco_enable_seq(struct mdss_pll_resources *dsi_pll_res);
+
+int set_bypass_lp_div_mux_sel(struct mux_clk *clk, int sel);
+int set_shadow_bypass_lp_div_mux_sel(struct mux_clk *clk, int sel);
+int get_bypass_lp_div_mux_sel(struct mux_clk *clk);
+int fixed_hr_oclk2_set_div(struct div_clk *clk, int div);
+int shadow_fixed_hr_oclk2_set_div(struct div_clk *clk, int div);
+int fixed_hr_oclk2_get_div(struct div_clk *clk);
+int hr_oclk3_set_div(struct div_clk *clk, int div);
+int shadow_hr_oclk3_set_div(struct div_clk *clk, int div);
+int hr_oclk3_get_div(struct div_clk *clk);
+int ndiv_set_div(struct div_clk *clk, int div);
+int shadow_ndiv_set_div(struct div_clk *clk, int div);
+int ndiv_get_div(struct div_clk *clk);
+void __dsi_pll_disable(void __iomem *pll_base);
+
+int set_mdss_pixel_mux_sel(struct mux_clk *clk, int sel);
+int get_mdss_pixel_mux_sel(struct mux_clk *clk);
+int set_mdss_byte_mux_sel(struct mux_clk *clk, int sel);
+int get_mdss_byte_mux_sel(struct mux_clk *clk);
+
+#endif
diff --git a/drivers/clk/qcom/mdss/mdss-hdmi-pll-8996.c b/drivers/clk/qcom/mdss/mdss-hdmi-pll-8996.c
new file mode 100644
index 000000000000..e79b5cc39a79
--- /dev/null
+++ b/drivers/clk/qcom/mdss/mdss-hdmi-pll-8996.c
@@ -0,0 +1,2686 @@
+/* Copyright (c) 2014-2016, 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/err.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/clk/msm-clk-provider.h>
+#include <linux/clk/msm-clk.h>
+#include <linux/clk/msm-clock-generic.h>
+#include <dt-bindings/clock/msm-clocks-8996.h>
+
+#include "mdss-pll.h"
+#include "mdss-hdmi-pll.h"
+
+/* CONSTANTS */
+#define HDMI_BIT_CLK_TO_PIX_CLK_RATIO 10
+#define HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD 3400000000UL
+#define HDMI_DIG_FREQ_BIT_CLK_THRESHOLD 1500000000UL
+#define HDMI_MID_FREQ_BIT_CLK_THRESHOLD 750000000
+#define HDMI_CLKS_PLL_DIVSEL 0
+#define HDMI_CORECLK_DIV 5
+#define HDMI_REF_CLOCK 19200000
+#define HDMI_64B_ERR_VAL 0xFFFFFFFFFFFFFFFF
+#define HDMI_VERSION_8996_V1 1
+#define HDMI_VERSION_8996_V2 2
+#define HDMI_VERSION_8996_V3 3
+#define HDMI_VERSION_8996_V3_1_8 4
+
+#define HDMI_VCO_MAX_FREQ 12000000000
+#define HDMI_VCO_MIN_FREQ 8000000000
+#define HDMI_2400MHZ_BIT_CLK_HZ 2400000000UL
+#define HDMI_2250MHZ_BIT_CLK_HZ 2250000000UL
+#define HDMI_2000MHZ_BIT_CLK_HZ 2000000000UL
+#define HDMI_1700MHZ_BIT_CLK_HZ 1700000000UL
+#define HDMI_1200MHZ_BIT_CLK_HZ 1200000000UL
+#define HDMI_1334MHZ_BIT_CLK_HZ 1334000000UL
+#define HDMI_1000MHZ_BIT_CLK_HZ 1000000000UL
+#define HDMI_850MHZ_BIT_CLK_HZ 850000000
+#define HDMI_667MHZ_BIT_CLK_HZ 667000000
+#define HDMI_600MHZ_BIT_CLK_HZ 600000000
+#define HDMI_500MHZ_BIT_CLK_HZ 500000000
+#define HDMI_450MHZ_BIT_CLK_HZ 450000000
+#define HDMI_334MHZ_BIT_CLK_HZ 334000000
+#define HDMI_300MHZ_BIT_CLK_HZ 300000000
+#define HDMI_282MHZ_BIT_CLK_HZ 282000000
+#define HDMI_250MHZ_BIT_CLK_HZ 250000000
+#define HDMI_KHZ_TO_HZ 1000
+
+/* PLL REGISTERS */
+#define QSERDES_COM_ATB_SEL1 (0x000)
+#define QSERDES_COM_ATB_SEL2 (0x004)
+#define QSERDES_COM_FREQ_UPDATE (0x008)
+#define QSERDES_COM_BG_TIMER (0x00C)
+#define QSERDES_COM_SSC_EN_CENTER (0x010)
+#define QSERDES_COM_SSC_ADJ_PER1 (0x014)
+#define QSERDES_COM_SSC_ADJ_PER2 (0x018)
+#define QSERDES_COM_SSC_PER1 (0x01C)
+#define QSERDES_COM_SSC_PER2 (0x020)
+#define QSERDES_COM_SSC_STEP_SIZE1 (0x024)
+#define QSERDES_COM_SSC_STEP_SIZE2 (0x028)
+#define QSERDES_COM_POST_DIV (0x02C)
+#define QSERDES_COM_POST_DIV_MUX (0x030)
+#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN (0x034)
+#define QSERDES_COM_CLK_ENABLE1 (0x038)
+#define QSERDES_COM_SYS_CLK_CTRL (0x03C)
+#define QSERDES_COM_SYSCLK_BUF_ENABLE (0x040)
+#define QSERDES_COM_PLL_EN (0x044)
+#define QSERDES_COM_PLL_IVCO (0x048)
+#define QSERDES_COM_LOCK_CMP1_MODE0 (0x04C)
+#define QSERDES_COM_LOCK_CMP2_MODE0 (0x050)
+#define QSERDES_COM_LOCK_CMP3_MODE0 (0x054)
+#define QSERDES_COM_LOCK_CMP1_MODE1 (0x058)
+#define QSERDES_COM_LOCK_CMP2_MODE1 (0x05C)
+#define QSERDES_COM_LOCK_CMP3_MODE1 (0x060)
+#define QSERDES_COM_LOCK_CMP1_MODE2 (0x064)
+#define QSERDES_COM_CMN_RSVD0 (0x064)
+#define QSERDES_COM_LOCK_CMP2_MODE2 (0x068)
+#define QSERDES_COM_EP_CLOCK_DETECT_CTRL (0x068)
+#define QSERDES_COM_LOCK_CMP3_MODE2 (0x06C)
+#define QSERDES_COM_SYSCLK_DET_COMP_STATUS (0x06C)
+#define QSERDES_COM_BG_TRIM (0x070)
+#define QSERDES_COM_CLK_EP_DIV (0x074)
+#define QSERDES_COM_CP_CTRL_MODE0 (0x078)
+#define QSERDES_COM_CP_CTRL_MODE1 (0x07C)
+#define QSERDES_COM_CP_CTRL_MODE2 (0x080)
+#define QSERDES_COM_CMN_RSVD1 (0x080)
+#define QSERDES_COM_PLL_RCTRL_MODE0 (0x084)
+#define QSERDES_COM_PLL_RCTRL_MODE1 (0x088)
+#define QSERDES_COM_PLL_RCTRL_MODE2 (0x08C)
+#define QSERDES_COM_CMN_RSVD2 (0x08C)
+#define QSERDES_COM_PLL_CCTRL_MODE0 (0x090)
+#define QSERDES_COM_PLL_CCTRL_MODE1 (0x094)
+#define QSERDES_COM_PLL_CCTRL_MODE2 (0x098)
+#define QSERDES_COM_CMN_RSVD3 (0x098)
+#define QSERDES_COM_PLL_CNTRL (0x09C)
+#define QSERDES_COM_PHASE_SEL_CTRL (0x0A0)
+#define QSERDES_COM_PHASE_SEL_DC (0x0A4)
+#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL (0x0A8)
+#define QSERDES_COM_BIAS_EN_CTRL_BY_PSM (0x0A8)
+#define QSERDES_COM_SYSCLK_EN_SEL (0x0AC)
+#define QSERDES_COM_CML_SYSCLK_SEL (0x0B0)
+#define QSERDES_COM_RESETSM_CNTRL (0x0B4)
+#define QSERDES_COM_RESETSM_CNTRL2 (0x0B8)
+#define QSERDES_COM_RESTRIM_CTRL (0x0BC)
+#define QSERDES_COM_RESTRIM_CTRL2 (0x0C0)
+#define QSERDES_COM_RESCODE_DIV_NUM (0x0C4)
+#define QSERDES_COM_LOCK_CMP_EN (0x0C8)
+#define QSERDES_COM_LOCK_CMP_CFG (0x0CC)
+#define QSERDES_COM_DEC_START_MODE0 (0x0D0)
+#define QSERDES_COM_DEC_START_MODE1 (0x0D4)
+#define QSERDES_COM_DEC_START_MODE2 (0x0D8)
+#define QSERDES_COM_VCOCAL_DEADMAN_CTRL (0x0D8)
+#define QSERDES_COM_DIV_FRAC_START1_MODE0 (0x0DC)
+#define QSERDES_COM_DIV_FRAC_START2_MODE0 (0x0E0)
+#define QSERDES_COM_DIV_FRAC_START3_MODE0 (0x0E4)
+#define QSERDES_COM_DIV_FRAC_START1_MODE1 (0x0E8)
+#define QSERDES_COM_DIV_FRAC_START2_MODE1 (0x0EC)
+#define QSERDES_COM_DIV_FRAC_START3_MODE1 (0x0F0)
+#define QSERDES_COM_DIV_FRAC_START1_MODE2 (0x0F4)
+#define QSERDES_COM_VCO_TUNE_MINVAL1 (0x0F4)
+#define QSERDES_COM_DIV_FRAC_START2_MODE2 (0x0F8)
+#define QSERDES_COM_VCO_TUNE_MINVAL2 (0x0F8)
+#define QSERDES_COM_DIV_FRAC_START3_MODE2 (0x0FC)
+#define QSERDES_COM_CMN_RSVD4 (0x0FC)
+#define QSERDES_COM_INTEGLOOP_INITVAL (0x100)
+#define QSERDES_COM_INTEGLOOP_EN (0x104)
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 (0x108)
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 (0x10C)
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 (0x110)
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 (0x114)
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE2 (0x118)
+#define QSERDES_COM_VCO_TUNE_MAXVAL1 (0x118)
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE2 (0x11C)
+#define QSERDES_COM_VCO_TUNE_MAXVAL2 (0x11C)
+#define QSERDES_COM_RES_TRIM_CONTROL2 (0x120)
+#define QSERDES_COM_VCO_TUNE_CTRL (0x124)
+#define QSERDES_COM_VCO_TUNE_MAP (0x128)
+#define QSERDES_COM_VCO_TUNE1_MODE0 (0x12C)
+#define QSERDES_COM_VCO_TUNE2_MODE0 (0x130)
+#define QSERDES_COM_VCO_TUNE1_MODE1 (0x134)
+#define QSERDES_COM_VCO_TUNE2_MODE1 (0x138)
+#define QSERDES_COM_VCO_TUNE1_MODE2 (0x13C)
+#define QSERDES_COM_VCO_TUNE_INITVAL1 (0x13C)
+#define QSERDES_COM_VCO_TUNE2_MODE2 (0x140)
+#define QSERDES_COM_VCO_TUNE_INITVAL2 (0x140)
+#define QSERDES_COM_VCO_TUNE_TIMER1 (0x144)
+#define QSERDES_COM_VCO_TUNE_TIMER2 (0x148)
+#define QSERDES_COM_SAR (0x14C)
+#define QSERDES_COM_SAR_CLK (0x150)
+#define QSERDES_COM_SAR_CODE_OUT_STATUS (0x154)
+#define QSERDES_COM_SAR_CODE_READY_STATUS (0x158)
+#define QSERDES_COM_CMN_STATUS (0x15C)
+#define QSERDES_COM_RESET_SM_STATUS (0x160)
+#define QSERDES_COM_RESTRIM_CODE_STATUS (0x164)
+#define QSERDES_COM_PLLCAL_CODE1_STATUS (0x168)
+#define QSERDES_COM_PLLCAL_CODE2_STATUS (0x16C)
+#define QSERDES_COM_BG_CTRL (0x170)
+#define QSERDES_COM_CLK_SELECT (0x174)
+#define QSERDES_COM_HSCLK_SEL (0x178)
+#define QSERDES_COM_INTEGLOOP_BINCODE_STATUS (0x17C)
+#define QSERDES_COM_PLL_ANALOG (0x180)
+#define QSERDES_COM_CORECLK_DIV (0x184)
+#define QSERDES_COM_SW_RESET (0x188)
+#define QSERDES_COM_CORE_CLK_EN (0x18C)
+#define QSERDES_COM_C_READY_STATUS (0x190)
+#define QSERDES_COM_CMN_CONFIG (0x194)
+#define QSERDES_COM_CMN_RATE_OVERRIDE (0x198)
+#define QSERDES_COM_SVS_MODE_CLK_SEL (0x19C)
+#define QSERDES_COM_DEBUG_BUS0 (0x1A0)
+#define QSERDES_COM_DEBUG_BUS1 (0x1A4)
+#define QSERDES_COM_DEBUG_BUS2 (0x1A8)
+#define QSERDES_COM_DEBUG_BUS3 (0x1AC)
+#define QSERDES_COM_DEBUG_BUS_SEL (0x1B0)
+#define QSERDES_COM_CMN_MISC1 (0x1B4)
+#define QSERDES_COM_CMN_MISC2 (0x1B8)
+#define QSERDES_COM_CORECLK_DIV_MODE1 (0x1BC)
+#define QSERDES_COM_CORECLK_DIV_MODE2 (0x1C0)
+#define QSERDES_COM_CMN_RSVD5 (0x1C0)
+
+/* Tx Channel base addresses */
+#define HDMI_TX_L0_BASE_OFFSET (0x400)
+#define HDMI_TX_L1_BASE_OFFSET (0x600)
+#define HDMI_TX_L2_BASE_OFFSET (0x800)
+#define HDMI_TX_L3_BASE_OFFSET (0xA00)
+
+/* Tx Channel PHY registers */
+#define QSERDES_TX_L0_BIST_MODE_LANENO (0x000)
+#define QSERDES_TX_L0_BIST_INVERT (0x004)
+#define QSERDES_TX_L0_CLKBUF_ENABLE (0x008)
+#define QSERDES_TX_L0_CMN_CONTROL_ONE (0x00C)
+#define QSERDES_TX_L0_CMN_CONTROL_TWO (0x010)
+#define QSERDES_TX_L0_CMN_CONTROL_THREE (0x014)
+#define QSERDES_TX_L0_TX_EMP_POST1_LVL (0x018)
+#define QSERDES_TX_L0_TX_POST2_EMPH (0x01C)
+#define QSERDES_TX_L0_TX_BOOST_LVL_UP_DN (0x020)
+#define QSERDES_TX_L0_HP_PD_ENABLES (0x024)
+#define QSERDES_TX_L0_TX_IDLE_LVL_LARGE_AMP (0x028)
+#define QSERDES_TX_L0_TX_DRV_LVL (0x02C)
+#define QSERDES_TX_L0_TX_DRV_LVL_OFFSET (0x030)
+#define QSERDES_TX_L0_RESET_TSYNC_EN (0x034)
+#define QSERDES_TX_L0_PRE_STALL_LDO_BOOST_EN (0x038)
+#define QSERDES_TX_L0_TX_BAND (0x03C)
+#define QSERDES_TX_L0_SLEW_CNTL (0x040)
+#define QSERDES_TX_L0_INTERFACE_SELECT (0x044)
+#define QSERDES_TX_L0_LPB_EN (0x048)
+#define QSERDES_TX_L0_RES_CODE_LANE_TX (0x04C)
+#define QSERDES_TX_L0_RES_CODE_LANE_RX (0x050)
+#define QSERDES_TX_L0_RES_CODE_LANE_OFFSET (0x054)
+#define QSERDES_TX_L0_PERL_LENGTH1 (0x058)
+#define QSERDES_TX_L0_PERL_LENGTH2 (0x05C)
+#define QSERDES_TX_L0_SERDES_BYP_EN_OUT (0x060)
+#define QSERDES_TX_L0_DEBUG_BUS_SEL (0x064)
+#define QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN (0x068)
+#define QSERDES_TX_L0_TX_POL_INV (0x06C)
+#define QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN (0x070)
+#define QSERDES_TX_L0_BIST_PATTERN1 (0x074)
+#define QSERDES_TX_L0_BIST_PATTERN2 (0x078)
+#define QSERDES_TX_L0_BIST_PATTERN3 (0x07C)
+#define QSERDES_TX_L0_BIST_PATTERN4 (0x080)
+#define QSERDES_TX_L0_BIST_PATTERN5 (0x084)
+#define QSERDES_TX_L0_BIST_PATTERN6 (0x088)
+#define QSERDES_TX_L0_BIST_PATTERN7 (0x08C)
+#define QSERDES_TX_L0_BIST_PATTERN8 (0x090)
+#define QSERDES_TX_L0_LANE_MODE (0x094)
+#define QSERDES_TX_L0_IDAC_CAL_LANE_MODE (0x098)
+#define QSERDES_TX_L0_IDAC_CAL_LANE_MODE_CONFIGURATION (0x09C)
+#define QSERDES_TX_L0_ATB_SEL1 (0x0A0)
+#define QSERDES_TX_L0_ATB_SEL2 (0x0A4)
+#define QSERDES_TX_L0_RCV_DETECT_LVL (0x0A8)
+#define QSERDES_TX_L0_RCV_DETECT_LVL_2 (0x0AC)
+#define QSERDES_TX_L0_PRBS_SEED1 (0x0B0)
+#define QSERDES_TX_L0_PRBS_SEED2 (0x0B4)
+#define QSERDES_TX_L0_PRBS_SEED3 (0x0B8)
+#define QSERDES_TX_L0_PRBS_SEED4 (0x0BC)
+#define QSERDES_TX_L0_RESET_GEN (0x0C0)
+#define QSERDES_TX_L0_RESET_GEN_MUXES (0x0C4)
+#define QSERDES_TX_L0_TRAN_DRVR_EMP_EN (0x0C8)
+#define QSERDES_TX_L0_TX_INTERFACE_MODE (0x0CC)
+#define QSERDES_TX_L0_PWM_CTRL (0x0D0)
+#define QSERDES_TX_L0_PWM_ENCODED_OR_DATA (0x0D4)
+#define QSERDES_TX_L0_PWM_GEAR_1_DIVIDER_BAND2 (0x0D8)
+#define QSERDES_TX_L0_PWM_GEAR_2_DIVIDER_BAND2 (0x0DC)
+#define QSERDES_TX_L0_PWM_GEAR_3_DIVIDER_BAND2 (0x0E0)
+#define QSERDES_TX_L0_PWM_GEAR_4_DIVIDER_BAND2 (0x0E4)
+#define QSERDES_TX_L0_PWM_GEAR_1_DIVIDER_BAND0_1 (0x0E8)
+#define QSERDES_TX_L0_PWM_GEAR_2_DIVIDER_BAND0_1 (0x0EC)
+#define QSERDES_TX_L0_PWM_GEAR_3_DIVIDER_BAND0_1 (0x0F0)
+#define QSERDES_TX_L0_PWM_GEAR_4_DIVIDER_BAND0_1 (0x0F4)
+#define QSERDES_TX_L0_VMODE_CTRL1 (0x0F8)
+#define QSERDES_TX_L0_VMODE_CTRL2 (0x0FC)
+#define QSERDES_TX_L0_TX_ALOG_INTF_OBSV_CNTL (0x100)
+#define QSERDES_TX_L0_BIST_STATUS (0x104)
+#define QSERDES_TX_L0_BIST_ERROR_COUNT1 (0x108)
+#define QSERDES_TX_L0_BIST_ERROR_COUNT2 (0x10C)
+#define QSERDES_TX_L0_TX_ALOG_INTF_OBSV (0x110)
+
+/* HDMI PHY REGISTERS */
+#define HDMI_PHY_BASE_OFFSET (0xC00)
+
+#define HDMI_PHY_CFG (0x00)
+#define HDMI_PHY_PD_CTL (0x04)
+#define HDMI_PHY_MODE (0x08)
+#define HDMI_PHY_MISR_CLEAR (0x0C)
+#define HDMI_PHY_TX0_TX1_BIST_CFG0 (0x10)
+#define HDMI_PHY_TX0_TX1_BIST_CFG1 (0x14)
+#define HDMI_PHY_TX0_TX1_PRBS_SEED_BYTE0 (0x18)
+#define HDMI_PHY_TX0_TX1_PRBS_SEED_BYTE1 (0x1C)
+#define HDMI_PHY_TX0_TX1_BIST_PATTERN0 (0x20)
+#define HDMI_PHY_TX0_TX1_BIST_PATTERN1 (0x24)
+#define HDMI_PHY_TX2_TX3_BIST_CFG0 (0x28)
+#define HDMI_PHY_TX2_TX3_BIST_CFG1 (0x2C)
+#define HDMI_PHY_TX2_TX3_PRBS_SEED_BYTE0 (0x30)
+#define HDMI_PHY_TX2_TX3_PRBS_SEED_BYTE1 (0x34)
+#define HDMI_PHY_TX2_TX3_BIST_PATTERN0 (0x38)
+#define HDMI_PHY_TX2_TX3_BIST_PATTERN1 (0x3C)
+#define HDMI_PHY_DEBUG_BUS_SEL (0x40)
+#define HDMI_PHY_TXCAL_CFG0 (0x44)
+#define HDMI_PHY_TXCAL_CFG1 (0x48)
+#define HDMI_PHY_TX0_TX1_LANE_CTL (0x4C)
+#define HDMI_PHY_TX2_TX3_LANE_CTL (0x50)
+#define HDMI_PHY_LANE_BIST_CONFIG (0x54)
+#define HDMI_PHY_CLOCK (0x58)
+#define HDMI_PHY_MISC1 (0x5C)
+#define HDMI_PHY_MISC2 (0x60)
+#define HDMI_PHY_TX0_TX1_BIST_STATUS0 (0x64)
+#define HDMI_PHY_TX0_TX1_BIST_STATUS1 (0x68)
+#define HDMI_PHY_TX0_TX1_BIST_STATUS2 (0x6C)
+#define HDMI_PHY_TX2_TX3_BIST_STATUS0 (0x70)
+#define HDMI_PHY_TX2_TX3_BIST_STATUS1 (0x74)
+#define HDMI_PHY_TX2_TX3_BIST_STATUS2 (0x78)
+#define HDMI_PHY_PRE_MISR_STATUS0 (0x7C)
+#define HDMI_PHY_PRE_MISR_STATUS1 (0x80)
+#define HDMI_PHY_PRE_MISR_STATUS2 (0x84)
+#define HDMI_PHY_PRE_MISR_STATUS3 (0x88)
+#define HDMI_PHY_POST_MISR_STATUS0 (0x8C)
+#define HDMI_PHY_POST_MISR_STATUS1 (0x90)
+#define HDMI_PHY_POST_MISR_STATUS2 (0x94)
+#define HDMI_PHY_POST_MISR_STATUS3 (0x98)
+#define HDMI_PHY_STATUS (0x9C)
+#define HDMI_PHY_MISC3_STATUS (0xA0)
+#define HDMI_PHY_MISC4_STATUS (0xA4)
+#define HDMI_PHY_DEBUG_BUS0 (0xA8)
+#define HDMI_PHY_DEBUG_BUS1 (0xAC)
+#define HDMI_PHY_DEBUG_BUS2 (0xB0)
+#define HDMI_PHY_DEBUG_BUS3 (0xB4)
+#define HDMI_PHY_PHY_REVISION_ID0 (0xB8)
+#define HDMI_PHY_PHY_REVISION_ID1 (0xBC)
+#define HDMI_PHY_PHY_REVISION_ID2 (0xC0)
+#define HDMI_PHY_PHY_REVISION_ID3 (0xC4)
+
+#define HDMI_PLL_POLL_MAX_READS 100
+#define HDMI_PLL_POLL_TIMEOUT_US 1500
+
+enum hdmi_pll_freqs {
+ HDMI_PCLK_25200_KHZ,
+ HDMI_PCLK_27027_KHZ,
+ HDMI_PCLK_27000_KHZ,
+ HDMI_PCLK_74250_KHZ,
+ HDMI_PCLK_148500_KHZ,
+ HDMI_PCLK_154000_KHZ,
+ HDMI_PCLK_268500_KHZ,
+ HDMI_PCLK_297000_KHZ,
+ HDMI_PCLK_594000_KHZ,
+ HDMI_PCLK_MAX
+};
+
+struct hdmi_8996_phy_pll_reg_cfg {
+ u32 tx_l0_lane_mode;
+ u32 tx_l2_lane_mode;
+ u32 tx_l0_tx_band;
+ u32 tx_l1_tx_band;
+ u32 tx_l2_tx_band;
+ u32 tx_l3_tx_band;
+ u32 com_svs_mode_clk_sel;
+ u32 com_hsclk_sel;
+ u32 com_pll_cctrl_mode0;
+ u32 com_pll_rctrl_mode0;
+ u32 com_cp_ctrl_mode0;
+ u32 com_dec_start_mode0;
+ u32 com_div_frac_start1_mode0;
+ u32 com_div_frac_start2_mode0;
+ u32 com_div_frac_start3_mode0;
+ u32 com_integloop_gain0_mode0;
+ u32 com_integloop_gain1_mode0;
+ u32 com_lock_cmp_en;
+ u32 com_lock_cmp1_mode0;
+ u32 com_lock_cmp2_mode0;
+ u32 com_lock_cmp3_mode0;
+ u32 com_core_clk_en;
+ u32 com_coreclk_div;
+ u32 com_restrim_ctrl;
+ u32 com_vco_tune_ctrl;
+
+ u32 tx_l0_tx_drv_lvl;
+ u32 tx_l0_tx_emp_post1_lvl;
+ u32 tx_l1_tx_drv_lvl;
+ u32 tx_l1_tx_emp_post1_lvl;
+ u32 tx_l2_tx_drv_lvl;
+ u32 tx_l2_tx_emp_post1_lvl;
+ u32 tx_l3_tx_drv_lvl;
+ u32 tx_l3_tx_emp_post1_lvl;
+ u32 tx_l0_vmode_ctrl1;
+ u32 tx_l0_vmode_ctrl2;
+ u32 tx_l1_vmode_ctrl1;
+ u32 tx_l1_vmode_ctrl2;
+ u32 tx_l2_vmode_ctrl1;
+ u32 tx_l2_vmode_ctrl2;
+ u32 tx_l3_vmode_ctrl1;
+ u32 tx_l3_vmode_ctrl2;
+ u32 tx_l0_res_code_lane_tx;
+ u32 tx_l1_res_code_lane_tx;
+ u32 tx_l2_res_code_lane_tx;
+ u32 tx_l3_res_code_lane_tx;
+
+ u32 phy_mode;
+};
+
+struct hdmi_8996_v3_post_divider {
+ u64 vco_freq;
+ u64 hsclk_divsel;
+ u64 vco_ratio;
+ u64 tx_band_sel;
+ u64 half_rate_mode;
+};
+
+static inline struct hdmi_pll_vco_clk *to_hdmi_8996_vco_clk(struct clk *clk)
+{
+ return container_of(clk, struct hdmi_pll_vco_clk, c);
+}
+
+static inline u64 hdmi_8996_v1_get_post_div_lt_2g(u64 bclk)
+{
+ if (bclk >= HDMI_2400MHZ_BIT_CLK_HZ)
+ return 2;
+ else if (bclk >= HDMI_1700MHZ_BIT_CLK_HZ)
+ return 3;
+ else if (bclk >= HDMI_1200MHZ_BIT_CLK_HZ)
+ return 4;
+ else if (bclk >= HDMI_850MHZ_BIT_CLK_HZ)
+ return 3;
+ else if (bclk >= HDMI_600MHZ_BIT_CLK_HZ)
+ return 4;
+ else if (bclk >= HDMI_450MHZ_BIT_CLK_HZ)
+ return 3;
+ else if (bclk >= HDMI_300MHZ_BIT_CLK_HZ)
+ return 4;
+
+ return HDMI_64B_ERR_VAL;
+}
+
+static inline u64 hdmi_8996_v2_get_post_div_lt_2g(u64 bclk, u64 vco_range)
+{
+ u64 hdmi_8ghz = vco_range;
+ u64 tmp_calc;
+
+ hdmi_8ghz <<= 2;
+ tmp_calc = hdmi_8ghz;
+ do_div(tmp_calc, 6U);
+
+ if (bclk >= vco_range)
+ return 2;
+ else if (bclk >= tmp_calc)
+ return 3;
+ else if (bclk >= vco_range >> 1)
+ return 4;
+
+ tmp_calc = hdmi_8ghz;
+ do_div(tmp_calc, 12U);
+ if (bclk >= tmp_calc)
+ return 3;
+ else if (bclk >= vco_range >> 2)
+ return 4;
+
+ tmp_calc = hdmi_8ghz;
+ do_div(tmp_calc, 24U);
+ if (bclk >= tmp_calc)
+ return 3;
+ else if (bclk >= vco_range >> 3)
+ return 4;
+
+ return HDMI_64B_ERR_VAL;
+}
+
+static inline u64 hdmi_8996_v2_get_post_div_gt_2g(u64 hsclk)
+{
+ if (hsclk >= 0 && hsclk <= 3)
+ return hsclk + 1;
+
+ return HDMI_64B_ERR_VAL;
+}
+
+static inline u64 hdmi_8996_get_coreclk_div_lt_2g(u64 bclk)
+{
+ if (bclk >= HDMI_1334MHZ_BIT_CLK_HZ)
+ return 1;
+ else if (bclk >= HDMI_1000MHZ_BIT_CLK_HZ)
+ return 1;
+ else if (bclk >= HDMI_667MHZ_BIT_CLK_HZ)
+ return 2;
+ else if (bclk >= HDMI_500MHZ_BIT_CLK_HZ)
+ return 2;
+ else if (bclk >= HDMI_334MHZ_BIT_CLK_HZ)
+ return 3;
+ else if (bclk >= HDMI_250MHZ_BIT_CLK_HZ)
+ return 3;
+
+ return HDMI_64B_ERR_VAL;
+}
+
+static inline u64 hdmi_8996_get_coreclk_div_ratio(u64 clks_pll_divsel,
+ u64 coreclk_div)
+{
+ if (clks_pll_divsel == 0)
+ return coreclk_div*2;
+ else if (clks_pll_divsel == 1)
+ return coreclk_div*4;
+
+ return HDMI_64B_ERR_VAL;
+}
+
+static inline u64 hdmi_8996_v1_get_tx_band(u64 bclk)
+{
+ if (bclk >= 2400000000UL)
+ return 0;
+ if (bclk >= 1200000000UL)
+ return 1;
+ if (bclk >= 600000000UL)
+ return 2;
+ if (bclk >= 300000000UL)
+ return 3;
+
+ return HDMI_64B_ERR_VAL;
+}
+
+static inline u64 hdmi_8996_v2_get_tx_band(u64 bclk, u64 vco_range)
+{
+ if (bclk >= vco_range)
+ return 0;
+ else if (bclk >= vco_range >> 1)
+ return 1;
+ else if (bclk >= vco_range >> 2)
+ return 2;
+ else if (bclk >= vco_range >> 3)
+ return 3;
+
+ return HDMI_64B_ERR_VAL;
+}
+
+static inline u64 hdmi_8996_v1_get_hsclk(u64 fdata)
+{
+ if (fdata >= 9600000000UL)
+ return 0;
+ else if (fdata >= 4800000000UL)
+ return 1;
+ else if (fdata >= 3200000000UL)
+ return 2;
+ else if (fdata >= 2400000000UL)
+ return 3;
+
+ return HDMI_64B_ERR_VAL;
+}
+
+static inline u64 hdmi_8996_v2_get_hsclk(u64 fdata, u64 vco_range)
+{
+ u64 tmp_calc = vco_range;
+
+ tmp_calc <<= 2;
+ do_div(tmp_calc, 3U);
+ if (fdata >= (vco_range << 2))
+ return 0;
+ else if (fdata >= (vco_range << 1))
+ return 1;
+ else if (fdata >= tmp_calc)
+ return 2;
+ else if (fdata >= vco_range)
+ return 3;
+
+ return HDMI_64B_ERR_VAL;
+
+}
+
+static inline u64 hdmi_8996_v2_get_vco_freq(u64 bclk, u64 vco_range)
+{
+ u64 tx_band_div_ratio = 1U << hdmi_8996_v2_get_tx_band(bclk, vco_range);
+ u64 pll_post_div_ratio;
+
+ if (bclk >= vco_range) {
+ u64 hsclk = hdmi_8996_v2_get_hsclk(bclk, vco_range);
+
+ pll_post_div_ratio = hdmi_8996_v2_get_post_div_gt_2g(hsclk);
+ } else {
+ pll_post_div_ratio = hdmi_8996_v2_get_post_div_lt_2g(bclk,
+ vco_range);
+ }
+
+ return bclk * (pll_post_div_ratio * tx_band_div_ratio);
+}
+
+static inline u64 hdmi_8996_v2_get_fdata(u64 bclk, u64 vco_range)
+{
+ if (bclk >= vco_range)
+ return bclk;
+
+ u64 tmp_calc = hdmi_8996_v2_get_vco_freq(bclk, vco_range);
+ u64 pll_post_div_ratio_lt_2g = hdmi_8996_v2_get_post_div_lt_2g(
+ bclk, vco_range);
+ if (pll_post_div_ratio_lt_2g == HDMI_64B_ERR_VAL)
+ return HDMI_64B_ERR_VAL;
+
+ do_div(tmp_calc, pll_post_div_ratio_lt_2g);
+ return tmp_calc;
+}
+
+static inline u64 hdmi_8996_get_cpctrl(u64 frac_start, bool gen_ssc)
+{
+ if ((frac_start != 0) ||
+ (gen_ssc == true))
+ /*
+ * This should be ROUND(11/(19.2/20))).
+ * Since ref clock does not change, hardcoding to 11
+ */
+ return 0xB;
+
+ return 0x23;
+}
+
+static inline u64 hdmi_8996_get_rctrl(u64 frac_start, bool gen_ssc)
+{
+ if ((frac_start != 0) || (gen_ssc == true))
+ return 0x16;
+
+ return 0x10;
+}
+
+static inline u64 hdmi_8996_get_cctrl(u64 frac_start, bool gen_ssc)
+{
+ if ((frac_start != 0) || (gen_ssc == true))
+ return 0x28;
+
+ return 0x1;
+}
+
+static inline u64 hdmi_8996_get_integloop_gain(u64 frac_start, bool gen_ssc)
+{
+ if ((frac_start != 0) || (gen_ssc == true))
+ return 0x80;
+
+ return 0xC4;
+}
+
+static inline u64 hdmi_8996_v3_get_integloop_gain(u64 frac_start, u64 bclk,
+ bool gen_ssc)
+{
+ u64 digclk_divsel = bclk >= HDMI_DIG_FREQ_BIT_CLK_THRESHOLD ? 1 : 2;
+ u64 base = ((frac_start != 0) || (gen_ssc == true)) ? 0x40 : 0xC4;
+
+ base <<= digclk_divsel;
+
+ return (base <= 2046 ? base : 0x7FE);
+}
+
+static inline u64 hdmi_8996_get_vco_tune(u64 fdata, u64 div)
+{
+ u64 vco_tune;
+
+ vco_tune = fdata * div;
+ do_div(vco_tune, 1000000);
+ vco_tune = 13000 - vco_tune - 256;
+ do_div(vco_tune, 5);
+
+ return vco_tune;
+}
+
+static inline u64 hdmi_8996_get_pll_cmp(u64 pll_cmp_cnt, u64 core_clk)
+{
+ u64 pll_cmp;
+ u64 rem;
+
+ pll_cmp = pll_cmp_cnt * core_clk;
+ rem = do_div(pll_cmp, HDMI_REF_CLOCK);
+ if (rem > (HDMI_REF_CLOCK >> 1))
+ pll_cmp++;
+ pll_cmp -= 1;
+
+ return pll_cmp;
+}
+
+static inline u64 hdmi_8996_v3_get_pll_cmp(u64 pll_cmp_cnt, u64 fdata)
+{
+ u64 dividend = pll_cmp_cnt * fdata;
+ u64 divisor = HDMI_REF_CLOCK * 10;
+ u64 rem;
+
+ rem = do_div(dividend, divisor);
+ if (rem > (divisor >> 1))
+ dividend++;
+
+ return dividend - 1;
+}
+
+static int hdmi_8996_v3_get_post_div(struct hdmi_8996_v3_post_divider *pd,
+ u64 bclk)
+{
+ u32 ratio[] = {2, 3, 4, 5, 6, 9, 10, 12, 14, 15, 20, 21, 25, 28, 35};
+ u32 tx_band_sel[] = {0, 1, 2, 3};
+ u64 vco_freq[60];
+ u64 vco, vco_optimal, half_rate_mode = 0;
+ int vco_optimal_index, vco_freq_index;
+ int i, j, k, x;
+
+ for (i = 0; i <= 1; i++) {
+ vco_optimal = HDMI_VCO_MAX_FREQ;
+ vco_optimal_index = -1;
+ vco_freq_index = 0;
+ for (j = 0; j < 15; j++) {
+ for (k = 0; k < 4; k++) {
+ u64 ratio_mult = ratio[j] << tx_band_sel[k];
+
+ vco = bclk >> half_rate_mode;
+ vco *= ratio_mult;
+ vco_freq[vco_freq_index++] = vco;
+ }
+ }
+
+ for (x = 0; x < 60; x++) {
+ u64 vco_tmp = vco_freq[x];
+
+ if ((vco_tmp >= HDMI_VCO_MIN_FREQ) &&
+ (vco_tmp <= vco_optimal)) {
+ vco_optimal = vco_tmp;
+ vco_optimal_index = x;
+ }
+ }
+
+ if (vco_optimal_index == -1) {
+ if (!half_rate_mode)
+ half_rate_mode++;
+ else
+ return -EINVAL;
+ } else {
+ pd->vco_freq = vco_optimal;
+ pd->tx_band_sel = tx_band_sel[vco_optimal_index % 4];
+ pd->vco_ratio = ratio[vco_optimal_index / 4];
+ break;
+ }
+ }
+
+ switch (pd->vco_ratio) {
+ case 2:
+ pd->hsclk_divsel = 0;
+ break;
+ case 3:
+ pd->hsclk_divsel = 4;
+ break;
+ case 4:
+ pd->hsclk_divsel = 8;
+ break;
+ case 5:
+ pd->hsclk_divsel = 12;
+ break;
+ case 6:
+ pd->hsclk_divsel = 1;
+ break;
+ case 9:
+ pd->hsclk_divsel = 5;
+ break;
+ case 10:
+ pd->hsclk_divsel = 2;
+ break;
+ case 12:
+ pd->hsclk_divsel = 9;
+ break;
+ case 14:
+ pd->hsclk_divsel = 3;
+ break;
+ case 15:
+ pd->hsclk_divsel = 13;
+ break;
+ case 20:
+ pd->hsclk_divsel = 10;
+ break;
+ case 21:
+ pd->hsclk_divsel = 7;
+ break;
+ case 25:
+ pd->hsclk_divsel = 14;
+ break;
+ case 28:
+ pd->hsclk_divsel = 11;
+ break;
+ case 35:
+ pd->hsclk_divsel = 15;
+ break;
+ };
+
+ return 0;
+}
+
+static int hdmi_8996_v1_calculate(u32 pix_clk,
+ struct hdmi_8996_phy_pll_reg_cfg *cfg)
+{
+ int rc = -EINVAL;
+ u64 fdata, clk_divtx, tmds_clk;
+ u64 bclk;
+ u64 post_div_gt_2g;
+ u64 post_div_lt_2g;
+ u64 coreclk_div1_lt_2g;
+ u64 core_clk_div_ratio;
+ u64 core_clk;
+ u64 pll_cmp;
+ u64 tx_band;
+ u64 tx_band_div_ratio;
+ u64 hsclk;
+ u64 dec_start;
+ u64 frac_start;
+ u64 pll_divisor = 4 * HDMI_REF_CLOCK;
+ u64 cpctrl;
+ u64 rctrl;
+ u64 cctrl;
+ u64 integloop_gain;
+ u64 vco_tune;
+ u64 vco_freq;
+ u64 rem;
+
+ /* FDATA, CLK_DIVTX, PIXEL_CLK, TMDS_CLK */
+ bclk = ((u64)pix_clk) * HDMI_BIT_CLK_TO_PIX_CLK_RATIO;
+
+ if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD)
+ tmds_clk = bclk/4;
+ else
+ tmds_clk = bclk;
+
+ post_div_lt_2g = hdmi_8996_v1_get_post_div_lt_2g(bclk);
+ if (post_div_lt_2g == HDMI_64B_ERR_VAL)
+ goto fail;
+
+ coreclk_div1_lt_2g = hdmi_8996_get_coreclk_div_lt_2g(bclk);
+
+ core_clk_div_ratio = hdmi_8996_get_coreclk_div_ratio(
+ HDMI_CLKS_PLL_DIVSEL, HDMI_CORECLK_DIV);
+
+ tx_band = hdmi_8996_v1_get_tx_band(bclk);
+ if (tx_band == HDMI_64B_ERR_VAL)
+ goto fail;
+
+ tx_band_div_ratio = 1 << tx_band;
+
+ if (bclk >= HDMI_2400MHZ_BIT_CLK_HZ) {
+ fdata = bclk;
+ hsclk = hdmi_8996_v1_get_hsclk(fdata);
+ if (hsclk == HDMI_64B_ERR_VAL)
+ goto fail;
+
+ post_div_gt_2g = (hsclk <= 3) ? (hsclk + 1) : HDMI_64B_ERR_VAL;
+ if (post_div_gt_2g == HDMI_64B_ERR_VAL)
+ goto fail;
+
+ vco_freq = bclk * (post_div_gt_2g * tx_band_div_ratio);
+ clk_divtx = vco_freq;
+ do_div(clk_divtx, post_div_gt_2g);
+ } else {
+ vco_freq = bclk * (post_div_lt_2g * tx_band_div_ratio);
+ fdata = vco_freq;
+ do_div(fdata, post_div_lt_2g);
+ hsclk = hdmi_8996_v1_get_hsclk(fdata);
+ if (hsclk == HDMI_64B_ERR_VAL)
+ goto fail;
+
+ clk_divtx = vco_freq;
+ do_div(clk_divtx, post_div_lt_2g);
+ post_div_gt_2g = (hsclk <= 3) ? (hsclk + 1) : HDMI_64B_ERR_VAL;
+ if (post_div_gt_2g == HDMI_64B_ERR_VAL)
+ goto fail;
+ }
+
+ /* Decimal and fraction values */
+ dec_start = fdata * post_div_gt_2g;
+ do_div(dec_start, pll_divisor);
+ frac_start = ((pll_divisor - (((dec_start + 1) * pll_divisor) -
+ (fdata * post_div_gt_2g))) * (1 << 20));
+ rem = do_div(frac_start, pll_divisor);
+ /* Round off frac_start to closest integer */
+ if (rem >= (pll_divisor >> 1))
+ frac_start++;
+
+ cpctrl = hdmi_8996_get_cpctrl(frac_start, false);
+ rctrl = hdmi_8996_get_rctrl(frac_start, false);
+ cctrl = hdmi_8996_get_cctrl(frac_start, false);
+ integloop_gain = hdmi_8996_get_integloop_gain(frac_start, false);
+ vco_tune = hdmi_8996_get_vco_tune(fdata, post_div_gt_2g);
+
+ core_clk = clk_divtx;
+ do_div(core_clk, core_clk_div_ratio);
+ pll_cmp = hdmi_8996_get_pll_cmp(1024, core_clk);
+
+ /* Debug dump */
+ DEV_DBG("%s: VCO freq: %llu\n", __func__, vco_freq);
+ DEV_DBG("%s: fdata: %llu\n", __func__, fdata);
+ DEV_DBG("%s: CLK_DIVTX: %llu\n", __func__, clk_divtx);
+ DEV_DBG("%s: pix_clk: %d\n", __func__, pix_clk);
+ DEV_DBG("%s: tmds clk: %llu\n", __func__, tmds_clk);
+ DEV_DBG("%s: HSCLK_SEL: %llu\n", __func__, hsclk);
+ DEV_DBG("%s: DEC_START: %llu\n", __func__, dec_start);
+ DEV_DBG("%s: DIV_FRAC_START: %llu\n", __func__, frac_start);
+ DEV_DBG("%s: PLL_CPCTRL: %llu\n", __func__, cpctrl);
+ DEV_DBG("%s: PLL_RCTRL: %llu\n", __func__, rctrl);
+ DEV_DBG("%s: PLL_CCTRL: %llu\n", __func__, cctrl);
+ DEV_DBG("%s: INTEGLOOP_GAIN: %llu\n", __func__, integloop_gain);
+ DEV_DBG("%s: VCO_TUNE: %llu\n", __func__, vco_tune);
+ DEV_DBG("%s: TX_BAND: %llu\n", __func__, tx_band);
+ DEV_DBG("%s: PLL_CMP: %llu\n", __func__, pll_cmp);
+
+ /* Convert these values to register specific values */
+ cfg->tx_l0_lane_mode = 0x3;
+ cfg->tx_l2_lane_mode = 0x3;
+ cfg->tx_l0_tx_band = tx_band + 4;
+ cfg->tx_l1_tx_band = tx_band + 4;
+ cfg->tx_l2_tx_band = tx_band + 4;
+ cfg->tx_l3_tx_band = tx_band + 4;
+ cfg->tx_l0_res_code_lane_tx = 0x33;
+ cfg->tx_l1_res_code_lane_tx = 0x33;
+ cfg->tx_l2_res_code_lane_tx = 0x33;
+ cfg->tx_l3_res_code_lane_tx = 0x33;
+ cfg->com_restrim_ctrl = 0x0;
+ cfg->com_vco_tune_ctrl = 0x1C;
+
+ cfg->com_svs_mode_clk_sel =
+ (bclk >= HDMI_DIG_FREQ_BIT_CLK_THRESHOLD ? 1 : 2);
+ cfg->com_hsclk_sel = (0x28 | hsclk);
+ cfg->com_pll_cctrl_mode0 = cctrl;
+ cfg->com_pll_rctrl_mode0 = rctrl;
+ cfg->com_cp_ctrl_mode0 = cpctrl;
+ cfg->com_dec_start_mode0 = dec_start;
+ cfg->com_div_frac_start1_mode0 = (frac_start & 0xFF);
+ cfg->com_div_frac_start2_mode0 = ((frac_start & 0xFF00) >> 8);
+ cfg->com_div_frac_start3_mode0 = ((frac_start & 0xF0000) >> 16);
+ cfg->com_integloop_gain0_mode0 = (integloop_gain & 0xFF);
+ cfg->com_integloop_gain1_mode0 = ((integloop_gain & 0xF00) >> 8);
+ cfg->com_lock_cmp1_mode0 = (pll_cmp & 0xFF);
+ cfg->com_lock_cmp2_mode0 = ((pll_cmp & 0xFF00) >> 8);
+ cfg->com_lock_cmp3_mode0 = ((pll_cmp & 0x30000) >> 16);
+ cfg->com_core_clk_en = (0x6C | (HDMI_CLKS_PLL_DIVSEL << 4));
+ cfg->com_coreclk_div = HDMI_CORECLK_DIV;
+
+ if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) {
+ cfg->tx_l0_tx_drv_lvl = 0x25;
+ cfg->tx_l0_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l1_tx_drv_lvl = 0x25;
+ cfg->tx_l1_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l2_tx_drv_lvl = 0x25;
+ cfg->tx_l2_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l3_tx_drv_lvl = 0x22;
+ cfg->tx_l3_tx_emp_post1_lvl = 0x27;
+ cfg->tx_l0_vmode_ctrl1 = 0x00;
+ cfg->tx_l0_vmode_ctrl2 = 0x0D;
+ cfg->tx_l1_vmode_ctrl1 = 0x00;
+ cfg->tx_l1_vmode_ctrl2 = 0x0D;
+ cfg->tx_l2_vmode_ctrl1 = 0x00;
+ cfg->tx_l2_vmode_ctrl2 = 0x0D;
+ cfg->tx_l3_vmode_ctrl1 = 0x00;
+ cfg->tx_l3_vmode_ctrl2 = 0x00;
+ cfg->com_restrim_ctrl = 0x0;
+ } else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) {
+ cfg->tx_l0_tx_drv_lvl = 0x25;
+ cfg->tx_l0_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l1_tx_drv_lvl = 0x25;
+ cfg->tx_l1_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l2_tx_drv_lvl = 0x25;
+ cfg->tx_l2_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l3_tx_drv_lvl = 0x25;
+ cfg->tx_l3_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l0_vmode_ctrl1 = 0x00;
+ cfg->tx_l0_vmode_ctrl2 = 0x0D;
+ cfg->tx_l1_vmode_ctrl1 = 0x00;
+ cfg->tx_l1_vmode_ctrl2 = 0x0D;
+ cfg->tx_l2_vmode_ctrl1 = 0x00;
+ cfg->tx_l2_vmode_ctrl2 = 0x0D;
+ cfg->tx_l3_vmode_ctrl1 = 0x00;
+ cfg->tx_l3_vmode_ctrl2 = 0x00;
+ cfg->com_restrim_ctrl = 0x0;
+ } else {
+ cfg->tx_l0_tx_drv_lvl = 0x20;
+ cfg->tx_l0_tx_emp_post1_lvl = 0x20;
+ cfg->tx_l1_tx_drv_lvl = 0x20;
+ cfg->tx_l1_tx_emp_post1_lvl = 0x20;
+ cfg->tx_l2_tx_drv_lvl = 0x20;
+ cfg->tx_l2_tx_emp_post1_lvl = 0x20;
+ cfg->tx_l3_tx_drv_lvl = 0x20;
+ cfg->tx_l3_tx_emp_post1_lvl = 0x20;
+ cfg->tx_l0_vmode_ctrl1 = 0x00;
+ cfg->tx_l0_vmode_ctrl2 = 0x0E;
+ cfg->tx_l1_vmode_ctrl1 = 0x00;
+ cfg->tx_l1_vmode_ctrl2 = 0x0E;
+ cfg->tx_l2_vmode_ctrl1 = 0x00;
+ cfg->tx_l2_vmode_ctrl2 = 0x0E;
+ cfg->tx_l3_vmode_ctrl1 = 0x00;
+ cfg->tx_l3_vmode_ctrl2 = 0x0E;
+ cfg->com_restrim_ctrl = 0xD8;
+ }
+
+ cfg->phy_mode = (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) ? 0x10 : 0x0;
+ DEV_DBG("HDMI 8996 PLL: PLL Settings\n");
+ DEV_DBG("PLL PARAM: tx_l0_lane_mode = 0x%x\n", cfg->tx_l0_lane_mode);
+ DEV_DBG("PLL PARAM: tx_l2_lane_mode = 0x%x\n", cfg->tx_l2_lane_mode);
+ DEV_DBG("PLL PARAM: tx_l0_tx_band = 0x%x\n", cfg->tx_l0_tx_band);
+ DEV_DBG("PLL PARAM: tx_l1_tx_band = 0x%x\n", cfg->tx_l1_tx_band);
+ DEV_DBG("PLL PARAM: tx_l2_tx_band = 0x%x\n", cfg->tx_l2_tx_band);
+ DEV_DBG("PLL PARAM: tx_l3_tx_band = 0x%x\n", cfg->tx_l3_tx_band);
+ DEV_DBG("PLL PARAM: com_svs_mode_clk_sel = 0x%x\n",
+ cfg->com_svs_mode_clk_sel);
+ DEV_DBG("PLL PARAM: com_hsclk_sel = 0x%x\n", cfg->com_hsclk_sel);
+ DEV_DBG("PLL PARAM: com_pll_cctrl_mode0 = 0x%x\n",
+ cfg->com_pll_cctrl_mode0);
+ DEV_DBG("PLL PARAM: com_pll_rctrl_mode0 = 0x%x\n",
+ cfg->com_pll_rctrl_mode0);
+ DEV_DBG("PLL PARAM: com_cp_ctrl_mode0 = 0x%x\n",
+ cfg->com_cp_ctrl_mode0);
+ DEV_DBG("PLL PARAM: com_dec_start_mode0 = 0x%x\n",
+ cfg->com_dec_start_mode0);
+ DEV_DBG("PLL PARAM: com_div_frac_start1_mode0 = 0x%x\n",
+ cfg->com_div_frac_start1_mode0);
+ DEV_DBG("PLL PARAM: com_div_frac_start2_mode0 = 0x%x\n",
+ cfg->com_div_frac_start2_mode0);
+ DEV_DBG("PLL PARAM: com_div_frac_start3_mode0 = 0x%x\n",
+ cfg->com_div_frac_start3_mode0);
+ DEV_DBG("PLL PARAM: com_integloop_gain0_mode0 = 0x%x\n",
+ cfg->com_integloop_gain0_mode0);
+ DEV_DBG("PLL PARAM: com_integloop_gain1_mode0 = 0x%x\n",
+ cfg->com_integloop_gain1_mode0);
+ DEV_DBG("PLL PARAM: com_lock_cmp1_mode0 = 0x%x\n",
+ cfg->com_lock_cmp1_mode0);
+ DEV_DBG("PLL PARAM: com_lock_cmp2_mode0 = 0x%x\n",
+ cfg->com_lock_cmp2_mode0);
+ DEV_DBG("PLL PARAM: com_lock_cmp3_mode0 = 0x%x\n",
+ cfg->com_lock_cmp3_mode0);
+ DEV_DBG("PLL PARAM: com_core_clk_en = 0x%x\n", cfg->com_core_clk_en);
+ DEV_DBG("PLL PARAM: com_coreclk_div = 0x%x\n", cfg->com_coreclk_div);
+ DEV_DBG("PLL PARAM: com_restrim_ctrl = 0x%x\n", cfg->com_restrim_ctrl);
+
+ DEV_DBG("PLL PARAM: l0_tx_drv_lvl = 0x%x\n", cfg->tx_l0_tx_drv_lvl);
+ DEV_DBG("PLL PARAM: l0_tx_emp_post1_lvl = 0x%x\n",
+ cfg->tx_l0_tx_emp_post1_lvl);
+ DEV_DBG("PLL PARAM: l1_tx_drv_lvl = 0x%x\n", cfg->tx_l1_tx_drv_lvl);
+ DEV_DBG("PLL PARAM: l1_tx_emp_post1_lvl = 0x%x\n",
+ cfg->tx_l1_tx_emp_post1_lvl);
+ DEV_DBG("PLL PARAM: l2_tx_drv_lvl = 0x%x\n", cfg->tx_l2_tx_drv_lvl);
+ DEV_DBG("PLL PARAM: l2_tx_emp_post1_lvl = 0x%x\n",
+ cfg->tx_l2_tx_emp_post1_lvl);
+ DEV_DBG("PLL PARAM: l3_tx_drv_lvl = 0x%x\n", cfg->tx_l3_tx_drv_lvl);
+ DEV_DBG("PLL PARAM: l3_tx_emp_post1_lvl = 0x%x\n",
+ cfg->tx_l3_tx_emp_post1_lvl);
+
+ DEV_DBG("PLL PARAM: l0_vmode_ctrl1 = 0x%x\n", cfg->tx_l0_vmode_ctrl1);
+ DEV_DBG("PLL PARAM: l0_vmode_ctrl2 = 0x%x\n", cfg->tx_l0_vmode_ctrl2);
+ DEV_DBG("PLL PARAM: l1_vmode_ctrl1 = 0x%x\n", cfg->tx_l1_vmode_ctrl1);
+ DEV_DBG("PLL PARAM: l1_vmode_ctrl2 = 0x%x\n", cfg->tx_l1_vmode_ctrl2);
+ DEV_DBG("PLL PARAM: l2_vmode_ctrl1 = 0x%x\n", cfg->tx_l2_vmode_ctrl1);
+ DEV_DBG("PLL PARAM: l2_vmode_ctrl2 = 0x%x\n", cfg->tx_l2_vmode_ctrl2);
+ DEV_DBG("PLL PARAM: l3_vmode_ctrl1 = 0x%x\n", cfg->tx_l3_vmode_ctrl1);
+ DEV_DBG("PLL PARAM: l3_vmode_ctrl2 = 0x%x\n", cfg->tx_l3_vmode_ctrl2);
+ DEV_DBG("PLL PARAM: tx_l0_res_code_lane_tx = 0x%x\n",
+ cfg->tx_l0_res_code_lane_tx);
+ DEV_DBG("PLL PARAM: tx_l1_res_code_lane_tx = 0x%x\n",
+ cfg->tx_l1_res_code_lane_tx);
+ DEV_DBG("PLL PARAM: tx_l2_res_code_lane_tx = 0x%x\n",
+ cfg->tx_l2_res_code_lane_tx);
+ DEV_DBG("PLL PARAM: tx_l3_res_code_lane_tx = 0x%x\n",
+ cfg->tx_l3_res_code_lane_tx);
+
+ DEV_DBG("PLL PARAM: phy_mode = 0x%x\n", cfg->phy_mode);
+ rc = 0;
+fail:
+ return rc;
+}
+
+static int hdmi_8996_v2_calculate(u32 pix_clk,
+ struct hdmi_8996_phy_pll_reg_cfg *cfg)
+{
+ int rc = -EINVAL;
+ u64 fdata, clk_divtx, tmds_clk;
+ u64 bclk;
+ u64 post_div;
+ u64 core_clk_div;
+ u64 core_clk_div_ratio;
+ u64 core_clk;
+ u64 pll_cmp;
+ u64 tx_band;
+ u64 tx_band_div_ratio;
+ u64 hsclk;
+ u64 dec_start;
+ u64 frac_start;
+ u64 pll_divisor = 4 * HDMI_REF_CLOCK;
+ u64 cpctrl;
+ u64 rctrl;
+ u64 cctrl;
+ u64 integloop_gain;
+ u64 vco_tune;
+ u64 vco_freq;
+ u64 vco_range;
+ u64 rem;
+
+ /* FDATA, CLK_DIVTX, PIXEL_CLK, TMDS_CLK */
+ bclk = ((u64)pix_clk) * HDMI_BIT_CLK_TO_PIX_CLK_RATIO;
+
+ if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD)
+ tmds_clk = pix_clk >> 2;
+ else
+ tmds_clk = pix_clk;
+
+ vco_range = bclk < HDMI_282MHZ_BIT_CLK_HZ ? HDMI_2000MHZ_BIT_CLK_HZ :
+ HDMI_2250MHZ_BIT_CLK_HZ;
+
+ fdata = hdmi_8996_v2_get_fdata(bclk, vco_range);
+ if (fdata == HDMI_64B_ERR_VAL)
+ goto fail;
+
+ hsclk = hdmi_8996_v2_get_hsclk(fdata, vco_range);
+ if (hsclk == HDMI_64B_ERR_VAL)
+ goto fail;
+
+ if (bclk >= vco_range)
+ post_div = hdmi_8996_v2_get_post_div_gt_2g(hsclk);
+ else
+ post_div = hdmi_8996_v2_get_post_div_lt_2g(bclk, vco_range);
+
+ if (post_div == HDMI_64B_ERR_VAL)
+ goto fail;
+
+ core_clk_div = 5;
+ core_clk_div_ratio = core_clk_div * 2;
+
+ tx_band = hdmi_8996_v2_get_tx_band(bclk, vco_range);
+ if (tx_band == HDMI_64B_ERR_VAL)
+ goto fail;
+
+ tx_band_div_ratio = 1 << tx_band;
+
+ vco_freq = hdmi_8996_v2_get_vco_freq(bclk, vco_range);
+ clk_divtx = vco_freq;
+ do_div(clk_divtx, post_div);
+
+ /* Decimal and fraction values */
+ dec_start = fdata * post_div;
+ do_div(dec_start, pll_divisor);
+ frac_start = ((pll_divisor - (((dec_start + 1) * pll_divisor) -
+ (fdata * post_div))) * (1 << 20));
+ rem = do_div(frac_start, pll_divisor);
+ /* Round off frac_start to closest integer */
+ if (rem >= (pll_divisor >> 1))
+ frac_start++;
+
+ cpctrl = hdmi_8996_get_cpctrl(frac_start, false);
+ rctrl = hdmi_8996_get_rctrl(frac_start, false);
+ cctrl = hdmi_8996_get_cctrl(frac_start, false);
+ integloop_gain = hdmi_8996_get_integloop_gain(frac_start, false);
+ vco_tune = hdmi_8996_get_vco_tune(fdata, post_div);
+
+ core_clk = clk_divtx;
+ do_div(core_clk, core_clk_div_ratio);
+ pll_cmp = hdmi_8996_get_pll_cmp(1024, core_clk);
+
+ /* Debug dump */
+ DEV_DBG("%s: VCO freq: %llu\n", __func__, vco_freq);
+ DEV_DBG("%s: fdata: %llu\n", __func__, fdata);
+ DEV_DBG("%s: CLK_DIVTX: %llu\n", __func__, clk_divtx);
+ DEV_DBG("%s: pix_clk: %d\n", __func__, pix_clk);
+ DEV_DBG("%s: tmds clk: %llu\n", __func__, tmds_clk);
+ DEV_DBG("%s: HSCLK_SEL: %llu\n", __func__, hsclk);
+ DEV_DBG("%s: DEC_START: %llu\n", __func__, dec_start);
+ DEV_DBG("%s: DIV_FRAC_START: %llu\n", __func__, frac_start);
+ DEV_DBG("%s: PLL_CPCTRL: %llu\n", __func__, cpctrl);
+ DEV_DBG("%s: PLL_RCTRL: %llu\n", __func__, rctrl);
+ DEV_DBG("%s: PLL_CCTRL: %llu\n", __func__, cctrl);
+ DEV_DBG("%s: INTEGLOOP_GAIN: %llu\n", __func__, integloop_gain);
+ DEV_DBG("%s: VCO_TUNE: %llu\n", __func__, vco_tune);
+ DEV_DBG("%s: TX_BAND: %llu\n", __func__, tx_band);
+ DEV_DBG("%s: PLL_CMP: %llu\n", __func__, pll_cmp);
+
+ /* Convert these values to register specific values */
+ cfg->tx_l0_lane_mode = 0x3;
+ cfg->tx_l2_lane_mode = 0x3;
+ cfg->tx_l0_tx_band = tx_band + 4;
+ cfg->tx_l1_tx_band = tx_band + 4;
+ cfg->tx_l2_tx_band = tx_band + 4;
+ cfg->tx_l3_tx_band = tx_band + 4;
+
+ if (bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD)
+ cfg->com_svs_mode_clk_sel = 1;
+ else
+ cfg->com_svs_mode_clk_sel = 2;
+
+ cfg->com_hsclk_sel = (0x28 | hsclk);
+ cfg->com_pll_cctrl_mode0 = cctrl;
+ cfg->com_pll_rctrl_mode0 = rctrl;
+ cfg->com_cp_ctrl_mode0 = cpctrl;
+ cfg->com_dec_start_mode0 = dec_start;
+ cfg->com_div_frac_start1_mode0 = (frac_start & 0xFF);
+ cfg->com_div_frac_start2_mode0 = ((frac_start & 0xFF00) >> 8);
+ cfg->com_div_frac_start3_mode0 = ((frac_start & 0xF0000) >> 16);
+ cfg->com_integloop_gain0_mode0 = (integloop_gain & 0xFF);
+ cfg->com_integloop_gain1_mode0 = ((integloop_gain & 0xF00) >> 8);
+ cfg->com_lock_cmp1_mode0 = (pll_cmp & 0xFF);
+ cfg->com_lock_cmp2_mode0 = ((pll_cmp & 0xFF00) >> 8);
+ cfg->com_lock_cmp3_mode0 = ((pll_cmp & 0x30000) >> 16);
+ cfg->com_core_clk_en = (0x6C | (HDMI_CLKS_PLL_DIVSEL << 4));
+ cfg->com_coreclk_div = HDMI_CORECLK_DIV;
+ cfg->com_vco_tune_ctrl = 0x0;
+
+ if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) {
+ cfg->tx_l0_tx_drv_lvl = 0x25;
+ cfg->tx_l0_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l1_tx_drv_lvl = 0x25;
+ cfg->tx_l1_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l2_tx_drv_lvl = 0x25;
+ cfg->tx_l2_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l3_tx_drv_lvl = 0x22;
+ cfg->tx_l3_tx_emp_post1_lvl = 0x27;
+ cfg->tx_l0_vmode_ctrl1 = 0x00;
+ cfg->tx_l0_vmode_ctrl2 = 0x0D;
+ cfg->tx_l1_vmode_ctrl1 = 0x00;
+ cfg->tx_l1_vmode_ctrl2 = 0x0D;
+ cfg->tx_l2_vmode_ctrl1 = 0x00;
+ cfg->tx_l2_vmode_ctrl2 = 0x0D;
+ cfg->tx_l3_vmode_ctrl1 = 0x00;
+ cfg->tx_l3_vmode_ctrl2 = 0x00;
+ cfg->tx_l0_res_code_lane_tx = 0x3F;
+ cfg->tx_l1_res_code_lane_tx = 0x3F;
+ cfg->tx_l2_res_code_lane_tx = 0x3F;
+ cfg->tx_l3_res_code_lane_tx = 0x3F;
+ cfg->com_restrim_ctrl = 0x0;
+ } else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) {
+ cfg->tx_l0_tx_drv_lvl = 0x25;
+ cfg->tx_l0_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l1_tx_drv_lvl = 0x25;
+ cfg->tx_l1_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l2_tx_drv_lvl = 0x25;
+ cfg->tx_l2_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l3_tx_drv_lvl = 0x25;
+ cfg->tx_l3_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l0_vmode_ctrl1 = 0x00;
+ cfg->tx_l0_vmode_ctrl2 = 0x0D;
+ cfg->tx_l1_vmode_ctrl1 = 0x00;
+ cfg->tx_l1_vmode_ctrl2 = 0x0D;
+ cfg->tx_l2_vmode_ctrl1 = 0x00;
+ cfg->tx_l2_vmode_ctrl2 = 0x0D;
+ cfg->tx_l3_vmode_ctrl1 = 0x00;
+ cfg->tx_l3_vmode_ctrl2 = 0x00;
+ cfg->tx_l0_res_code_lane_tx = 0x39;
+ cfg->tx_l1_res_code_lane_tx = 0x39;
+ cfg->tx_l2_res_code_lane_tx = 0x39;
+ cfg->tx_l3_res_code_lane_tx = 0x39;
+ cfg->com_restrim_ctrl = 0x0;
+ } else {
+ cfg->tx_l0_tx_drv_lvl = 0x20;
+ cfg->tx_l0_tx_emp_post1_lvl = 0x20;
+ cfg->tx_l1_tx_drv_lvl = 0x20;
+ cfg->tx_l1_tx_emp_post1_lvl = 0x20;
+ cfg->tx_l2_tx_drv_lvl = 0x20;
+ cfg->tx_l2_tx_emp_post1_lvl = 0x20;
+ cfg->tx_l3_tx_drv_lvl = 0x20;
+ cfg->tx_l3_tx_emp_post1_lvl = 0x20;
+ cfg->tx_l0_vmode_ctrl1 = 0x00;
+ cfg->tx_l0_vmode_ctrl2 = 0x0E;
+ cfg->tx_l1_vmode_ctrl1 = 0x00;
+ cfg->tx_l1_vmode_ctrl2 = 0x0E;
+ cfg->tx_l2_vmode_ctrl1 = 0x00;
+ cfg->tx_l2_vmode_ctrl2 = 0x0E;
+ cfg->tx_l3_vmode_ctrl1 = 0x00;
+ cfg->tx_l3_vmode_ctrl2 = 0x0E;
+ cfg->tx_l0_res_code_lane_tx = 0x3F;
+ cfg->tx_l1_res_code_lane_tx = 0x3F;
+ cfg->tx_l2_res_code_lane_tx = 0x3F;
+ cfg->tx_l3_res_code_lane_tx = 0x3F;
+ cfg->com_restrim_ctrl = 0xD8;
+ }
+
+ cfg->phy_mode = (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) ? 0x10 : 0x0;
+ DEV_DBG("HDMI 8996 PLL: PLL Settings\n");
+ DEV_DBG("PLL PARAM: tx_l0_lane_mode = 0x%x\n", cfg->tx_l0_lane_mode);
+ DEV_DBG("PLL PARAM: tx_l2_lane_mode = 0x%x\n", cfg->tx_l2_lane_mode);
+ DEV_DBG("PLL PARAM: tx_l0_tx_band = 0x%x\n", cfg->tx_l0_tx_band);
+ DEV_DBG("PLL PARAM: tx_l1_tx_band = 0x%x\n", cfg->tx_l1_tx_band);
+ DEV_DBG("PLL PARAM: tx_l2_tx_band = 0x%x\n", cfg->tx_l2_tx_band);
+ DEV_DBG("PLL PARAM: tx_l3_tx_band = 0x%x\n", cfg->tx_l3_tx_band);
+ DEV_DBG("PLL PARAM: com_svs_mode_clk_sel = 0x%x\n",
+ cfg->com_svs_mode_clk_sel);
+ DEV_DBG("PLL PARAM: com_vco_tune_ctrl = 0x%x\n",
+ cfg->com_vco_tune_ctrl);
+ DEV_DBG("PLL PARAM: com_hsclk_sel = 0x%x\n", cfg->com_hsclk_sel);
+ DEV_DBG("PLL PARAM: com_lock_cmp_en = 0x%x\n", cfg->com_lock_cmp_en);
+ DEV_DBG("PLL PARAM: com_pll_cctrl_mode0 = 0x%x\n",
+ cfg->com_pll_cctrl_mode0);
+ DEV_DBG("PLL PARAM: com_pll_rctrl_mode0 = 0x%x\n",
+ cfg->com_pll_rctrl_mode0);
+ DEV_DBG("PLL PARAM: com_cp_ctrl_mode0 = 0x%x\n",
+ cfg->com_cp_ctrl_mode0);
+ DEV_DBG("PLL PARAM: com_dec_start_mode0 = 0x%x\n",
+ cfg->com_dec_start_mode0);
+ DEV_DBG("PLL PARAM: com_div_frac_start1_mode0 = 0x%x\n",
+ cfg->com_div_frac_start1_mode0);
+ DEV_DBG("PLL PARAM: com_div_frac_start2_mode0 = 0x%x\n",
+ cfg->com_div_frac_start2_mode0);
+ DEV_DBG("PLL PARAM: com_div_frac_start3_mode0 = 0x%x\n",
+ cfg->com_div_frac_start3_mode0);
+ DEV_DBG("PLL PARAM: com_integloop_gain0_mode0 = 0x%x\n",
+ cfg->com_integloop_gain0_mode0);
+ DEV_DBG("PLL PARAM: com_integloop_gain1_mode0 = 0x%x\n",
+ cfg->com_integloop_gain1_mode0);
+ DEV_DBG("PLL PARAM: com_lock_cmp1_mode0 = 0x%x\n",
+ cfg->com_lock_cmp1_mode0);
+ DEV_DBG("PLL PARAM: com_lock_cmp2_mode0 = 0x%x\n",
+ cfg->com_lock_cmp2_mode0);
+ DEV_DBG("PLL PARAM: com_lock_cmp3_mode0 = 0x%x\n",
+ cfg->com_lock_cmp3_mode0);
+ DEV_DBG("PLL PARAM: com_core_clk_en = 0x%x\n", cfg->com_core_clk_en);
+ DEV_DBG("PLL PARAM: com_coreclk_div = 0x%x\n", cfg->com_coreclk_div);
+
+ DEV_DBG("PLL PARAM: l0_tx_drv_lvl = 0x%x\n", cfg->tx_l0_tx_drv_lvl);
+ DEV_DBG("PLL PARAM: l0_tx_emp_post1_lvl = 0x%x\n",
+ cfg->tx_l0_tx_emp_post1_lvl);
+ DEV_DBG("PLL PARAM: l1_tx_drv_lvl = 0x%x\n", cfg->tx_l1_tx_drv_lvl);
+ DEV_DBG("PLL PARAM: l1_tx_emp_post1_lvl = 0x%x\n",
+ cfg->tx_l1_tx_emp_post1_lvl);
+ DEV_DBG("PLL PARAM: l2_tx_drv_lvl = 0x%x\n", cfg->tx_l2_tx_drv_lvl);
+ DEV_DBG("PLL PARAM: l2_tx_emp_post1_lvl = 0x%x\n",
+ cfg->tx_l2_tx_emp_post1_lvl);
+ DEV_DBG("PLL PARAM: l3_tx_drv_lvl = 0x%x\n", cfg->tx_l3_tx_drv_lvl);
+ DEV_DBG("PLL PARAM: l3_tx_emp_post1_lvl = 0x%x\n",
+ cfg->tx_l3_tx_emp_post1_lvl);
+
+ DEV_DBG("PLL PARAM: l0_vmode_ctrl1 = 0x%x\n", cfg->tx_l0_vmode_ctrl1);
+ DEV_DBG("PLL PARAM: l0_vmode_ctrl2 = 0x%x\n", cfg->tx_l0_vmode_ctrl2);
+ DEV_DBG("PLL PARAM: l1_vmode_ctrl1 = 0x%x\n", cfg->tx_l1_vmode_ctrl1);
+ DEV_DBG("PLL PARAM: l1_vmode_ctrl2 = 0x%x\n", cfg->tx_l1_vmode_ctrl2);
+ DEV_DBG("PLL PARAM: l2_vmode_ctrl1 = 0x%x\n", cfg->tx_l2_vmode_ctrl1);
+ DEV_DBG("PLL PARAM: l2_vmode_ctrl2 = 0x%x\n", cfg->tx_l2_vmode_ctrl2);
+ DEV_DBG("PLL PARAM: l3_vmode_ctrl1 = 0x%x\n", cfg->tx_l3_vmode_ctrl1);
+ DEV_DBG("PLL PARAM: l3_vmode_ctrl2 = 0x%x\n", cfg->tx_l3_vmode_ctrl2);
+ DEV_DBG("PLL PARAM: tx_l0_res_code_lane_tx = 0x%x\n",
+ cfg->tx_l0_res_code_lane_tx);
+ DEV_DBG("PLL PARAM: tx_l1_res_code_lane_tx = 0x%x\n",
+ cfg->tx_l1_res_code_lane_tx);
+ DEV_DBG("PLL PARAM: tx_l2_res_code_lane_tx = 0x%x\n",
+ cfg->tx_l2_res_code_lane_tx);
+ DEV_DBG("PLL PARAM: tx_l3_res_code_lane_tx = 0x%x\n",
+ cfg->tx_l3_res_code_lane_tx);
+ DEV_DBG("PLL PARAM: com_restrim_ctrl = 0x%x\n", cfg->com_restrim_ctrl);
+
+ DEV_DBG("PLL PARAM: phy_mode = 0x%x\n", cfg->phy_mode);
+ rc = 0;
+fail:
+ return rc;
+}
+
+static int hdmi_8996_v3_calculate(u32 pix_clk,
+ struct hdmi_8996_phy_pll_reg_cfg *cfg)
+{
+ int rc = -EINVAL;
+ struct hdmi_8996_v3_post_divider pd;
+ u64 fdata, tmds_clk;
+ u64 bclk;
+ u64 pll_cmp;
+ u64 tx_band;
+ u64 hsclk;
+ u64 dec_start;
+ u64 frac_start;
+ u64 pll_divisor = 4 * HDMI_REF_CLOCK;
+ u64 cpctrl;
+ u64 rctrl;
+ u64 cctrl;
+ u64 integloop_gain;
+ u64 vco_freq;
+ u64 rem;
+
+ /* FDATA, HSCLK, PIXEL_CLK, TMDS_CLK */
+ bclk = ((u64)pix_clk) * HDMI_BIT_CLK_TO_PIX_CLK_RATIO;
+
+ if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD)
+ tmds_clk = pix_clk >> 2;
+ else
+ tmds_clk = pix_clk;
+
+ if (hdmi_8996_v3_get_post_div(&pd, bclk) || pd.vco_ratio <= 0 ||
+ pd.vco_freq <= 0)
+ goto fail;
+
+ vco_freq = pd.vco_freq;
+ fdata = pd.vco_freq;
+ do_div(fdata, pd.vco_ratio);
+
+ hsclk = pd.hsclk_divsel;
+ dec_start = vco_freq;
+ do_div(dec_start, pll_divisor);
+
+ frac_start = vco_freq * (1 << 20);
+ rem = do_div(frac_start, pll_divisor);
+ frac_start -= dec_start * (1 << 20);
+ if (rem > (pll_divisor >> 1))
+ frac_start++;
+
+ cpctrl = hdmi_8996_get_cpctrl(frac_start, false);
+ rctrl = hdmi_8996_get_rctrl(frac_start, false);
+ cctrl = hdmi_8996_get_cctrl(frac_start, false);
+ integloop_gain = hdmi_8996_v3_get_integloop_gain(frac_start, bclk,
+ false);
+ pll_cmp = hdmi_8996_v3_get_pll_cmp(1024, fdata);
+ tx_band = pd.tx_band_sel;
+
+ /* Debug dump */
+ DEV_DBG("%s: VCO freq: %llu\n", __func__, vco_freq);
+ DEV_DBG("%s: fdata: %llu\n", __func__, fdata);
+ DEV_DBG("%s: pix_clk: %d\n", __func__, pix_clk);
+ DEV_DBG("%s: tmds clk: %llu\n", __func__, tmds_clk);
+ DEV_DBG("%s: HSCLK_SEL: %llu\n", __func__, hsclk);
+ DEV_DBG("%s: DEC_START: %llu\n", __func__, dec_start);
+ DEV_DBG("%s: DIV_FRAC_START: %llu\n", __func__, frac_start);
+ DEV_DBG("%s: PLL_CPCTRL: %llu\n", __func__, cpctrl);
+ DEV_DBG("%s: PLL_RCTRL: %llu\n", __func__, rctrl);
+ DEV_DBG("%s: PLL_CCTRL: %llu\n", __func__, cctrl);
+ DEV_DBG("%s: INTEGLOOP_GAIN: %llu\n", __func__, integloop_gain);
+ DEV_DBG("%s: TX_BAND: %llu\n", __func__, tx_band);
+ DEV_DBG("%s: PLL_CMP: %llu\n", __func__, pll_cmp);
+
+ /* Convert these values to register specific values */
+ cfg->tx_l0_tx_band = tx_band + 4;
+ cfg->tx_l1_tx_band = tx_band + 4;
+ cfg->tx_l2_tx_band = tx_band + 4;
+ cfg->tx_l3_tx_band = tx_band + 4;
+
+ if (bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD)
+ cfg->com_svs_mode_clk_sel = 1;
+ else
+ cfg->com_svs_mode_clk_sel = 2;
+
+ cfg->com_hsclk_sel = (0x20 | hsclk);
+ cfg->com_pll_cctrl_mode0 = cctrl;
+ cfg->com_pll_rctrl_mode0 = rctrl;
+ cfg->com_cp_ctrl_mode0 = cpctrl;
+ cfg->com_dec_start_mode0 = dec_start;
+ cfg->com_div_frac_start1_mode0 = (frac_start & 0xFF);
+ cfg->com_div_frac_start2_mode0 = ((frac_start & 0xFF00) >> 8);
+ cfg->com_div_frac_start3_mode0 = ((frac_start & 0xF0000) >> 16);
+ cfg->com_integloop_gain0_mode0 = (integloop_gain & 0xFF);
+ cfg->com_integloop_gain1_mode0 = ((integloop_gain & 0xF00) >> 8);
+ cfg->com_lock_cmp1_mode0 = (pll_cmp & 0xFF);
+ cfg->com_lock_cmp2_mode0 = ((pll_cmp & 0xFF00) >> 8);
+ cfg->com_lock_cmp3_mode0 = ((pll_cmp & 0x30000) >> 16);
+ cfg->com_lock_cmp_en = 0x04;
+ cfg->com_core_clk_en = 0x2C;
+ cfg->com_coreclk_div = HDMI_CORECLK_DIV;
+ cfg->phy_mode = (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) ? 0x10 : 0x0;
+ cfg->com_vco_tune_ctrl = 0x0;
+
+ cfg->tx_l0_lane_mode = 0x43;
+ cfg->tx_l2_lane_mode = 0x43;
+
+ if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) {
+ cfg->tx_l0_tx_drv_lvl = 0x25;
+ cfg->tx_l0_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l1_tx_drv_lvl = 0x25;
+ cfg->tx_l1_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l2_tx_drv_lvl = 0x25;
+ cfg->tx_l2_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l3_tx_drv_lvl = 0x22;
+ cfg->tx_l3_tx_emp_post1_lvl = 0x27;
+ cfg->tx_l0_vmode_ctrl1 = 0x00;
+ cfg->tx_l0_vmode_ctrl2 = 0x0D;
+ cfg->tx_l1_vmode_ctrl1 = 0x00;
+ cfg->tx_l1_vmode_ctrl2 = 0x0D;
+ cfg->tx_l2_vmode_ctrl1 = 0x00;
+ cfg->tx_l2_vmode_ctrl2 = 0x0D;
+ cfg->tx_l3_vmode_ctrl1 = 0x00;
+ cfg->tx_l3_vmode_ctrl2 = 0x00;
+ } else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) {
+ cfg->tx_l0_tx_drv_lvl = 0x25;
+ cfg->tx_l0_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l1_tx_drv_lvl = 0x25;
+ cfg->tx_l1_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l2_tx_drv_lvl = 0x25;
+ cfg->tx_l2_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l3_tx_drv_lvl = 0x25;
+ cfg->tx_l3_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l0_vmode_ctrl1 = 0x00;
+ cfg->tx_l0_vmode_ctrl2 = 0x0D;
+ cfg->tx_l1_vmode_ctrl1 = 0x00;
+ cfg->tx_l1_vmode_ctrl2 = 0x0D;
+ cfg->tx_l2_vmode_ctrl1 = 0x00;
+ cfg->tx_l2_vmode_ctrl2 = 0x0D;
+ cfg->tx_l3_vmode_ctrl1 = 0x00;
+ cfg->tx_l3_vmode_ctrl2 = 0x00;
+ } else {
+ cfg->tx_l0_tx_drv_lvl = 0x20;
+ cfg->tx_l0_tx_emp_post1_lvl = 0x20;
+ cfg->tx_l1_tx_drv_lvl = 0x20;
+ cfg->tx_l1_tx_emp_post1_lvl = 0x20;
+ cfg->tx_l2_tx_drv_lvl = 0x20;
+ cfg->tx_l2_tx_emp_post1_lvl = 0x20;
+ cfg->tx_l3_tx_drv_lvl = 0x20;
+ cfg->tx_l3_tx_emp_post1_lvl = 0x20;
+ cfg->tx_l0_vmode_ctrl1 = 0x00;
+ cfg->tx_l0_vmode_ctrl2 = 0x0E;
+ cfg->tx_l1_vmode_ctrl1 = 0x00;
+ cfg->tx_l1_vmode_ctrl2 = 0x0E;
+ cfg->tx_l2_vmode_ctrl1 = 0x00;
+ cfg->tx_l2_vmode_ctrl2 = 0x0E;
+ cfg->tx_l3_vmode_ctrl1 = 0x00;
+ cfg->tx_l3_vmode_ctrl2 = 0x0E;
+ }
+
+ DEV_DBG("HDMI 8996 PLL: PLL Settings\n");
+ DEV_DBG("PLL PARAM: tx_l0_tx_band = 0x%x\n", cfg->tx_l0_tx_band);
+ DEV_DBG("PLL PARAM: tx_l1_tx_band = 0x%x\n", cfg->tx_l1_tx_band);
+ DEV_DBG("PLL PARAM: tx_l2_tx_band = 0x%x\n", cfg->tx_l2_tx_band);
+ DEV_DBG("PLL PARAM: tx_l3_tx_band = 0x%x\n", cfg->tx_l3_tx_band);
+ DEV_DBG("PLL PARAM: com_svs_mode_clk_sel = 0x%x\n",
+ cfg->com_svs_mode_clk_sel);
+ DEV_DBG("PLL PARAM: com_hsclk_sel = 0x%x\n", cfg->com_hsclk_sel);
+ DEV_DBG("PLL PARAM: com_lock_cmp_en = 0x%x\n", cfg->com_lock_cmp_en);
+ DEV_DBG("PLL PARAM: com_pll_cctrl_mode0 = 0x%x\n",
+ cfg->com_pll_cctrl_mode0);
+ DEV_DBG("PLL PARAM: com_pll_rctrl_mode0 = 0x%x\n",
+ cfg->com_pll_rctrl_mode0);
+ DEV_DBG("PLL PARAM: com_cp_ctrl_mode0 = 0x%x\n",
+ cfg->com_cp_ctrl_mode0);
+ DEV_DBG("PLL PARAM: com_dec_start_mode0 = 0x%x\n",
+ cfg->com_dec_start_mode0);
+ DEV_DBG("PLL PARAM: com_div_frac_start1_mode0 = 0x%x\n",
+ cfg->com_div_frac_start1_mode0);
+ DEV_DBG("PLL PARAM: com_div_frac_start2_mode0 = 0x%x\n",
+ cfg->com_div_frac_start2_mode0);
+ DEV_DBG("PLL PARAM: com_div_frac_start3_mode0 = 0x%x\n",
+ cfg->com_div_frac_start3_mode0);
+ DEV_DBG("PLL PARAM: com_integloop_gain0_mode0 = 0x%x\n",
+ cfg->com_integloop_gain0_mode0);
+ DEV_DBG("PLL PARAM: com_integloop_gain1_mode0 = 0x%x\n",
+ cfg->com_integloop_gain1_mode0);
+ DEV_DBG("PLL PARAM: com_lock_cmp1_mode0 = 0x%x\n",
+ cfg->com_lock_cmp1_mode0);
+ DEV_DBG("PLL PARAM: com_lock_cmp2_mode0 = 0x%x\n",
+ cfg->com_lock_cmp2_mode0);
+ DEV_DBG("PLL PARAM: com_lock_cmp3_mode0 = 0x%x\n",
+ cfg->com_lock_cmp3_mode0);
+ DEV_DBG("PLL PARAM: com_core_clk_en = 0x%x\n", cfg->com_core_clk_en);
+ DEV_DBG("PLL PARAM: com_coreclk_div = 0x%x\n", cfg->com_coreclk_div);
+ DEV_DBG("PLL PARAM: phy_mode = 0x%x\n", cfg->phy_mode);
+
+ DEV_DBG("PLL PARAM: tx_l0_lane_mode = 0x%x\n", cfg->tx_l0_lane_mode);
+ DEV_DBG("PLL PARAM: tx_l2_lane_mode = 0x%x\n", cfg->tx_l2_lane_mode);
+ DEV_DBG("PLL PARAM: l0_tx_drv_lvl = 0x%x\n", cfg->tx_l0_tx_drv_lvl);
+ DEV_DBG("PLL PARAM: l0_tx_emp_post1_lvl = 0x%x\n",
+ cfg->tx_l0_tx_emp_post1_lvl);
+ DEV_DBG("PLL PARAM: l1_tx_drv_lvl = 0x%x\n", cfg->tx_l1_tx_drv_lvl);
+ DEV_DBG("PLL PARAM: l1_tx_emp_post1_lvl = 0x%x\n",
+ cfg->tx_l1_tx_emp_post1_lvl);
+ DEV_DBG("PLL PARAM: l2_tx_drv_lvl = 0x%x\n", cfg->tx_l2_tx_drv_lvl);
+ DEV_DBG("PLL PARAM: l2_tx_emp_post1_lvl = 0x%x\n",
+ cfg->tx_l2_tx_emp_post1_lvl);
+ DEV_DBG("PLL PARAM: l3_tx_drv_lvl = 0x%x\n", cfg->tx_l3_tx_drv_lvl);
+ DEV_DBG("PLL PARAM: l3_tx_emp_post1_lvl = 0x%x\n",
+ cfg->tx_l3_tx_emp_post1_lvl);
+
+ DEV_DBG("PLL PARAM: l0_vmode_ctrl1 = 0x%x\n", cfg->tx_l0_vmode_ctrl1);
+ DEV_DBG("PLL PARAM: l0_vmode_ctrl2 = 0x%x\n", cfg->tx_l0_vmode_ctrl2);
+ DEV_DBG("PLL PARAM: l1_vmode_ctrl1 = 0x%x\n", cfg->tx_l1_vmode_ctrl1);
+ DEV_DBG("PLL PARAM: l1_vmode_ctrl2 = 0x%x\n", cfg->tx_l1_vmode_ctrl2);
+ DEV_DBG("PLL PARAM: l2_vmode_ctrl1 = 0x%x\n", cfg->tx_l2_vmode_ctrl1);
+ DEV_DBG("PLL PARAM: l2_vmode_ctrl2 = 0x%x\n", cfg->tx_l2_vmode_ctrl2);
+ DEV_DBG("PLL PARAM: l3_vmode_ctrl1 = 0x%x\n", cfg->tx_l3_vmode_ctrl1);
+ DEV_DBG("PLL PARAM: l3_vmode_ctrl2 = 0x%x\n", cfg->tx_l3_vmode_ctrl2);
+ rc = 0;
+fail:
+ return rc;
+}
+
+static int hdmi_8996_calculate(u32 pix_clk,
+ struct hdmi_8996_phy_pll_reg_cfg *cfg, u32 ver)
+{
+ switch (ver) {
+ case HDMI_VERSION_8996_V3:
+ case HDMI_VERSION_8996_V3_1_8:
+ return hdmi_8996_v3_calculate(pix_clk, cfg);
+ case HDMI_VERSION_8996_V2:
+ return hdmi_8996_v2_calculate(pix_clk, cfg);
+ default:
+ return hdmi_8996_v1_calculate(pix_clk, cfg);
+ }
+}
+
+static int hdmi_8996_phy_pll_set_clk_rate(struct clk *c, u32 tmds_clk, u32 ver)
+{
+ int rc = 0;
+ struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
+ struct mdss_pll_resources *io = vco->priv;
+ struct hdmi_8996_phy_pll_reg_cfg cfg = {0};
+
+ rc = hdmi_8996_calculate(tmds_clk, &cfg, ver);
+ if (rc) {
+ DEV_ERR("%s: PLL calculation failed\n", __func__);
+ return rc;
+ }
+
+ /* Initially shut down PHY */
+ DEV_DBG("%s: Disabling PHY\n", __func__);
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_PD_CTL, 0x0);
+ udelay(500);
+
+ /* Power up sequence */
+ switch (ver) {
+ case HDMI_VERSION_8996_V2:
+ case HDMI_VERSION_8996_V3:
+ case HDMI_VERSION_8996_V3_1_8:
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BG_CTRL, 0x04);
+ break;
+ };
+
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_PD_CTL, 0x1);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_RESETSM_CNTRL, 0x20);
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_TX0_TX1_LANE_CTL, 0x0F);
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_TX2_TX3_LANE_CTL, 0x0F);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_CLKBUF_ENABLE, 0x03);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_CLKBUF_ENABLE, 0x03);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_CLKBUF_ENABLE, 0x03);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_CLKBUF_ENABLE, 0x03);
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_LANE_MODE, cfg.tx_l0_lane_mode);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_LANE_MODE, cfg.tx_l2_lane_mode);
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_TX_BAND, cfg.tx_l0_tx_band);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_TX_BAND, cfg.tx_l1_tx_band);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_TX_BAND, cfg.tx_l2_tx_band);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_TX_BAND, cfg.tx_l3_tx_band);
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_RESET_TSYNC_EN, 0x03);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_RESET_TSYNC_EN, 0x03);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_RESET_TSYNC_EN, 0x03);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_RESET_TSYNC_EN, 0x03);
+
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1E);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x07);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SYSCLK_EN_SEL, 0x37);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SYS_CLK_CTRL, 0x02);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CLK_ENABLE1, 0x0E);
+ if (ver == HDMI_VERSION_8996_V1)
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BG_CTRL, 0x06);
+
+ /* Bypass VCO calibration */
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SVS_MODE_CLK_SEL,
+ cfg.com_svs_mode_clk_sel);
+
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BG_TRIM, 0x0F);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_IVCO, 0x0F);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE_CTRL,
+ cfg.com_vco_tune_ctrl);
+
+ switch (ver) {
+ case HDMI_VERSION_8996_V1:
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SVS_MODE_CLK_SEL,
+ cfg.com_svs_mode_clk_sel);
+ break;
+ default:
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BG_CTRL, 0x06);
+ }
+
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CLK_SELECT, 0x30);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_HSCLK_SEL,
+ cfg.com_hsclk_sel);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_EN,
+ cfg.com_lock_cmp_en);
+
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_CCTRL_MODE0,
+ cfg.com_pll_cctrl_mode0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_RCTRL_MODE0,
+ cfg.com_pll_rctrl_mode0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CP_CTRL_MODE0,
+ cfg.com_cp_ctrl_mode0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DEC_START_MODE0,
+ cfg.com_dec_start_mode0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DIV_FRAC_START1_MODE0,
+ cfg.com_div_frac_start1_mode0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DIV_FRAC_START2_MODE0,
+ cfg.com_div_frac_start2_mode0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DIV_FRAC_START3_MODE0,
+ cfg.com_div_frac_start3_mode0);
+
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_INTEGLOOP_GAIN0_MODE0,
+ cfg.com_integloop_gain0_mode0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_INTEGLOOP_GAIN1_MODE0,
+ cfg.com_integloop_gain1_mode0);
+
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP1_MODE0,
+ cfg.com_lock_cmp1_mode0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP2_MODE0,
+ cfg.com_lock_cmp2_mode0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP3_MODE0,
+ cfg.com_lock_cmp3_mode0);
+
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE_MAP, 0x00);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CORE_CLK_EN,
+ cfg.com_core_clk_en);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CORECLK_DIV,
+ cfg.com_coreclk_div);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_CONFIG, 0x02);
+
+ if (ver == HDMI_VERSION_8996_V3 || ver == HDMI_VERSION_8996_V3_1_8)
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_RESCODE_DIV_NUM, 0x15);
+
+ /* TX lanes setup (TX 0/1/2/3) */
+ if (ver == HDMI_VERSION_8996_V3_1_8) {
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_TX_DRV_LVL,
+ 0x00000023);
+ } else {
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_TX_DRV_LVL,
+ cfg.tx_l0_tx_drv_lvl);
+ }
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_TX_EMP_POST1_LVL,
+ cfg.tx_l0_tx_emp_post1_lvl);
+
+ if (ver == HDMI_VERSION_8996_V3_1_8) {
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_TX_DRV_LVL,
+ 0x00000023);
+ } else {
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_TX_DRV_LVL,
+ cfg.tx_l1_tx_drv_lvl);
+ }
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_TX_EMP_POST1_LVL,
+ cfg.tx_l1_tx_emp_post1_lvl);
+
+ if (ver == HDMI_VERSION_8996_V3_1_8) {
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_TX_DRV_LVL,
+ 0x00000023);
+ } else {
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_TX_DRV_LVL,
+ cfg.tx_l2_tx_drv_lvl);
+ }
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_TX_EMP_POST1_LVL,
+ cfg.tx_l2_tx_emp_post1_lvl);
+
+ if (ver == HDMI_VERSION_8996_V3_1_8) {
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_TX_DRV_LVL,
+ 0x00000020);
+ } else {
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_TX_DRV_LVL,
+ cfg.tx_l3_tx_drv_lvl);
+ }
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_TX_EMP_POST1_LVL,
+ cfg.tx_l3_tx_emp_post1_lvl);
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_VMODE_CTRL1,
+ cfg.tx_l0_vmode_ctrl1);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_VMODE_CTRL2,
+ cfg.tx_l0_vmode_ctrl2);
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_VMODE_CTRL1,
+ cfg.tx_l1_vmode_ctrl1);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_VMODE_CTRL2,
+ cfg.tx_l1_vmode_ctrl2);
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_VMODE_CTRL1,
+ cfg.tx_l2_vmode_ctrl1);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_VMODE_CTRL2,
+ cfg.tx_l2_vmode_ctrl2);
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_VMODE_CTRL1,
+ cfg.tx_l3_vmode_ctrl1);
+ if (ver == HDMI_VERSION_8996_V3_1_8) {
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_VMODE_CTRL2,
+ 0x0000000D);
+ } else {
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_VMODE_CTRL2,
+ cfg.tx_l3_vmode_ctrl2);
+ }
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_TX_DRV_LVL_OFFSET, 0x00);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_TX_DRV_LVL_OFFSET, 0x00);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_TX_DRV_LVL_OFFSET, 0x00);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_TX_DRV_LVL_OFFSET, 0x00);
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_RES_CODE_LANE_OFFSET, 0x00);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_RES_CODE_LANE_OFFSET, 0x00);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_RES_CODE_LANE_OFFSET, 0x00);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_RES_CODE_LANE_OFFSET, 0x00);
+
+ if (ver < HDMI_VERSION_8996_V3) {
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_RES_CODE_LANE_TX,
+ cfg.tx_l0_res_code_lane_tx);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_RES_CODE_LANE_TX,
+ cfg.tx_l1_res_code_lane_tx);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_RES_CODE_LANE_TX,
+ cfg.tx_l2_res_code_lane_tx);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_RES_CODE_LANE_TX,
+ cfg.tx_l3_res_code_lane_tx);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_RESTRIM_CTRL,
+ cfg.com_restrim_ctrl);
+
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_TXCAL_CFG0, 0x00);
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_TXCAL_CFG1, 0x05);
+ }
+
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_MODE, cfg.phy_mode);
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_PD_CTL, 0x1F);
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_TRAN_DRVR_EMP_EN, 0x03);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_TRAN_DRVR_EMP_EN, 0x03);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_TRAN_DRVR_EMP_EN, 0x03);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_TRAN_DRVR_EMP_EN, 0x03);
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN, 0x40);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN, 0x40);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN, 0x40);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN, 0x40);
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_HP_PD_ENABLES, 0x0C);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_HP_PD_ENABLES, 0x0C);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_HP_PD_ENABLES, 0x0C);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_HP_PD_ENABLES, 0x03);
+
+ if (ver == HDMI_VERSION_8996_V2) {
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_ATB_SEL1, 0x01);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_ATB_SEL2, 0x01);
+ }
+ /*
+ * Ensure that vco configuration gets flushed to hardware before
+ * enabling the PLL
+ */
+ wmb();
+ return 0;
+}
+
+static int hdmi_8996_phy_ready_status(struct mdss_pll_resources *io)
+{
+ u32 status = 0;
+ int phy_ready = 0;
+ int rc;
+ u32 read_count = 0;
+
+ rc = mdss_pll_resource_enable(io, true);
+ if (rc) {
+ DEV_ERR("%s: pll resource can't be enabled\n", __func__);
+ return rc;
+ }
+
+ DEV_DBG("%s: Waiting for PHY Ready\n", __func__);
+
+ /* Poll for PHY read status */
+ while (read_count < HDMI_PLL_POLL_MAX_READS) {
+ status = MDSS_PLL_REG_R(io->phy_base, HDMI_PHY_STATUS);
+ if ((status & BIT(0)) == 1) {
+ phy_ready = 1;
+ DEV_DBG("%s: PHY READY\n", __func__);
+ break;
+ }
+ udelay(HDMI_PLL_POLL_TIMEOUT_US);
+ read_count++;
+ }
+
+ if (read_count == HDMI_PLL_POLL_MAX_READS) {
+ phy_ready = 0;
+ DEV_DBG("%s: PHY READY TIMEOUT\n", __func__);
+ }
+
+ mdss_pll_resource_enable(io, false);
+
+ return phy_ready;
+}
+
+static int hdmi_8996_pll_lock_status(struct mdss_pll_resources *io)
+{
+ u32 status;
+ int pll_locked = 0;
+ int rc;
+ u32 read_count = 0;
+
+ rc = mdss_pll_resource_enable(io, true);
+ if (rc) {
+ DEV_ERR("%s: pll resource can't be enabled\n", __func__);
+ return rc;
+ }
+
+ DEV_DBG("%s: Waiting for PLL lock\n", __func__);
+
+ while (read_count < HDMI_PLL_POLL_MAX_READS) {
+ status = MDSS_PLL_REG_R(io->pll_base,
+ QSERDES_COM_C_READY_STATUS);
+ if ((status & BIT(0)) == 1) {
+ pll_locked = 1;
+ DEV_DBG("%s: C READY\n", __func__);
+ break;
+ }
+ udelay(HDMI_PLL_POLL_TIMEOUT_US);
+ read_count++;
+ }
+
+ if (read_count == HDMI_PLL_POLL_MAX_READS) {
+ pll_locked = 0;
+ DEV_DBG("%s: C READY TIMEOUT\n", __func__);
+ }
+
+ mdss_pll_resource_enable(io, false);
+
+ return pll_locked;
+}
+
+static int hdmi_8996_v1_perform_sw_calibration(struct clk *c)
+{
+ int rc = 0;
+ struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
+ struct mdss_pll_resources *io = vco->priv;
+
+ u32 max_code = 0x190;
+ u32 min_code = 0x0;
+ u32 max_cnt = 0;
+ u32 min_cnt = 0;
+ u32 expected_counter_value = 0;
+ u32 step = 0;
+ u32 dbus_all = 0;
+ u32 dbus_sel = 0;
+ u32 vco_code = 0;
+ u32 val = 0;
+
+ vco_code = 0xC8;
+
+ DEV_DBG("%s: Starting SW calibration with vco_code = %d\n", __func__,
+ vco_code);
+
+ expected_counter_value =
+ (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP3_MODE0) << 16) |
+ (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP2_MODE0) << 8) |
+ (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP1_MODE0));
+
+ DEV_DBG("%s: expected_counter_value = %d\n", __func__,
+ expected_counter_value);
+
+ val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_CMN_MISC1);
+ val |= BIT(4);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_MISC1, val);
+
+ val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_CMN_MISC1);
+ val |= BIT(3);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_MISC1, val);
+
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DEBUG_BUS_SEL, 0x4);
+
+ val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP_CFG);
+ val |= BIT(1);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_CFG, val);
+
+ udelay(60);
+
+ while (1) {
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE1_MODE0,
+ vco_code & 0xFF);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE2_MODE0,
+ (vco_code >> 8) & 0x3);
+
+ udelay(20);
+
+ val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP_CFG);
+ val &= ~BIT(1);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_CFG, val);
+
+ udelay(60);
+
+ val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP_CFG);
+ val |= BIT(1);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_CFG, val);
+
+ udelay(60);
+
+ dbus_all =
+ (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEBUG_BUS3) << 24) |
+ (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEBUG_BUS2) << 16) |
+ (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEBUG_BUS1) << 8) |
+ (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEBUG_BUS0));
+
+ dbus_sel = (dbus_all >> 9) & 0x3FFFF;
+ DEV_DBG("%s: loop[%d], dbus_all = 0x%x, dbus_sel = 0x%x\n",
+ __func__, step, dbus_all, dbus_sel);
+ if (dbus_sel == 0)
+ DEV_ERR("%s: CHECK HDMI REF CLK\n", __func__);
+
+ if (dbus_sel == expected_counter_value) {
+ max_code = vco_code;
+ max_cnt = dbus_sel;
+ min_code = vco_code;
+ min_cnt = dbus_sel;
+ } else if (dbus_sel == 0) {
+ max_code = vco_code;
+ max_cnt = dbus_sel;
+ vco_code = (max_code + min_code)/2;
+ } else if (dbus_sel > expected_counter_value) {
+ min_code = vco_code;
+ min_cnt = dbus_sel;
+ vco_code = (max_code + min_code)/2;
+ } else if (dbus_sel < expected_counter_value) {
+ max_code = vco_code;
+ max_cnt = dbus_sel;
+ vco_code = (max_code + min_code)/2;
+ }
+
+ step++;
+
+ if ((vco_code == 0) || (vco_code == 0x3FF) || (step > 0x3FF)) {
+ DEV_ERR("%s: VCO tune code search failed\n", __func__);
+ rc = -ENOTSUPP;
+ break;
+ }
+ if ((max_code - min_code) <= 1) {
+ if ((max_code - min_code) == 1) {
+ if (abs((int)(max_cnt - expected_counter_value))
+ < abs((int)(min_cnt - expected_counter_value
+ ))) {
+ vco_code = max_code;
+ } else {
+ vco_code = min_code;
+ }
+ }
+ break;
+ }
+ DEV_DBG("%s: loop[%d], new vco_code = %d\n", __func__, step,
+ vco_code);
+ }
+
+ DEV_DBG("%s: CALIB done. vco_code = %d\n", __func__, vco_code);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE1_MODE0,
+ vco_code & 0xFF);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE2_MODE0,
+ (vco_code >> 8) & 0x3);
+ val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP_CFG);
+ val &= ~BIT(1);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_CFG, val);
+
+ val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_CMN_MISC1);
+ val |= BIT(4);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_MISC1, val);
+
+ val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_CMN_MISC1);
+ val &= ~BIT(3);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_MISC1, val);
+
+ return rc;
+}
+
+static int hdmi_8996_v2_perform_sw_calibration(struct clk *c)
+{
+ int rc = 0;
+ struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
+ struct mdss_pll_resources *io = vco->priv;
+ u32 vco_code1, vco_code2, integral_loop, ready_poll;
+ u32 read_count = 0;
+
+ while (read_count < (HDMI_PLL_POLL_MAX_READS << 1)) {
+ ready_poll = MDSS_PLL_REG_R(io->pll_base,
+ QSERDES_COM_C_READY_STATUS);
+ if ((ready_poll & BIT(0)) == 1) {
+ ready_poll = 1;
+ DEV_DBG("%s: C READY\n", __func__);
+ break;
+ }
+ udelay(HDMI_PLL_POLL_TIMEOUT_US);
+ read_count++;
+ }
+
+ if (read_count == (HDMI_PLL_POLL_MAX_READS << 1)) {
+ ready_poll = 0;
+ DEV_DBG("%s: C READY TIMEOUT, TRYING SW CALIBRATION\n",
+ __func__);
+ }
+
+ vco_code1 = MDSS_PLL_REG_R(io->pll_base,
+ QSERDES_COM_PLLCAL_CODE1_STATUS);
+ vco_code2 = MDSS_PLL_REG_R(io->pll_base,
+ QSERDES_COM_PLLCAL_CODE2_STATUS);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DEBUG_BUS_SEL, 0x5);
+ integral_loop = MDSS_PLL_REG_R(io->pll_base,
+ QSERDES_COM_DEBUG_BUS0);
+
+ if (((ready_poll & 0x1) == 0) || (((ready_poll & 1) == 1) &&
+ (vco_code1 == 0xFF) && ((vco_code2 & 0x3) == 0x1) &&
+ (integral_loop > 0xC0))) {
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_ATB_SEL1, 0x04);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_ATB_SEL2, 0x00);
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x17);
+ udelay(100);
+
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x11);
+ udelay(100);
+
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x19);
+ }
+ return rc;
+}
+
+static int hdmi_8996_perform_sw_calibration(struct clk *c, u32 ver)
+{
+ switch (ver) {
+ case HDMI_VERSION_8996_V1:
+ return hdmi_8996_v1_perform_sw_calibration(c);
+ case HDMI_VERSION_8996_V2:
+ return hdmi_8996_v2_perform_sw_calibration(c);
+ }
+ return 0;
+}
+
+static int hdmi_8996_vco_enable(struct clk *c, u32 ver)
+{
+ int rc = 0;
+ struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
+ struct mdss_pll_resources *io = vco->priv;
+
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x1);
+ udelay(100);
+
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x19);
+ udelay(100);
+
+ rc = hdmi_8996_perform_sw_calibration(c, ver);
+ if (rc) {
+ DEV_ERR("%s: software calibration failed\n", __func__);
+ return rc;
+ }
+
+ rc = hdmi_8996_pll_lock_status(io);
+ if (!rc) {
+ DEV_ERR("%s: PLL not locked\n", __func__);
+ return rc;
+ }
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN,
+ 0x6F);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN,
+ 0x6F);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN,
+ 0x6F);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN,
+ 0x6F);
+
+ /* Disable SSC */
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_PER1, 0x0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_PER2, 0x0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_STEP_SIZE1, 0x0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_STEP_SIZE2, 0x0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_EN_CENTER, 0x2);
+
+ rc = hdmi_8996_phy_ready_status(io);
+ if (!rc) {
+ DEV_ERR("%s: PHY not READY\n", __func__);
+ return rc;
+ }
+
+ /* Restart the retiming buffer */
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x18);
+ udelay(1);
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x19);
+
+ io->pll_on = true;
+ return 0;
+}
+
+static int hdmi_8996_v1_vco_enable(struct clk *c)
+{
+ return hdmi_8996_vco_enable(c, HDMI_VERSION_8996_V1);
+}
+
+static int hdmi_8996_v2_vco_enable(struct clk *c)
+{
+ return hdmi_8996_vco_enable(c, HDMI_VERSION_8996_V2);
+}
+
+static int hdmi_8996_v3_vco_enable(struct clk *c)
+{
+ return hdmi_8996_vco_enable(c, HDMI_VERSION_8996_V3);
+}
+
+static int hdmi_8996_v3_1p8_vco_enable(struct clk *c)
+{
+ return hdmi_8996_vco_enable(c, HDMI_VERSION_8996_V3_1_8);
+}
+
+static int hdmi_8996_vco_get_lock_range(struct clk *c, unsigned long pixel_clk)
+{
+ u32 rng = 64, cmp_cnt = 1024;
+ u32 coreclk_div = 5, clks_pll_divsel = 2;
+ u32 vco_freq, vco_ratio, ppm_range;
+ u64 bclk;
+ struct hdmi_8996_v3_post_divider pd;
+
+ bclk = ((u64)pixel_clk) * HDMI_BIT_CLK_TO_PIX_CLK_RATIO;
+
+ DEV_DBG("%s: rate=%ld\n", __func__, pixel_clk);
+
+ if (hdmi_8996_v3_get_post_div(&pd, bclk) ||
+ pd.vco_ratio <= 0 || pd.vco_freq <= 0) {
+ DEV_ERR("%s: couldn't get post div\n", __func__);
+ return -EINVAL;
+ }
+
+ do_div(pd.vco_freq, HDMI_KHZ_TO_HZ * HDMI_KHZ_TO_HZ);
+
+ vco_freq = (u32) pd.vco_freq;
+ vco_ratio = (u32) pd.vco_ratio;
+
+ DEV_DBG("%s: freq %d, ratio %d\n", __func__,
+ vco_freq, vco_ratio);
+
+ ppm_range = (rng * HDMI_REF_CLOCK) / cmp_cnt;
+ ppm_range /= vco_freq / vco_ratio;
+ ppm_range *= coreclk_div * clks_pll_divsel;
+
+ DEV_DBG("%s: ppm range: %d\n", __func__, ppm_range);
+
+ return ppm_range;
+}
+
+static int hdmi_8996_vco_rate_atomic_update(struct clk *c,
+ unsigned long rate, u32 ver)
+{
+ struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
+ struct mdss_pll_resources *io = vco->priv;
+ void __iomem *pll;
+ struct hdmi_8996_phy_pll_reg_cfg cfg = {0};
+ int rc = 0;
+
+ rc = hdmi_8996_calculate(rate, &cfg, ver);
+ if (rc) {
+ DEV_ERR("%s: PLL calculation failed\n", __func__);
+ goto end;
+ }
+
+ pll = io->pll_base;
+
+ MDSS_PLL_REG_W(pll, QSERDES_COM_DEC_START_MODE0,
+ cfg.com_dec_start_mode0);
+ MDSS_PLL_REG_W(pll, QSERDES_COM_DIV_FRAC_START1_MODE0,
+ cfg.com_div_frac_start1_mode0);
+ MDSS_PLL_REG_W(pll, QSERDES_COM_DIV_FRAC_START2_MODE0,
+ cfg.com_div_frac_start2_mode0);
+ MDSS_PLL_REG_W(pll, QSERDES_COM_DIV_FRAC_START3_MODE0,
+ cfg.com_div_frac_start3_mode0);
+
+ MDSS_PLL_REG_W(pll, QSERDES_COM_FREQ_UPDATE, 0x01);
+ MDSS_PLL_REG_W(pll, QSERDES_COM_FREQ_UPDATE, 0x00);
+
+ DEV_DBG("%s: updated to rate %ld\n", __func__, rate);
+end:
+ return rc;
+}
+
+static int hdmi_8996_vco_set_rate(struct clk *c, unsigned long rate, u32 ver)
+{
+ struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
+ struct mdss_pll_resources *io = vco->priv;
+ unsigned int set_power_dwn = 0;
+ bool atomic_update = false;
+ int rc, pll_lock_range;
+
+ rc = mdss_pll_resource_enable(io, true);
+ if (rc) {
+ DEV_ERR("pll resource can't be enabled\n");
+ return rc;
+ }
+
+ DEV_DBG("%s: rate %ld\n", __func__, rate);
+
+ if (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_C_READY_STATUS) & BIT(0) &&
+ MDSS_PLL_REG_R(io->phy_base, HDMI_PHY_STATUS) & BIT(0)) {
+ pll_lock_range = hdmi_8996_vco_get_lock_range(c, vco->rate);
+
+ if (pll_lock_range > 0 && vco->rate) {
+ u32 range_limit;
+
+ range_limit = vco->rate *
+ (pll_lock_range / HDMI_KHZ_TO_HZ);
+ range_limit /= HDMI_KHZ_TO_HZ;
+
+ DEV_DBG("%s: range limit %d\n", __func__, range_limit);
+
+ if (abs(rate - vco->rate) < range_limit)
+ atomic_update = true;
+ }
+ }
+
+ if (io->pll_on && !atomic_update)
+ set_power_dwn = 1;
+
+ if (atomic_update) {
+ hdmi_8996_vco_rate_atomic_update(c, rate, ver);
+ } else {
+ rc = hdmi_8996_phy_pll_set_clk_rate(c, rate, ver);
+ if (rc)
+ DEV_ERR("%s: Failed to set clk rate\n", __func__);
+ }
+
+ mdss_pll_resource_enable(io, false);
+
+ if (set_power_dwn)
+ hdmi_8996_vco_enable(c, ver);
+
+ vco->rate = rate;
+ vco->rate_set = true;
+
+ return 0;
+}
+
+static int hdmi_8996_v1_vco_set_rate(struct clk *c, unsigned long rate)
+{
+ return hdmi_8996_vco_set_rate(c, rate, HDMI_VERSION_8996_V1);
+}
+
+static int hdmi_8996_v2_vco_set_rate(struct clk *c, unsigned long rate)
+{
+ return hdmi_8996_vco_set_rate(c, rate, HDMI_VERSION_8996_V2);
+}
+
+static int hdmi_8996_v3_vco_set_rate(struct clk *c, unsigned long rate)
+{
+ return hdmi_8996_vco_set_rate(c, rate, HDMI_VERSION_8996_V3);
+}
+
+static int hdmi_8996_v3_1p8_vco_set_rate(struct clk *c, unsigned long rate)
+{
+ return hdmi_8996_vco_set_rate(c, rate, HDMI_VERSION_8996_V3_1_8);
+}
+
+static unsigned long hdmi_get_hsclk_sel_divisor(unsigned long hsclk_sel)
+{
+ unsigned long divisor;
+
+ switch (hsclk_sel) {
+ case 0:
+ divisor = 2;
+ break;
+ case 1:
+ divisor = 6;
+ break;
+ case 2:
+ divisor = 10;
+ break;
+ case 3:
+ divisor = 14;
+ break;
+ case 4:
+ divisor = 3;
+ break;
+ case 5:
+ divisor = 9;
+ break;
+ case 6:
+ case 13:
+ divisor = 15;
+ break;
+ case 7:
+ divisor = 21;
+ break;
+ case 8:
+ divisor = 4;
+ break;
+ case 9:
+ divisor = 12;
+ break;
+ case 10:
+ divisor = 20;
+ break;
+ case 11:
+ divisor = 28;
+ break;
+ case 12:
+ divisor = 5;
+ break;
+ case 14:
+ divisor = 25;
+ break;
+ case 15:
+ divisor = 35;
+ break;
+ default:
+ divisor = 1;
+ DEV_ERR("%s: invalid hsclk_sel value = %lu",
+ __func__, hsclk_sel);
+ break;
+ }
+
+ return divisor;
+}
+
+static unsigned long hdmi_8996_vco_get_rate(struct clk *c)
+{
+ unsigned long freq = 0, hsclk_sel = 0, tx_band = 0, dec_start = 0,
+ div_frac_start = 0, vco_clock_freq = 0;
+ struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
+ struct mdss_pll_resources *io = vco->priv;
+
+ if (mdss_pll_resource_enable(io, true)) {
+ DEV_ERR("%s: pll resource can't be enabled\n", __func__);
+ return freq;
+ }
+
+ dec_start = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEC_START_MODE0);
+
+ div_frac_start =
+ MDSS_PLL_REG_R(io->pll_base,
+ QSERDES_COM_DIV_FRAC_START1_MODE0) |
+ MDSS_PLL_REG_R(io->pll_base,
+ QSERDES_COM_DIV_FRAC_START2_MODE0) << 8 |
+ MDSS_PLL_REG_R(io->pll_base,
+ QSERDES_COM_DIV_FRAC_START3_MODE0) << 16;
+
+ vco_clock_freq = (dec_start + (div_frac_start / (1 << 20)))
+ * 4 * (HDMI_REF_CLOCK);
+
+ hsclk_sel = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_HSCLK_SEL) & 0x15;
+ hsclk_sel = hdmi_get_hsclk_sel_divisor(hsclk_sel);
+ tx_band = MDSS_PLL_REG_R(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_TX_BAND) & 0x3;
+
+ freq = vco_clock_freq / (10 * hsclk_sel * (1 << tx_band));
+
+ mdss_pll_resource_enable(io, false);
+
+ DEV_DBG("%s: freq = %lu\n", __func__, freq);
+
+ return freq;
+}
+
+static long hdmi_8996_vco_round_rate(struct clk *c, unsigned long rate)
+{
+ unsigned long rrate = rate;
+
+ DEV_DBG("rrate=%ld\n", rrate);
+
+ return rrate;
+}
+
+static int hdmi_8996_vco_prepare(struct clk *c, u32 ver)
+{
+ struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
+ struct mdss_pll_resources *io = vco->priv;
+ int ret = 0;
+
+ DEV_DBG("rate=%ld\n", vco->rate);
+
+ if (!vco->rate_set && vco->rate)
+ ret = hdmi_8996_vco_set_rate(c, vco->rate, ver);
+
+ if (!ret) {
+ ret = mdss_pll_resource_enable(io, true);
+ if (ret)
+ DEV_ERR("pll resource can't be enabled\n");
+ }
+
+ return ret;
+}
+
+static int hdmi_8996_v1_vco_prepare(struct clk *c)
+{
+ return hdmi_8996_vco_prepare(c, HDMI_VERSION_8996_V1);
+}
+
+static int hdmi_8996_v2_vco_prepare(struct clk *c)
+{
+ return hdmi_8996_vco_prepare(c, HDMI_VERSION_8996_V2);
+}
+
+static int hdmi_8996_v3_vco_prepare(struct clk *c)
+{
+ return hdmi_8996_vco_prepare(c, HDMI_VERSION_8996_V3);
+}
+
+static int hdmi_8996_v3_1p8_vco_prepare(struct clk *c)
+{
+ return hdmi_8996_vco_prepare(c, HDMI_VERSION_8996_V3_1_8);
+}
+
+static void hdmi_8996_vco_unprepare(struct clk *c)
+{
+ struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
+ struct mdss_pll_resources *io = vco->priv;
+
+ vco->rate_set = false;
+
+ if (!io) {
+ DEV_ERR("Invalid input parameter\n");
+ return;
+ }
+
+ if (!io->pll_on &&
+ mdss_pll_resource_enable(io, true)) {
+ DEV_ERR("pll resource can't be enabled\n");
+ return;
+ }
+
+ io->handoff_resources = false;
+ mdss_pll_resource_enable(io, false);
+ io->pll_on = false;
+}
+
+static enum handoff hdmi_8996_vco_handoff(struct clk *c)
+{
+ enum handoff ret = HANDOFF_DISABLED_CLK;
+ struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
+ struct mdss_pll_resources *io = vco->priv;
+
+ if (is_gdsc_disabled(io))
+ return HANDOFF_DISABLED_CLK;
+
+ if (mdss_pll_resource_enable(io, true)) {
+ DEV_ERR("pll resource can't be enabled\n");
+ return ret;
+ }
+
+ io->handoff_resources = true;
+
+ if (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_C_READY_STATUS) & BIT(0)) {
+ if (MDSS_PLL_REG_R(io->phy_base, HDMI_PHY_STATUS) & BIT(0)) {
+ io->pll_on = true;
+ c->rate = hdmi_8996_vco_get_rate(c);
+ vco->rate = c->rate;
+ ret = HANDOFF_ENABLED_CLK;
+ } else {
+ io->handoff_resources = false;
+ mdss_pll_resource_enable(io, false);
+ DEV_DBG("%s: PHY not ready\n", __func__);
+ }
+ } else {
+ io->handoff_resources = false;
+ mdss_pll_resource_enable(io, false);
+ DEV_DBG("%s: PLL not locked\n", __func__);
+ }
+
+ DEV_DBG("done, ret=%d\n", ret);
+ return ret;
+}
+
+static struct clk_ops hdmi_8996_v1_vco_clk_ops = {
+ .enable = hdmi_8996_v1_vco_enable,
+ .set_rate = hdmi_8996_v1_vco_set_rate,
+ .get_rate = hdmi_8996_vco_get_rate,
+ .round_rate = hdmi_8996_vco_round_rate,
+ .prepare = hdmi_8996_v1_vco_prepare,
+ .unprepare = hdmi_8996_vco_unprepare,
+ .handoff = hdmi_8996_vco_handoff,
+};
+
+static struct clk_ops hdmi_8996_v2_vco_clk_ops = {
+ .enable = hdmi_8996_v2_vco_enable,
+ .set_rate = hdmi_8996_v2_vco_set_rate,
+ .get_rate = hdmi_8996_vco_get_rate,
+ .round_rate = hdmi_8996_vco_round_rate,
+ .prepare = hdmi_8996_v2_vco_prepare,
+ .unprepare = hdmi_8996_vco_unprepare,
+ .handoff = hdmi_8996_vco_handoff,
+};
+
+static struct clk_ops hdmi_8996_v3_vco_clk_ops = {
+ .enable = hdmi_8996_v3_vco_enable,
+ .set_rate = hdmi_8996_v3_vco_set_rate,
+ .get_rate = hdmi_8996_vco_get_rate,
+ .round_rate = hdmi_8996_vco_round_rate,
+ .prepare = hdmi_8996_v3_vco_prepare,
+ .unprepare = hdmi_8996_vco_unprepare,
+ .handoff = hdmi_8996_vco_handoff,
+};
+
+static struct clk_ops hdmi_8996_v3_1p8_vco_clk_ops = {
+ .enable = hdmi_8996_v3_1p8_vco_enable,
+ .set_rate = hdmi_8996_v3_1p8_vco_set_rate,
+ .get_rate = hdmi_8996_vco_get_rate,
+ .round_rate = hdmi_8996_vco_round_rate,
+ .prepare = hdmi_8996_v3_1p8_vco_prepare,
+ .unprepare = hdmi_8996_vco_unprepare,
+ .handoff = hdmi_8996_vco_handoff,
+};
+
+
+static struct hdmi_pll_vco_clk hdmi_vco_clk = {
+ .c = {
+ .dbg_name = "hdmi_8996_vco_clk",
+ .ops = &hdmi_8996_v1_vco_clk_ops,
+ CLK_INIT(hdmi_vco_clk.c),
+ },
+};
+
+static struct clk_lookup hdmipllcc_8996[] = {
+ CLK_LIST(hdmi_vco_clk),
+};
+
+int hdmi_8996_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res, u32 ver)
+{
+ int rc = -ENOTSUPP;
+
+ if (!pll_res || !pll_res->phy_base || !pll_res->pll_base) {
+ DEV_ERR("%s: Invalid input parameters\n", __func__);
+ return -EPROBE_DEFER;
+ }
+
+ /* Set client data for vco, mux and div clocks */
+ hdmi_vco_clk.priv = pll_res;
+
+ switch (ver) {
+ case HDMI_VERSION_8996_V2:
+ hdmi_vco_clk.c.ops = &hdmi_8996_v2_vco_clk_ops;
+ break;
+ case HDMI_VERSION_8996_V3:
+ hdmi_vco_clk.c.ops = &hdmi_8996_v3_vco_clk_ops;
+ break;
+ case HDMI_VERSION_8996_V3_1_8:
+ hdmi_vco_clk.c.ops = &hdmi_8996_v3_1p8_vco_clk_ops;
+ break;
+ default:
+ hdmi_vco_clk.c.ops = &hdmi_8996_v1_vco_clk_ops;
+ break;
+ };
+
+ rc = of_msm_clock_register(pdev->dev.of_node, hdmipllcc_8996,
+ ARRAY_SIZE(hdmipllcc_8996));
+ if (rc) {
+ DEV_ERR("%s: Clock register failed rc=%d\n", __func__, rc);
+ rc = -EPROBE_DEFER;
+ } else {
+ DEV_DBG("%s SUCCESS\n", __func__);
+ }
+
+ return rc;
+}
+
+int hdmi_8996_v1_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ return hdmi_8996_pll_clock_register(pdev, pll_res,
+ HDMI_VERSION_8996_V1);
+}
+
+int hdmi_8996_v2_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ return hdmi_8996_pll_clock_register(pdev, pll_res,
+ HDMI_VERSION_8996_V2);
+}
+
+int hdmi_8996_v3_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ return hdmi_8996_pll_clock_register(pdev, pll_res,
+ HDMI_VERSION_8996_V3);
+}
+
+int hdmi_8996_v3_1p8_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ return hdmi_8996_pll_clock_register(pdev, pll_res,
+ HDMI_VERSION_8996_V3_1_8);
+}
diff --git a/drivers/clk/qcom/mdss/mdss-hdmi-pll.h b/drivers/clk/qcom/mdss/mdss-hdmi-pll.h
new file mode 100644
index 000000000000..d4226bf43e13
--- /dev/null
+++ b/drivers/clk/qcom/mdss/mdss-hdmi-pll.h
@@ -0,0 +1,61 @@
+/* Copyright (c) 2012-2016, 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_HDMI_PLL_H
+#define __MDSS_HDMI_PLL_H
+
+struct hdmi_pll_cfg {
+ unsigned long vco_rate;
+ u32 reg;
+};
+
+struct hdmi_pll_vco_clk {
+ unsigned long rate; /* current vco rate */
+ unsigned long min_rate; /* min vco rate */
+ unsigned long max_rate; /* max vco rate */
+ bool rate_set;
+ struct hdmi_pll_cfg *ip_seti;
+ struct hdmi_pll_cfg *cp_seti;
+ struct hdmi_pll_cfg *ip_setp;
+ struct hdmi_pll_cfg *cp_setp;
+ struct hdmi_pll_cfg *crctrl;
+ void *priv;
+
+ struct clk c;
+};
+
+static inline struct hdmi_pll_vco_clk *to_hdmi_vco_clk(struct clk *clk)
+{
+ return container_of(clk, struct hdmi_pll_vco_clk, c);
+}
+
+int hdmi_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+
+int hdmi_20nm_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+
+int hdmi_8996_v1_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+
+int hdmi_8996_v2_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+
+int hdmi_8996_v3_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+
+int hdmi_8996_v3_1p8_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+
+int hdmi_cobalt_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+#endif
diff --git a/drivers/clk/qcom/mdss/mdss-pll-util.c b/drivers/clk/qcom/mdss/mdss-pll-util.c
new file mode 100644
index 000000000000..690c53f30eb7
--- /dev/null
+++ b/drivers/clk/qcom/mdss/mdss-pll-util.c
@@ -0,0 +1,438 @@
+/* Copyright (c) 2013-2016, 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/err.h>
+#include <linux/string.h>
+#include <linux/clk/msm-clock-generic.h>
+#include <linux/of_address.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/memblock.h>
+
+#include "mdss-pll.h"
+
+int mdss_pll_util_resource_init(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ int rc = 0;
+ struct dss_module_power *mp = &pll_res->mp;
+
+ rc = msm_dss_config_vreg(&pdev->dev,
+ mp->vreg_config, mp->num_vreg, 1);
+ if (rc) {
+ pr_err("Vreg config failed rc=%d\n", rc);
+ goto vreg_err;
+ }
+
+ rc = msm_dss_get_clk(&pdev->dev, mp->clk_config, mp->num_clk);
+ if (rc) {
+ pr_err("Clock get failed rc=%d\n", rc);
+ goto clk_err;
+ }
+
+ return rc;
+
+clk_err:
+ msm_dss_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg, 0);
+vreg_err:
+ return rc;
+}
+
+/**
+ * mdss_pll_get_mp_by_reg_name() -- Find power module by regulator name
+ *@pll_res: Pointer to the PLL resource
+ *@name: Regulator name as specified in the pll dtsi
+ *
+ * This is a helper function to retrieve the regulator information
+ * for each pll resource.
+ */
+struct dss_vreg *mdss_pll_get_mp_by_reg_name(struct mdss_pll_resources *pll_res
+ , char *name)
+{
+
+ struct dss_vreg *regulator = NULL;
+ int i;
+
+ if ((pll_res == NULL) || (pll_res->mp.vreg_config == NULL)) {
+ pr_err("%s Invalid PLL resource\n", __func__);
+ goto error;
+ }
+
+ regulator = pll_res->mp.vreg_config;
+
+ for (i = 0; i < pll_res->mp.num_vreg; i++) {
+ if (!strcmp(name, regulator->vreg_name)) {
+ pr_debug("Found regulator match for %s\n", name);
+ break;
+ }
+ regulator++;
+ }
+
+error:
+ return regulator;
+}
+
+void mdss_pll_util_resource_deinit(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ struct dss_module_power *mp = &pll_res->mp;
+
+ msm_dss_put_clk(mp->clk_config, mp->num_clk);
+
+ msm_dss_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg, 0);
+}
+
+void mdss_pll_util_resource_release(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ struct dss_module_power *mp = &pll_res->mp;
+
+ devm_kfree(&pdev->dev, mp->clk_config);
+ devm_kfree(&pdev->dev, mp->vreg_config);
+ mp->num_vreg = 0;
+ mp->num_clk = 0;
+}
+
+int mdss_pll_util_resource_enable(struct mdss_pll_resources *pll_res,
+ bool enable)
+{
+ int rc = 0;
+ struct dss_module_power *mp = &pll_res->mp;
+
+ if (enable) {
+ rc = msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, enable);
+ if (rc) {
+ pr_err("Failed to enable vregs rc=%d\n", rc);
+ goto vreg_err;
+ }
+
+ rc = msm_dss_clk_set_rate(mp->clk_config, mp->num_clk);
+ if (rc) {
+ pr_err("Failed to set clock rate rc=%d\n", rc);
+ goto clk_err;
+ }
+
+ rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, enable);
+ if (rc) {
+ pr_err("clock enable failed rc:%d\n", rc);
+ goto clk_err;
+ }
+ } else {
+ msm_dss_enable_clk(mp->clk_config, mp->num_clk, enable);
+
+ msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, enable);
+ }
+
+ return rc;
+
+clk_err:
+ msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, 0);
+vreg_err:
+ return rc;
+}
+
+static int mdss_pll_util_parse_dt_supply(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ int i = 0, rc = 0;
+ u32 tmp = 0;
+ struct device_node *of_node = NULL, *supply_root_node = NULL;
+ struct device_node *supply_node = NULL;
+ struct dss_module_power *mp = &pll_res->mp;
+
+ of_node = pdev->dev.of_node;
+
+ mp->num_vreg = 0;
+ supply_root_node = of_get_child_by_name(of_node,
+ "qcom,platform-supply-entries");
+ if (!supply_root_node) {
+ pr_err("no supply entry present\n");
+ return rc;
+ }
+
+ for_each_child_of_node(supply_root_node, supply_node) {
+ mp->num_vreg++;
+ }
+
+ if (mp->num_vreg == 0) {
+ pr_debug("no vreg\n");
+ return rc;
+ }
+ pr_debug("vreg found. count=%d\n", mp->num_vreg);
+
+ mp->vreg_config = devm_kzalloc(&pdev->dev, sizeof(struct dss_vreg) *
+ mp->num_vreg, GFP_KERNEL);
+ if (!mp->vreg_config) {
+ rc = -ENOMEM;
+ return rc;
+ }
+
+ for_each_child_of_node(supply_root_node, supply_node) {
+
+ const char *st = NULL;
+
+ rc = of_property_read_string(supply_node,
+ "qcom,supply-name", &st);
+ if (rc) {
+ pr_err(":error reading name. rc=%d\n", rc);
+ goto error;
+ }
+
+ strlcpy(mp->vreg_config[i].vreg_name, st,
+ sizeof(mp->vreg_config[i].vreg_name));
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-min-voltage", &tmp);
+ if (rc) {
+ pr_err(": error reading min volt. rc=%d\n", rc);
+ goto error;
+ }
+ mp->vreg_config[i].min_voltage = tmp;
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-max-voltage", &tmp);
+ if (rc) {
+ pr_err(": error reading max volt. rc=%d\n", rc);
+ goto error;
+ }
+ mp->vreg_config[i].max_voltage = tmp;
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-enable-load", &tmp);
+ if (rc) {
+ pr_err(": error reading enable load. rc=%d\n", rc);
+ goto error;
+ }
+ mp->vreg_config[i].enable_load = tmp;
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-disable-load", &tmp);
+ if (rc) {
+ pr_err(": error reading disable load. rc=%d\n", rc);
+ goto error;
+ }
+ mp->vreg_config[i].disable_load = tmp;
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-pre-on-sleep", &tmp);
+ if (rc)
+ pr_debug("error reading supply pre sleep value. rc=%d\n",
+ rc);
+
+ mp->vreg_config[i].pre_on_sleep = (!rc ? tmp : 0);
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-pre-off-sleep", &tmp);
+ if (rc)
+ pr_debug("error reading supply pre sleep value. rc=%d\n",
+ rc);
+
+ mp->vreg_config[i].pre_off_sleep = (!rc ? tmp : 0);
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-post-on-sleep", &tmp);
+ if (rc)
+ pr_debug("error reading supply post sleep value. rc=%d\n",
+ rc);
+
+ mp->vreg_config[i].post_on_sleep = (!rc ? tmp : 0);
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-post-off-sleep", &tmp);
+ if (rc)
+ pr_debug("error reading supply post sleep value. rc=%d\n",
+ rc);
+
+ mp->vreg_config[i].post_off_sleep = (!rc ? tmp : 0);
+
+ pr_debug("%s min=%d, max=%d, enable=%d, disable=%d, preonsleep=%d, postonsleep=%d, preoffsleep=%d, postoffsleep=%d\n",
+ mp->vreg_config[i].vreg_name,
+ mp->vreg_config[i].min_voltage,
+ mp->vreg_config[i].max_voltage,
+ mp->vreg_config[i].enable_load,
+ mp->vreg_config[i].disable_load,
+ mp->vreg_config[i].pre_on_sleep,
+ mp->vreg_config[i].post_on_sleep,
+ mp->vreg_config[i].pre_off_sleep,
+ mp->vreg_config[i].post_off_sleep);
+ ++i;
+
+ rc = 0;
+ }
+
+ return rc;
+
+error:
+ if (mp->vreg_config) {
+ devm_kfree(&pdev->dev, mp->vreg_config);
+ mp->vreg_config = NULL;
+ mp->num_vreg = 0;
+ }
+
+ return rc;
+}
+
+static int mdss_pll_util_parse_dt_clock(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ u32 i = 0, rc = 0;
+ struct dss_module_power *mp = &pll_res->mp;
+ const char *clock_name;
+ u32 clock_rate;
+
+ mp->num_clk = of_property_count_strings(pdev->dev.of_node,
+ "clock-names");
+ if (mp->num_clk <= 0) {
+ pr_err("clocks are not defined\n");
+ goto clk_err;
+ }
+
+ mp->clk_config = devm_kzalloc(&pdev->dev,
+ sizeof(struct dss_clk) * mp->num_clk, GFP_KERNEL);
+ if (!mp->clk_config) {
+ rc = -ENOMEM;
+ mp->num_clk = 0;
+ goto clk_err;
+ }
+
+ for (i = 0; i < mp->num_clk; i++) {
+ of_property_read_string_index(pdev->dev.of_node, "clock-names",
+ i, &clock_name);
+ strlcpy(mp->clk_config[i].clk_name, clock_name,
+ sizeof(mp->clk_config[i].clk_name));
+
+ of_property_read_u32_index(pdev->dev.of_node, "clock-rate",
+ i, &clock_rate);
+ mp->clk_config[i].rate = clock_rate;
+
+ if (!clock_rate)
+ mp->clk_config[i].type = DSS_CLK_AHB;
+ else
+ mp->clk_config[i].type = DSS_CLK_PCLK;
+ }
+
+clk_err:
+ return rc;
+}
+
+static void mdss_pll_free_bootmem(u32 mem_addr, u32 size)
+{
+ unsigned long pfn_start, pfn_end, pfn_idx;
+
+ pfn_start = mem_addr >> PAGE_SHIFT;
+ pfn_end = (mem_addr + size) >> PAGE_SHIFT;
+ for (pfn_idx = pfn_start; pfn_idx < pfn_end; pfn_idx++)
+ free_reserved_page(pfn_to_page(pfn_idx));
+}
+
+static int mdss_pll_util_parse_dt_dfps(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ int rc = 0;
+ struct device_node *pnode;
+ const u32 *addr;
+ struct vm_struct *area;
+ u64 size;
+ u32 offsets[2];
+ unsigned long virt_add;
+
+ pnode = of_parse_phandle(pdev->dev.of_node, "memory-region", 0);
+ if (IS_ERR_OR_NULL(pnode)) {
+ rc = PTR_ERR(pnode);
+ goto pnode_err;
+ }
+
+ addr = of_get_address(pnode, 0, &size, NULL);
+ if (!addr) {
+ pr_err("failed to parse the dfps memory address\n");
+ rc = -EINVAL;
+ goto pnode_err;
+ }
+ /* maintain compatibility for 32/64 bit */
+ offsets[0] = (u32) of_read_ulong(addr, 2);
+ offsets[1] = (u32) size;
+
+ area = get_vm_area(offsets[1], VM_IOREMAP);
+ if (!area) {
+ rc = -ENOMEM;
+ goto dfps_mem_err;
+ }
+
+ virt_add = (unsigned long)area->addr;
+ rc = ioremap_page_range(virt_add, (virt_add + offsets[1]),
+ offsets[0], PAGE_KERNEL);
+ if (rc) {
+ rc = -ENOMEM;
+ goto ioremap_err;
+ }
+
+ pll_res->dfps = kzalloc(sizeof(struct dfps_info), GFP_KERNEL);
+ if (IS_ERR_OR_NULL(pll_res->dfps)) {
+ rc = PTR_ERR(pll_res->dfps);
+ pr_err("couldn't allocate dfps kernel memory\n");
+ goto addr_err;
+ }
+
+ /* memcopy complete dfps structure from kernel virtual memory */
+ memcpy_fromio(pll_res->dfps, area->addr, sizeof(struct dfps_info));
+
+addr_err:
+ if (virt_add)
+ unmap_kernel_range(virt_add, (unsigned long) size);
+ioremap_err:
+ if (area)
+ vfree(area->addr);
+dfps_mem_err:
+ /* free the dfps memory here */
+ memblock_free(offsets[0], offsets[1]);
+ mdss_pll_free_bootmem(offsets[0], offsets[1]);
+pnode_err:
+ if (pnode)
+ of_node_put(pnode);
+
+ dma_release_declared_memory(&pdev->dev);
+ return rc;
+}
+
+int mdss_pll_util_resource_parse(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ int rc = 0;
+ struct dss_module_power *mp = &pll_res->mp;
+
+ rc = mdss_pll_util_parse_dt_supply(pdev, pll_res);
+ if (rc) {
+ pr_err("vreg parsing failed rc=%d\n", rc);
+ goto end;
+ }
+
+ rc = mdss_pll_util_parse_dt_clock(pdev, pll_res);
+ if (rc) {
+ pr_err("clock name parsing failed rc=%d", rc);
+ goto clk_err;
+ }
+
+ if (mdss_pll_util_parse_dt_dfps(pdev, pll_res))
+ pr_err("dfps not enabled!\n");
+
+ return rc;
+
+clk_err:
+ devm_kfree(&pdev->dev, mp->vreg_config);
+ mp->num_vreg = 0;
+end:
+ return rc;
+}
diff --git a/drivers/clk/qcom/mdss/mdss-pll.c b/drivers/clk/qcom/mdss/mdss-pll.c
new file mode 100644
index 000000000000..e91e9c9dc768
--- /dev/null
+++ b/drivers/clk/qcom/mdss/mdss-pll.c
@@ -0,0 +1,437 @@
+/* Copyright (c) 2013-2016, 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/of_device.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/clk/msm-clock-generic.h>
+
+#include "mdss-pll.h"
+#include "mdss-dsi-pll.h"
+#include "mdss-hdmi-pll.h"
+#include "mdss-dp-pll.h"
+
+int mdss_pll_resource_enable(struct mdss_pll_resources *pll_res, bool enable)
+{
+ int rc = 0;
+ int changed = 0;
+
+ if (!pll_res) {
+ pr_err("Invalid input parameters\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Don't turn off resources during handoff or add more than
+ * 1 refcount.
+ */
+ if (pll_res->handoff_resources &&
+ (!enable || (enable & pll_res->resource_enable))) {
+ pr_debug("Do not turn on/off pll resources during handoff case\n");
+ return rc;
+ }
+
+ if (enable) {
+ if (pll_res->resource_ref_cnt == 0)
+ changed++;
+ pll_res->resource_ref_cnt++;
+ } else {
+ if (pll_res->resource_ref_cnt) {
+ pll_res->resource_ref_cnt--;
+ if (pll_res->resource_ref_cnt == 0)
+ changed++;
+ } else {
+ pr_err("PLL Resources already OFF\n");
+ }
+ }
+
+ if (changed) {
+ rc = mdss_pll_util_resource_enable(pll_res, enable);
+ if (rc)
+ pr_err("Resource update failed rc=%d\n", rc);
+ else
+ pll_res->resource_enable = enable;
+ }
+
+ return rc;
+}
+
+static int mdss_pll_resource_init(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ if (!pdev || !pll_res) {
+ pr_err("Invalid input parameters\n");
+ return -EINVAL;
+ }
+
+ return mdss_pll_util_resource_init(pdev, pll_res);
+}
+
+static void mdss_pll_resource_deinit(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ if (!pdev || !pll_res) {
+ pr_err("Invalid input parameters\n");
+ return;
+ }
+
+ mdss_pll_util_resource_deinit(pdev, pll_res);
+}
+
+static void mdss_pll_resource_release(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ if (!pdev || !pll_res) {
+ pr_err("Invalid input parameters\n");
+ return;
+ }
+
+ mdss_pll_util_resource_release(pdev, pll_res);
+}
+
+static int mdss_pll_resource_parse(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ int rc = 0;
+ const char *compatible_stream;
+
+ if (!pdev || !pll_res) {
+ pr_err("Invalid input parameters\n");
+ return -EINVAL;
+ }
+
+ rc = mdss_pll_util_resource_parse(pdev, pll_res);
+ if (rc) {
+ pr_err("Failed to parse the resources rc=%d\n", rc);
+ goto end;
+ }
+
+ compatible_stream = of_get_property(pdev->dev.of_node,
+ "compatible", NULL);
+ if (!compatible_stream) {
+ pr_err("Failed to parse the compatible stream\n");
+ goto err;
+ }
+
+ if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_8996")) {
+ pll_res->pll_interface_type = MDSS_DSI_PLL_8996;
+ pll_res->target_id = MDSS_PLL_TARGET_8996;
+ pll_res->revision = 1;
+ } else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_8996_v2")) {
+ pll_res->pll_interface_type = MDSS_DSI_PLL_8996;
+ pll_res->target_id = MDSS_PLL_TARGET_8996;
+ pll_res->revision = 2;
+ } else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_cobalt")) {
+ pll_res->pll_interface_type = MDSS_DSI_PLL_COBALT;
+ } else if (!strcmp(compatible_stream, "qcom,mdss_dp_pll_cobalt")) {
+ pll_res->pll_interface_type = MDSS_DP_PLL_COBALT;
+ } else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_8996")) {
+ pll_res->pll_interface_type = MDSS_HDMI_PLL_8996;
+ } else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_8996_v2")) {
+ pll_res->pll_interface_type = MDSS_HDMI_PLL_8996_V2;
+ } else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_8996_v3")) {
+ pll_res->pll_interface_type = MDSS_HDMI_PLL_8996_V3;
+ } else if (!strcmp(compatible_stream,
+ "qcom,mdss_hdmi_pll_8996_v3_1p8")) {
+ pll_res->pll_interface_type = MDSS_HDMI_PLL_8996_V3_1_8;
+ } else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_cobalt")) {
+ pll_res->pll_interface_type = MDSS_HDMI_PLL_COBALT;
+ } else {
+ goto err;
+ }
+
+ return rc;
+
+err:
+ mdss_pll_resource_release(pdev, pll_res);
+end:
+ return rc;
+}
+
+static int mdss_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ int rc;
+
+ if (!pdev || !pll_res) {
+ pr_err("Invalid input parameters\n");
+ return -EINVAL;
+ }
+
+ switch (pll_res->pll_interface_type) {
+ case MDSS_DSI_PLL_8996:
+ rc = dsi_pll_clock_register_8996(pdev, pll_res);
+ break;
+ case MDSS_DSI_PLL_COBALT:
+ rc = dsi_pll_clock_register_cobalt(pdev, pll_res);
+ case MDSS_DP_PLL_COBALT:
+ rc = dp_pll_clock_register_cobalt(pdev, pll_res);
+ break;
+ case MDSS_HDMI_PLL_8996:
+ rc = hdmi_8996_v1_pll_clock_register(pdev, pll_res);
+ break;
+ case MDSS_HDMI_PLL_8996_V2:
+ rc = hdmi_8996_v2_pll_clock_register(pdev, pll_res);
+ break;
+ case MDSS_HDMI_PLL_8996_V3:
+ rc = hdmi_8996_v3_pll_clock_register(pdev, pll_res);
+ break;
+ case MDSS_HDMI_PLL_8996_V3_1_8:
+ rc = hdmi_8996_v3_1p8_pll_clock_register(pdev, pll_res);
+ break;
+ case MDSS_HDMI_PLL_COBALT:
+ rc = hdmi_cobalt_pll_clock_register(pdev, pll_res);
+ break;
+ case MDSS_UNKNOWN_PLL:
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ if (rc) {
+ pr_err("Pll ndx=%d clock register failed rc=%d\n",
+ pll_res->index, rc);
+ }
+
+ return rc;
+}
+
+static int mdss_pll_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ const char *label;
+ struct resource *pll_base_reg;
+ struct resource *phy_base_reg;
+ struct resource *dynamic_pll_base_reg;
+ struct resource *gdsc_base_reg;
+ struct mdss_pll_resources *pll_res;
+
+ if (!pdev->dev.of_node) {
+ pr_err("MDSS pll driver only supports device tree probe\n");
+ rc = -ENOTSUPP;
+ goto error;
+ }
+
+ label = of_get_property(pdev->dev.of_node, "label", NULL);
+ if (!label)
+ pr_info("%d: MDSS pll label not specified\n", __LINE__);
+ else
+ pr_info("MDSS pll label = %s\n", label);
+
+ pll_res = devm_kzalloc(&pdev->dev, sizeof(struct mdss_pll_resources),
+ GFP_KERNEL);
+ if (!pll_res) {
+ rc = -ENOMEM;
+ goto error;
+ }
+ platform_set_drvdata(pdev, pll_res);
+
+ rc = of_property_read_u32(pdev->dev.of_node, "cell-index",
+ &pll_res->index);
+ if (rc) {
+ pr_err("Unable to get the cell-index rc=%d\n", rc);
+ pll_res->index = 0;
+ }
+
+ pll_res->ssc_en = of_property_read_bool(pdev->dev.of_node,
+ "qcom,dsi-pll-ssc-en");
+
+ if (pll_res->ssc_en) {
+ pr_info("%s: label=%s PLL SSC enabled\n", __func__, label);
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,ssc-frequency-hz", &pll_res->ssc_freq);
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,ssc-ppm", &pll_res->ssc_ppm);
+
+ pll_res->ssc_center = false;
+
+ label = of_get_property(pdev->dev.of_node,
+ "qcom,dsi-pll-ssc-mode", NULL);
+
+ if (label && !strcmp(label, "center-spread"))
+ pll_res->ssc_center = true;
+ }
+
+ pll_base_reg = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "pll_base");
+ if (!pll_base_reg) {
+ pr_err("Unable to get the pll base resources\n");
+ rc = -ENOMEM;
+ goto io_error;
+ }
+
+ pll_res->pll_base = ioremap(pll_base_reg->start,
+ resource_size(pll_base_reg));
+ if (!pll_res->pll_base) {
+ pr_err("Unable to remap pll base resources\n");
+ rc = -ENOMEM;
+ goto io_error;
+ }
+
+ pr_debug("%s: ndx=%d base=%p\n", __func__,
+ pll_res->index, pll_res->pll_base);
+
+ rc = mdss_pll_resource_parse(pdev, pll_res);
+ if (rc) {
+ pr_err("Pll resource parsing from dt failed rc=%d\n", rc);
+ goto res_parse_error;
+ }
+
+ phy_base_reg = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "phy_base");
+ if (phy_base_reg) {
+ pll_res->phy_base = ioremap(phy_base_reg->start,
+ resource_size(phy_base_reg));
+ if (!pll_res->phy_base) {
+ pr_err("Unable to remap pll phy base resources\n");
+ rc = -ENOMEM;
+ goto phy_io_error;
+ }
+ }
+
+ dynamic_pll_base_reg = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "dynamic_pll_base");
+ if (dynamic_pll_base_reg) {
+ pll_res->dyn_pll_base = ioremap(dynamic_pll_base_reg->start,
+ resource_size(dynamic_pll_base_reg));
+ if (!pll_res->dyn_pll_base) {
+ pr_err("Unable to remap dynamic pll base resources\n");
+ rc = -ENOMEM;
+ goto dyn_pll_io_error;
+ }
+ }
+
+ gdsc_base_reg = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "gdsc_base");
+ if (!gdsc_base_reg) {
+ pr_err("Unable to get the gdsc base resource\n");
+ rc = -ENOMEM;
+ goto gdsc_io_error;
+ }
+ pll_res->gdsc_base = ioremap(gdsc_base_reg->start,
+ resource_size(gdsc_base_reg));
+ if (!pll_res->gdsc_base) {
+ pr_err("Unable to remap gdsc base resources\n");
+ rc = -ENOMEM;
+ goto gdsc_io_error;
+ }
+
+ rc = mdss_pll_resource_init(pdev, pll_res);
+ if (rc) {
+ pr_err("Pll ndx=%d resource init failed rc=%d\n",
+ pll_res->index, rc);
+ goto res_init_error;
+ }
+
+ rc = mdss_pll_clock_register(pdev, pll_res);
+ if (rc) {
+ pr_err("Pll ndx=%d clock register failed rc=%d\n",
+ pll_res->index, rc);
+ goto clock_register_error;
+ }
+
+ return rc;
+
+clock_register_error:
+ mdss_pll_resource_deinit(pdev, pll_res);
+res_init_error:
+ if (pll_res->gdsc_base)
+ iounmap(pll_res->gdsc_base);
+gdsc_io_error:
+ if (pll_res->dyn_pll_base)
+ iounmap(pll_res->dyn_pll_base);
+dyn_pll_io_error:
+ if (pll_res->phy_base)
+ iounmap(pll_res->phy_base);
+phy_io_error:
+ mdss_pll_resource_release(pdev, pll_res);
+res_parse_error:
+ iounmap(pll_res->pll_base);
+io_error:
+ devm_kfree(&pdev->dev, pll_res);
+error:
+ return rc;
+}
+
+static int mdss_pll_remove(struct platform_device *pdev)
+{
+ struct mdss_pll_resources *pll_res;
+
+ pll_res = platform_get_drvdata(pdev);
+ if (!pll_res) {
+ pr_err("Invalid PLL resource data");
+ return 0;
+ }
+
+ mdss_pll_resource_deinit(pdev, pll_res);
+ if (pll_res->phy_base)
+ iounmap(pll_res->phy_base);
+ if (pll_res->gdsc_base)
+ iounmap(pll_res->gdsc_base);
+ mdss_pll_resource_release(pdev, pll_res);
+ iounmap(pll_res->pll_base);
+ devm_kfree(&pdev->dev, pll_res);
+ return 0;
+}
+
+static const struct of_device_id mdss_pll_dt_match[] = {
+ {.compatible = "qcom,mdss_dsi_pll_8996"},
+ {.compatible = "qcom,mdss_dsi_pll_8996_v2"},
+ {.compatible = "qcom,mdss_dsi_pll_cobalt"},
+ {.compatible = "qcom,mdss_hdmi_pll_8996"},
+ {.compatible = "qcom,mdss_hdmi_pll_8996_v2"},
+ {.compatible = "qcom,mdss_hdmi_pll_8996_v3"},
+ {.compatible = "qcom,mdss_hdmi_pll_8996_v3_1p8"},
+ {.compatible = "qcom,mdss_dp_pll_cobalt"},
+ {.compatible = "qcom,mdss_hdmi_pll_cobalt"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, mdss_clock_dt_match);
+
+static struct platform_driver mdss_pll_driver = {
+ .probe = mdss_pll_probe,
+ .remove = mdss_pll_remove,
+ .driver = {
+ .name = "mdss_pll",
+ .of_match_table = mdss_pll_dt_match,
+ },
+};
+
+static int __init mdss_pll_driver_init(void)
+{
+ int rc;
+
+ rc = platform_driver_register(&mdss_pll_driver);
+ if (rc)
+ pr_err("mdss_register_pll_driver() failed!\n");
+
+ return rc;
+}
+subsys_initcall(mdss_pll_driver_init);
+
+static void __exit mdss_pll_driver_deinit(void)
+{
+ platform_driver_unregister(&mdss_pll_driver);
+}
+module_exit(mdss_pll_driver_deinit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("mdss pll driver");
diff --git a/drivers/clk/qcom/mdss/mdss-pll.h b/drivers/clk/qcom/mdss/mdss-pll.h
new file mode 100644
index 000000000000..a2eb03e09146
--- /dev/null
+++ b/drivers/clk/qcom/mdss/mdss-pll.h
@@ -0,0 +1,233 @@
+/* Copyright (c) 2013-2016, 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_PLL_H
+#define __MDSS_PLL_H
+
+#include <linux/mdss_io_util.h>
+#include <linux/clk/msm-clock-generic.h>
+#include <linux/io.h>
+
+#define MDSS_PLL_REG_W(base, offset, data) \
+ writel_relaxed((data), (base) + (offset))
+#define MDSS_PLL_REG_R(base, offset) readl_relaxed((base) + (offset))
+
+#define PLL_CALC_DATA(addr0, addr1, data0, data1) \
+ (((data1) << 24) | ((((addr1) / 4) & 0xFF) << 16) | \
+ ((data0) << 8) | (((addr0) / 4) & 0xFF))
+
+#define MDSS_DYN_PLL_REG_W(base, offset, addr0, addr1, data0, data1) \
+ writel_relaxed(PLL_CALC_DATA(addr0, addr1, data0, data1), \
+ (base) + (offset))
+
+enum {
+ MDSS_DSI_PLL_8996,
+ MDSS_DSI_PLL_COBALT,
+ MDSS_DP_PLL_COBALT,
+ MDSS_HDMI_PLL_8996,
+ MDSS_HDMI_PLL_8996_V2,
+ MDSS_HDMI_PLL_8996_V3,
+ MDSS_HDMI_PLL_8996_V3_1_8,
+ MDSS_HDMI_PLL_COBALT,
+ MDSS_UNKNOWN_PLL,
+};
+
+enum {
+ MDSS_PLL_TARGET_8996,
+};
+
+#define DFPS_MAX_NUM_OF_FRAME_RATES 20
+
+struct dfps_panel_info {
+ uint32_t enabled;
+ uint32_t frame_rate_cnt;
+ uint32_t frame_rate[DFPS_MAX_NUM_OF_FRAME_RATES]; /* hz */
+};
+
+struct dfps_pll_codes {
+ uint32_t pll_codes_1;
+ uint32_t pll_codes_2;
+};
+
+struct dfps_codes_info {
+ uint32_t is_valid;
+ uint32_t frame_rate; /* hz */
+ uint32_t clk_rate; /* hz */
+ struct dfps_pll_codes pll_codes;
+};
+
+struct dfps_info {
+ struct dfps_panel_info panel_dfps;
+ struct dfps_codes_info codes_dfps[DFPS_MAX_NUM_OF_FRAME_RATES];
+ void *dfps_fb_base;
+};
+
+struct mdss_pll_resources {
+
+ /* Pll specific resources like GPIO, power supply, clocks, etc*/
+ struct dss_module_power mp;
+
+ /*
+ * dsi/edp/hmdi plls' base register, phy, gdsc and dynamic refresh
+ * register mapping
+ */
+ void __iomem *pll_base;
+ void __iomem *phy_base;
+ void __iomem *gdsc_base;
+ void __iomem *dyn_pll_base;
+
+ bool is_init_locked;
+ s64 vco_current_rate;
+ s64 vco_locking_rate;
+ s64 vco_ref_clk_rate;
+
+ /*
+ * Certain pll's needs to update the same vco rate after resume in
+ * suspend/resume scenario. Cached the vco rate for such plls.
+ */
+ unsigned long vco_cached_rate;
+
+ /* dsi/edp/hmdi pll interface type */
+ u32 pll_interface_type;
+
+ /*
+ * Target ID. Used in pll_register API for valid target check before
+ * registering the PLL clocks.
+ */
+ u32 target_id;
+
+ /* HW recommended delay during configuration of vco clock rate */
+ u32 vco_delay;
+
+ /* Ref-count of the PLL resources */
+ u32 resource_ref_cnt;
+
+ /*
+ * Keep track to resource status to avoid updating same status for the
+ * pll from different paths
+ */
+ bool resource_enable;
+
+ /*
+ * Certain plls' do not allow vco rate update if it is on. Keep track of
+ * status for them to turn on/off after set rate success.
+ */
+ bool pll_on;
+
+ /*
+ * handoff_status is true of pll is already enabled by bootloader with
+ * continuous splash enable case. Clock API will call the handoff API
+ * to enable the status. It is disabled if continuous splash
+ * feature is disabled.
+ */
+ bool handoff_resources;
+
+ /*
+ * caching the pll trim codes in the case of dynamic refresh
+ */
+ int cache_pll_trim_codes[2];
+
+ /*
+ * for maintaining the status of saving trim codes
+ */
+ bool reg_upd;
+
+ /*
+ * Notifier callback for MDSS gdsc regulator events
+ */
+ struct notifier_block gdsc_cb;
+
+ /*
+ * Worker function to call PLL off event
+ */
+ struct work_struct pll_off;
+
+ /*
+ * PLL index if multiple index are available. Eg. in case of
+ * DSI we have 2 plls.
+ */
+ uint32_t index;
+
+ bool ssc_en; /* share pll with master */
+ bool ssc_center; /* default is down spread */
+ u32 ssc_freq;
+ u32 ssc_ppm;
+
+ struct mdss_pll_resources *slave;
+
+ /*
+ * target pll revision information
+ */
+ int revision;
+
+ void *priv;
+
+ /*
+ * dynamic refresh pll codes stored in this structure
+ */
+ struct dfps_info *dfps;
+
+};
+
+struct mdss_pll_vco_calc {
+ s32 div_frac_start1;
+ s32 div_frac_start2;
+ s32 div_frac_start3;
+ s64 dec_start1;
+ s64 dec_start2;
+ s64 pll_plllock_cmp1;
+ s64 pll_plllock_cmp2;
+ s64 pll_plllock_cmp3;
+};
+
+static inline bool is_gdsc_disabled(struct mdss_pll_resources *pll_res)
+{
+ if (!pll_res->gdsc_base) {
+ WARN(1, "gdsc_base register is not defined\n");
+ return true;
+ }
+
+ return ((readl_relaxed(pll_res->gdsc_base + 0x4) & BIT(31)) &&
+ (!(readl_relaxed(pll_res->gdsc_base) & BIT(0)))) ? false : true;
+}
+
+static inline int mdss_pll_div_prepare(struct clk *c)
+{
+ struct div_clk *div = to_div_clk(c);
+ /* Restore the divider's value */
+ return div->ops->set_div(div, div->data.div);
+}
+
+static inline int mdss_set_mux_sel(struct mux_clk *clk, int sel)
+{
+ return 0;
+}
+
+static inline int mdss_get_mux_sel(struct mux_clk *clk)
+{
+ return 0;
+}
+
+int mdss_pll_resource_enable(struct mdss_pll_resources *pll_res, bool enable);
+int mdss_pll_util_resource_init(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+void mdss_pll_util_resource_deinit(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+void mdss_pll_util_resource_release(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+int mdss_pll_util_resource_enable(struct mdss_pll_resources *pll_res,
+ bool enable);
+int mdss_pll_util_resource_parse(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+struct dss_vreg *mdss_pll_get_mp_by_reg_name(struct mdss_pll_resources *pll_res
+ , char *name);
+#endif
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 680d0b596970..386c85fc714b 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -372,18 +372,18 @@ config CRYPTO_DEV_QCRYPTO
config CRYPTO_DEV_QCOM_MSM_QCE
tristate "Qualcomm Crypto Engine (QCE) module"
- select CRYPTO_DEV_QCE50 if ARCH_APQ8084 || ARCH_MSM8916 || ARCH_MSM8994 || ARCH_MSM8996 || ARCH_MSM8992 || ARCH_MSMTITANIUM || ARCH_MSM8909 || ARCH_MSMCOBALT
+ select CRYPTO_DEV_QCE50 if ARCH_APQ8084 || ARCH_MSM8916 || ARCH_MSM8994 || ARCH_MSM8996 || ARCH_MSM8992 || ARCH_MSMTITANIUM || ARCH_MSM8909 || ARCH_MSMCOBALT || ARCH_MSMFALCON
default n
help
This driver supports Qualcomm Crypto Engine in MSM7x30, MSM8660
MSM8x55, MSM8960, MSM9615, MSM8916, MSM8994, MSM8996, FSM9900,
- MSMTITANINUM, APQ8084 and MSMCOBALT.
+ MSMTITANINUM, APQ8084, MSMCOBALT and MSMFALCON.
To compile this driver as a module, choose M here: the
For MSM7x30 MSM8660 and MSM8x55 the module is called qce
For MSM8960, APQ8064 and MSM9615 the module is called qce40
For MSM8974, MSM8916, MSM8994, MSM8996, MSM8992, MSMTITANIUM,
- APQ8084 and MSMCOBALT the module is called qce50.
+ APQ8084, MSMCOBALT and MSMFALCON the module is called qce50.
config CRYPTO_DEV_QCEDEV
tristate "QCEDEV Interface to CE module"
@@ -391,7 +391,7 @@ config CRYPTO_DEV_QCEDEV
help
This driver supports Qualcomm QCEDEV Crypto in MSM7x30, MSM8660,
MSM8960, MSM9615, APQ8064, MSM8974, MSM8916, MSM8994, MSM8996,
- APQ8084, MSMCOBALT. This exposes the interface to the QCE hardware
+ APQ8084, MSMCOBALT, MSMFALCON. This exposes the interface to the QCE hardware
accelerator via IOCTLs.
To compile this driver as a module, choose M here: the
diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c
index 6bcc08e94657..61f99370863d 100644
--- a/drivers/crypto/msm/qce50.c
+++ b/drivers/crypto/msm/qce50.c
@@ -1,6 +1,6 @@
/* Qualcomm Crypto Engine driver.
*
- * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2016, 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
@@ -69,7 +69,7 @@ static LIST_HEAD(qce50_bam_list);
/* Max number of request supported */
#define MAX_QCE_BAM_REQ 8
/* Interrupt flag will be set for every SET_INTR_AT_REQ request */
-#define SET_INTR_AT_REQ (MAX_QCE_BAM_REQ - 2)
+#define SET_INTR_AT_REQ (MAX_QCE_BAM_REQ / 2)
/* To create extra request space to hold dummy request */
#define MAX_QCE_BAM_REQ_WITH_DUMMY_REQ (MAX_QCE_BAM_REQ + 1)
/* Allocate the memory for MAX_QCE_BAM_REQ + 1 (for dummy request) */
@@ -84,6 +84,12 @@ static LIST_HEAD(qce50_bam_list);
/* Index to point the dummy request */
#define DUMMY_REQ_INDEX MAX_QCE_BAM_REQ
+enum qce_owner {
+ QCE_OWNER_NONE = 0,
+ QCE_OWNER_CLIENT = 1,
+ QCE_OWNER_TIMEOUT = 2
+};
+
struct dummy_request {
struct qce_sha_req sreq;
uint8_t *in_buf;
@@ -133,9 +139,8 @@ struct qce_device {
struct ce_bam_info ce_bam_info;
struct ce_request_info ce_request_info[MAX_QCE_ALLOC_BAM_REQ];
unsigned int ce_request_index;
- spinlock_t lock;
- spinlock_t sps_lock;
- unsigned int no_of_queued_req;
+ enum qce_owner owner;
+ atomic_t no_of_queued_req;
struct timer_list timer;
struct dummy_request dummyreq;
unsigned int mode;
@@ -144,6 +149,7 @@ struct qce_device {
struct qce_driver_stats qce_stats;
atomic_t bunch_cmd_seq;
atomic_t last_intr_seq;
+ bool cadence_flag;
};
static void print_notify_debug(struct sps_event_notify *notify);
@@ -2539,7 +2545,6 @@ static int _qce_sps_add_cmd(struct qce_device *pce_dev, uint32_t flag,
static int _qce_sps_transfer(struct qce_device *pce_dev, int req_info)
{
int rc = 0;
- unsigned long flags;
struct ce_sps_data *pce_sps_data;
pce_sps_data = &pce_dev->ce_request_info[req_info].ce_sps;
@@ -2551,7 +2556,6 @@ static int _qce_sps_transfer(struct qce_device *pce_dev, int req_info)
(unsigned int) req_info));
_qce_dump_descr_fifos_dbg(pce_dev, req_info);
- spin_lock_irqsave(&pce_dev->sps_lock, flags);
if (pce_sps_data->in_transfer.iovec_count) {
rc = sps_transfer(pce_dev->ce_bam_info.consumer.pipe,
&pce_sps_data->in_transfer);
@@ -2570,7 +2574,6 @@ static int _qce_sps_transfer(struct qce_device *pce_dev, int req_info)
ret:
if (rc)
_qce_dump_descr_fifos(pce_dev, req_info);
- spin_unlock_irqrestore(&pce_dev->sps_lock, flags);
return rc;
}
@@ -2955,23 +2958,20 @@ static inline int qce_alloc_req_info(struct qce_device *pce_dev)
}
}
pr_warn("pcedev %d no reqs available no_of_queued_req %d\n",
- pce_dev->dev_no, pce_dev->no_of_queued_req);
+ pce_dev->dev_no, atomic_read(
+ &pce_dev->no_of_queued_req));
return -EBUSY;
}
static inline void qce_free_req_info(struct qce_device *pce_dev, int req_info,
bool is_complete)
{
- unsigned long flags;
-
- spin_lock_irqsave(&pce_dev->lock, flags);
pce_dev->ce_request_info[req_info].xfer_type = QCE_XFER_TYPE_LAST;
if (xchg(&pce_dev->ce_request_info[req_info].in_use, false) == true) {
if (req_info < MAX_QCE_BAM_REQ && is_complete)
- pce_dev->no_of_queued_req--;
+ atomic_dec(&pce_dev->no_of_queued_req);
} else
pr_warn("request info %d free already\n", req_info);
- spin_unlock_irqrestore(&pce_dev->lock, flags);
}
static void print_notify_debug(struct sps_event_notify *notify)
@@ -3018,7 +3018,6 @@ static void qce_multireq_timeout(unsigned long data)
{
struct qce_device *pce_dev = (struct qce_device *)data;
int ret = 0;
- unsigned long flags;
int last_seq;
last_seq = atomic_read(&pce_dev->bunch_cmd_seq);
@@ -3029,27 +3028,29 @@ static void qce_multireq_timeout(unsigned long data)
return;
}
/* last bunch mode command time out */
- spin_lock_irqsave(&pce_dev->lock, flags);
+ if (cmpxchg(&pce_dev->owner, QCE_OWNER_NONE, QCE_OWNER_TIMEOUT)
+ != QCE_OWNER_NONE) {
+ mod_timer(&(pce_dev->timer), (jiffies + DELAY_IN_JIFFIES));
+ return;
+ }
del_timer(&(pce_dev->timer));
pce_dev->mode = IN_INTERRUPT_MODE;
pce_dev->qce_stats.no_of_timeouts++;
pr_debug("pcedev %d mode switch to INTR\n", pce_dev->dev_no);
- spin_unlock_irqrestore(&pce_dev->lock, flags);
ret = qce_dummy_req(pce_dev);
if (ret)
pr_warn("pcedev %d: Failed to insert dummy req\n",
pce_dev->dev_no);
+ cmpxchg(&pce_dev->owner, QCE_OWNER_TIMEOUT, QCE_OWNER_NONE);
}
void qce_get_driver_stats(void *handle)
{
- unsigned long flags;
struct qce_device *pce_dev = (struct qce_device *) handle;
if (!_qce50_disp_stats)
return;
- spin_lock_irqsave(&pce_dev->lock, flags);
pr_info("Engine %d timeout occuured %d\n", pce_dev->dev_no,
pce_dev->qce_stats.no_of_timeouts);
pr_info("Engine %d dummy request inserted %d\n", pce_dev->dev_no,
@@ -3059,20 +3060,16 @@ void qce_get_driver_stats(void *handle)
else
pr_info("Engine %d is in INTERRUPT MODE\n", pce_dev->dev_no);
pr_info("Engine %d outstanding request %d\n", pce_dev->dev_no,
- pce_dev->no_of_queued_req);
- spin_unlock_irqrestore(&pce_dev->lock, flags);
+ atomic_read(&pce_dev->no_of_queued_req));
}
EXPORT_SYMBOL(qce_get_driver_stats);
void qce_clear_driver_stats(void *handle)
{
- unsigned long flags;
struct qce_device *pce_dev = (struct qce_device *) handle;
- spin_lock_irqsave(&pce_dev->lock, flags);
pce_dev->qce_stats.no_of_timeouts = 0;
pce_dev->qce_stats.no_of_dummy_reqs = 0;
- spin_unlock_irqrestore(&pce_dev->lock, flags);
}
EXPORT_SYMBOL(qce_clear_driver_stats);
@@ -3084,7 +3081,6 @@ static void _sps_producer_callback(struct sps_event_notify *notify)
unsigned int req_info;
struct ce_sps_data *pce_sps_data;
struct ce_request_info *preq_info;
- unsigned long flags;
print_notify_debug(notify);
@@ -3113,10 +3109,8 @@ static void _sps_producer_callback(struct sps_event_notify *notify)
&pce_sps_data->out_transfer);
_qce_set_flag(&pce_sps_data->out_transfer,
SPS_IOVEC_FLAG_INT);
- spin_lock_irqsave(&pce_dev->sps_lock, flags);
rc = sps_transfer(pce_dev->ce_bam_info.producer.pipe,
&pce_sps_data->out_transfer);
- spin_unlock_irqrestore(&pce_dev->sps_lock, flags);
if (rc) {
pr_err("sps_xfr() fail (producer pipe=0x%lx) rc = %d\n",
(uintptr_t)pce_dev->ce_bam_info.producer.pipe,
@@ -4590,18 +4584,27 @@ static int qce_dummy_req(struct qce_device *pce_dev)
static int select_mode(struct qce_device *pce_dev,
struct ce_request_info *preq_info)
{
- unsigned long flags;
struct ce_sps_data *pce_sps_data = &preq_info->ce_sps;
+ unsigned int no_of_queued_req;
+ unsigned int cadence;
if (!pce_dev->no_get_around) {
_qce_set_flag(&pce_sps_data->out_transfer, SPS_IOVEC_FLAG_INT);
return 0;
}
- spin_lock_irqsave(&pce_dev->lock, flags);
- pce_dev->no_of_queued_req++;
+ /*
+ * claim ownership of device
+ */
+again:
+ if (cmpxchg(&pce_dev->owner, QCE_OWNER_NONE, QCE_OWNER_CLIENT)
+ != QCE_OWNER_NONE) {
+ ndelay(40);
+ goto again;
+ }
+ no_of_queued_req = atomic_inc_return(&pce_dev->no_of_queued_req);
if (pce_dev->mode == IN_INTERRUPT_MODE) {
- if (pce_dev->no_of_queued_req >= MAX_BUNCH_MODE_REQ) {
+ if (no_of_queued_req >= MAX_BUNCH_MODE_REQ) {
pce_dev->mode = IN_BUNCH_MODE;
pr_debug("pcedev %d mode switch to BUNCH\n",
pce_dev->dev_no);
@@ -4618,17 +4621,21 @@ static int select_mode(struct qce_device *pce_dev,
}
} else {
pce_dev->intr_cadence++;
- if (pce_dev->intr_cadence >= SET_INTR_AT_REQ) {
+ cadence = (preq_info->req_len >> 7) + 1;
+ if (cadence > SET_INTR_AT_REQ)
+ cadence = SET_INTR_AT_REQ;
+ if (pce_dev->intr_cadence < cadence || ((pce_dev->intr_cadence
+ == cadence) && pce_dev->cadence_flag))
+ atomic_inc(&pce_dev->bunch_cmd_seq);
+ else {
_qce_set_flag(&pce_sps_data->out_transfer,
SPS_IOVEC_FLAG_INT);
pce_dev->intr_cadence = 0;
atomic_set(&pce_dev->bunch_cmd_seq, 0);
atomic_set(&pce_dev->last_intr_seq, 0);
- } else {
- atomic_inc(&pce_dev->bunch_cmd_seq);
+ pce_dev->cadence_flag = ~pce_dev->cadence_flag;
}
}
- spin_unlock_irqrestore(&pce_dev->lock, flags);
return 0;
}
@@ -4746,6 +4753,7 @@ static int _qce_aead_ccm_req(void *handle, struct qce_req *q_req)
/* setup xfer type for producer callback handling */
preq_info->xfer_type = QCE_XFER_AEAD;
+ preq_info->req_len = totallen_in;
_qce_sps_iovec_count_init(pce_dev, req_info);
@@ -4804,8 +4812,9 @@ static int _qce_aead_ccm_req(void *handle, struct qce_req *q_req)
_qce_ccm_get_around_output(pce_dev, preq_info, q_req->dir);
select_mode(pce_dev, preq_info);
+ rc = _qce_sps_transfer(pce_dev, req_info);
+ cmpxchg(&pce_dev->owner, QCE_OWNER_CLIENT, QCE_OWNER_NONE);
}
- rc = _qce_sps_transfer(pce_dev, req_info);
if (rc)
goto bad;
return 0;
@@ -4973,6 +4982,7 @@ int qce_aead_req(void *handle, struct qce_req *q_req)
/* setup xfer type for producer callback handling */
preq_info->xfer_type = QCE_XFER_AEAD;
+ preq_info->req_len = totallen;
_qce_sps_iovec_count_init(pce_dev, req_info);
@@ -5013,6 +5023,7 @@ int qce_aead_req(void *handle, struct qce_req *q_req)
SPS_IOVEC_FLAG_INT);
pce_sps_data->producer_state = QCE_PIPE_STATE_COMP;
}
+ rc = _qce_sps_transfer(pce_dev, req_info);
} else {
if (_qce_sps_add_sg_data(pce_dev, areq->src, totallen,
&pce_sps_data->in_transfer))
@@ -5040,8 +5051,9 @@ int qce_aead_req(void *handle, struct qce_req *q_req)
pce_sps_data->producer_state = QCE_PIPE_STATE_IDLE;
}
select_mode(pce_dev, preq_info);
+ rc = _qce_sps_transfer(pce_dev, req_info);
+ cmpxchg(&pce_dev->owner, QCE_OWNER_CLIENT, QCE_OWNER_NONE);
}
- rc = _qce_sps_transfer(pce_dev, req_info);
if (rc)
goto bad;
return 0;
@@ -5129,6 +5141,7 @@ int qce_ablk_cipher_req(void *handle, struct qce_req *c_req)
/* setup xfer type for producer callback handling */
preq_info->xfer_type = QCE_XFER_CIPHERING;
+ preq_info->req_len = areq->nbytes;
_qce_sps_iovec_count_init(pce_dev, req_info);
if (pce_dev->support_cmd_dscr)
@@ -5160,8 +5173,8 @@ int qce_ablk_cipher_req(void *handle, struct qce_req *c_req)
}
select_mode(pce_dev, preq_info);
-
rc = _qce_sps_transfer(pce_dev, req_info);
+ cmpxchg(&pce_dev->owner, QCE_OWNER_CLIENT, QCE_OWNER_NONE);
if (rc)
goto bad;
@@ -5233,6 +5246,7 @@ int qce_process_sha_req(void *handle, struct qce_sha_req *sreq)
/* setup xfer type for producer callback handling */
preq_info->xfer_type = QCE_XFER_HASHING;
+ preq_info->req_len = sreq->size;
_qce_sps_iovec_count_init(pce_dev, req_info);
@@ -5261,11 +5275,14 @@ int qce_process_sha_req(void *handle, struct qce_sha_req *sreq)
&pce_sps_data->out_transfer))
goto bad;
- if (is_dummy)
+ if (is_dummy) {
_qce_set_flag(&pce_sps_data->out_transfer, SPS_IOVEC_FLAG_INT);
- else
+ rc = _qce_sps_transfer(pce_dev, req_info);
+ } else {
select_mode(pce_dev, preq_info);
- rc = _qce_sps_transfer(pce_dev, req_info);
+ rc = _qce_sps_transfer(pce_dev, req_info);
+ cmpxchg(&pce_dev->owner, QCE_OWNER_CLIENT, QCE_OWNER_NONE);
+ }
if (rc)
goto bad;
return 0;
@@ -5353,6 +5370,7 @@ int qce_f8_req(void *handle, struct qce_f8_req *req,
/* setup xfer type for producer callback handling */
preq_info->xfer_type = QCE_XFER_F8;
+ preq_info->req_len = req->data_len;
_qce_sps_iovec_count_init(pce_dev, req_info);
@@ -5378,8 +5396,8 @@ int qce_f8_req(void *handle, struct qce_f8_req *req,
&pce_sps_data->out_transfer);
select_mode(pce_dev, preq_info);
-
rc = _qce_sps_transfer(pce_dev, req_info);
+ cmpxchg(&pce_dev->owner, QCE_OWNER_CLIENT, QCE_OWNER_NONE);
if (rc)
goto bad;
return 0;
@@ -5468,6 +5486,7 @@ int qce_f8_multi_pkt_req(void *handle, struct qce_f8_multi_pkt_req *mreq,
/* setup xfer type for producer callback handling */
preq_info->xfer_type = QCE_XFER_F8;
+ preq_info->req_len = total;
_qce_sps_iovec_count_init(pce_dev, req_info);
@@ -5492,8 +5511,8 @@ int qce_f8_multi_pkt_req(void *handle, struct qce_f8_multi_pkt_req *mreq,
&pce_sps_data->out_transfer);
select_mode(pce_dev, preq_info);
-
rc = _qce_sps_transfer(pce_dev, req_info);
+ cmpxchg(&pce_dev->owner, QCE_OWNER_CLIENT, QCE_OWNER_NONE);
if (rc == 0)
return 0;
@@ -5554,6 +5573,7 @@ int qce_f9_req(void *handle, struct qce_f9_req *req, void *cookie,
/* setup xfer type for producer callback handling */
preq_info->xfer_type = QCE_XFER_F9;
+ preq_info->req_len = req->msize;
_qce_sps_iovec_count_init(pce_dev, req_info);
if (pce_dev->support_cmd_dscr)
@@ -5573,8 +5593,8 @@ int qce_f9_req(void *handle, struct qce_f9_req *req, void *cookie,
&pce_sps_data->out_transfer);
select_mode(pce_dev, preq_info);
-
rc = _qce_sps_transfer(pce_dev, req_info);
+ cmpxchg(&pce_dev->owner, QCE_OWNER_CLIENT, QCE_OWNER_NONE);
if (rc)
goto bad;
return 0;
@@ -5939,9 +5959,7 @@ void *qce_open(struct platform_device *pdev, int *rc)
qce_setup_ce_sps_data(pce_dev);
qce_disable_clk(pce_dev);
setup_dummy_req(pce_dev);
- spin_lock_init(&pce_dev->lock);
- spin_lock_init(&pce_dev->sps_lock);
- pce_dev->no_of_queued_req = 0;
+ atomic_set(&pce_dev->no_of_queued_req, 0);
pce_dev->mode = IN_INTERRUPT_MODE;
init_timer(&(pce_dev->timer));
pce_dev->timer.function = qce_multireq_timeout;
@@ -5950,6 +5968,7 @@ void *qce_open(struct platform_device *pdev, int *rc)
pce_dev->intr_cadence = 0;
pce_dev->dev_no = pcedev_no;
pcedev_no++;
+ pce_dev->owner = QCE_OWNER_NONE;
mutex_unlock(&qce_iomap_mutex);
return pce_dev;
err:
diff --git a/drivers/crypto/msm/qce50.h b/drivers/crypto/msm/qce50.h
index cef466382ee4..6dba3664ff08 100644
--- a/drivers/crypto/msm/qce50.h
+++ b/drivers/crypto/msm/qce50.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2016, 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
@@ -232,6 +232,7 @@ struct ce_request_info {
dma_addr_t phy_ota_src;
dma_addr_t phy_ota_dst;
unsigned int ota_size;
+ unsigned int req_len;
};
struct qce_driver_stats {
diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c
index ab026d24e978..faeff0b55202 100644
--- a/drivers/crypto/msm/qcrypto.c
+++ b/drivers/crypto/msm/qcrypto.c
@@ -32,6 +32,7 @@
#include <linux/cache.h>
#include <linux/platform_data/qcom_crypto_device.h>
#include <linux/msm-bus.h>
+#include <linux/hardirq.h>
#include <linux/qcrypto.h>
#include <crypto/ctr.h>
@@ -51,7 +52,7 @@
#include "qce.h"
#define DEBUG_MAX_FNAME 16
-#define DEBUG_MAX_RW_BUF 2048
+#define DEBUG_MAX_RW_BUF 4096
#define QCRYPTO_BIG_NUMBER 9999999 /* a big number */
/*
@@ -131,6 +132,7 @@ struct qcrypto_req_control {
struct crypto_engine *pce;
struct crypto_async_request *req;
struct qcrypto_resp_ctx *arsp;
+ int res; /* execution result */
};
struct crypto_engine {
@@ -167,8 +169,14 @@ struct crypto_engine {
unsigned int max_req;
struct qcrypto_req_control *preq_pool;
atomic_t req_count;
+ bool issue_req; /* an request is being issued to qce */
+ bool first_engine; /* this engine is the first engine or not */
+ unsigned int irq_cpu; /* the cpu running the irq of this engine */
+ unsigned int max_req_used; /* debug stats */
};
+#define MAX_SMP_CPU 8
+
struct crypto_priv {
/* CE features supported by target device*/
struct msm_ce_hw_support platform_support;
@@ -208,21 +216,37 @@ struct crypto_priv {
enum resp_workq_sts sched_resp_workq_status;
enum req_processing_sts ce_req_proc_sts;
int cpu_getting_irqs_frm_first_ce;
+ struct crypto_engine *first_engine;
+ struct crypto_engine *scheduled_eng; /* last engine scheduled */
+
+ /* debug stats */
+ unsigned no_avail;
+ unsigned resp_stop;
+ unsigned resp_start;
+ unsigned max_qlen;
+ unsigned int queue_work_eng3;
+ unsigned int queue_work_not_eng3;
+ unsigned int queue_work_not_eng3_nz;
+ unsigned int max_resp_qlen;
+ unsigned int max_reorder_cnt;
+ unsigned int cpu_req[MAX_SMP_CPU+1];
};
static struct crypto_priv qcrypto_dev;
static struct crypto_engine *_qcrypto_static_assign_engine(
struct crypto_priv *cp);
static struct crypto_engine *_avail_eng(struct crypto_priv *cp);
-
static struct qcrypto_req_control *qcrypto_alloc_req_control(
struct crypto_engine *pce)
{
int i;
struct qcrypto_req_control *pqcrypto_req_control = pce->preq_pool;
+ unsigned int req_count;
for (i = 0; i < pce->max_req; i++) {
if (xchg(&pqcrypto_req_control->in_use, true) == false) {
- atomic_inc(&pce->req_count);
+ req_count = atomic_inc_return(&pce->req_count);
+ if (req_count > pce->max_req_used)
+ pce->max_req_used = req_count;
return pqcrypto_req_control;
}
pqcrypto_req_control++;
@@ -233,11 +257,13 @@ static struct qcrypto_req_control *qcrypto_alloc_req_control(
static void qcrypto_free_req_control(struct crypto_engine *pce,
struct qcrypto_req_control *preq)
{
+ /* do this before free req */
+ preq->req = NULL;
+ preq->arsp = NULL;
+ /* free req */
if (xchg(&preq->in_use, false) == false) {
pr_warn("request info %p free already\n", preq);
} else {
- preq->req = NULL;
- preq->arsp = NULL;
atomic_dec(&pce->req_count);
}
}
@@ -441,7 +467,9 @@ struct qcrypto_cipher_req_ctx {
#define SHA_MAX_DIGEST_SIZE SHA256_DIGEST_SIZE
#define MSM_QCRYPTO_REQ_QUEUE_LENGTH 768
-#define COMPLETION_CB_BACKLOG_LENGTH 768
+#define COMPLETION_CB_BACKLOG_LENGTH_STOP 400
+#define COMPLETION_CB_BACKLOG_LENGTH_START \
+ (COMPLETION_CB_BACKLOG_LENGTH_STOP / 2)
static uint8_t _std_init_vector_sha1_uint8[] = {
0x67, 0x45, 0x23, 0x01, 0xEF, 0xCD, 0xAB, 0x89,
@@ -1066,6 +1094,7 @@ static int _disp_stats(int id)
unsigned long flags;
struct crypto_priv *cp = &qcrypto_dev;
struct crypto_engine *pe;
+ int i;
pstat = &_qcrypto_stat;
len = scnprintf(_debug_read_buf, DEBUG_MAX_RW_BUF - 1,
@@ -1188,14 +1217,27 @@ static int _disp_stats(int id)
" AHASH operation fail : %llu\n",
pstat->ahash_op_fail);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+ " resp start, resp stop, max rsp queue reorder-cnt : %u %u %u %u\n",
+ cp->resp_start, cp->resp_stop,
+ cp->max_resp_qlen, cp->max_reorder_cnt);
+ len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+ " max queue legnth, no avail : %u %u\n",
+ cp->max_qlen, cp->no_avail);
+ len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+ " work queue : %u %u %u\n",
+ cp->queue_work_eng3,
+ cp->queue_work_not_eng3,
+ cp->queue_work_not_eng3_nz);
+ len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
"\n");
spin_lock_irqsave(&cp->lock, flags);
list_for_each_entry(pe, &cp->engine_list, elist) {
len += scnprintf(
_debug_read_buf + len,
DEBUG_MAX_RW_BUF - len - 1,
- " Engine %4d Req : %llu\n",
+ " Engine %4d Req max %d : %llu\n",
pe->unit,
+ pe->max_req_used,
pe->total_req
);
len += scnprintf(
@@ -1208,6 +1250,14 @@ static int _disp_stats(int id)
qce_get_driver_stats(pe->qce);
}
spin_unlock_irqrestore(&cp->lock, flags);
+
+ for (i = 0; i < MAX_SMP_CPU+1; i++)
+ if (cp->cpu_req[i])
+ len += scnprintf(
+ _debug_read_buf + len,
+ DEBUG_MAX_RW_BUF - len - 1,
+ "CPU %d Issue Req : %d\n",
+ i, cp->cpu_req[i]);
return len;
}
@@ -1217,13 +1267,25 @@ static void _qcrypto_remove_engine(struct crypto_engine *pengine)
struct qcrypto_alg *q_alg;
struct qcrypto_alg *n;
unsigned long flags;
+ struct crypto_engine *pe;
cp = pengine->pcp;
spin_lock_irqsave(&cp->lock, flags);
list_del(&pengine->elist);
+ if (pengine->first_engine) {
+ cp->first_engine = NULL;
+ pe = list_first_entry(&cp->engine_list, struct crypto_engine,
+ elist);
+ if (pe) {
+ pe->first_engine = true;
+ cp->first_engine = pe;
+ }
+ }
if (cp->next_engine == pengine)
cp->next_engine = NULL;
+ if (cp->scheduled_eng == pengine)
+ cp->scheduled_eng = NULL;
spin_unlock_irqrestore(&cp->lock, flags);
cp->total_units--;
@@ -1432,41 +1494,15 @@ static int _qcrypto_setkey_3des(struct crypto_ablkcipher *cipher, const u8 *key,
return 0;
};
-static struct crypto_engine *eng_sel_avoid_first(struct crypto_priv *cp)
-{
- /*
- * This function need not be spinlock protected when called from
- * the seq_response workq as it will not have any contentions when all
- * request processing is stopped.
- */
- struct crypto_engine *p;
- struct crypto_engine *q = NULL;
- int max_user = QCRYPTO_BIG_NUMBER;
- int use_cnt;
-
- if (unlikely(list_empty(&cp->engine_list))) {
- pr_err("%s: no valid ce to schedule\n", __func__);
- return NULL;
- }
-
- p = list_first_entry(&cp->engine_list, struct crypto_engine,
- elist);
- list_for_each_entry_continue(p, &cp->engine_list, elist) {
- use_cnt = atomic_read(&p->req_count);
- if ((use_cnt < p->max_req) && (use_cnt < max_user)) {
- q = p;
- max_user = use_cnt;
- }
- }
- return q;
-}
-
static void seq_response(struct work_struct *work)
{
struct crypto_priv *cp = container_of(work, struct crypto_priv,
resp_work);
struct llist_node *list;
struct llist_node *rev = NULL;
+ struct crypto_engine *pengine;
+ unsigned long flags;
+ int total_unit;
again:
list = llist_del_all(&cp->ordered_resp_list);
@@ -1485,7 +1521,6 @@ again:
while (rev) {
struct qcrypto_resp_ctx *arsp;
struct crypto_async_request *areq;
- struct crypto_engine *pengine;
arsp = container_of(rev, struct qcrypto_resp_ctx, llist);
rev = llist_next(rev);
@@ -1495,12 +1530,20 @@ again:
areq->complete(areq, arsp->res);
local_bh_enable();
atomic_dec(&cp->resp_cnt);
- if (ACCESS_ONCE(cp->ce_req_proc_sts) == STOPPED &&
- atomic_read(&cp->resp_cnt) <=
- (COMPLETION_CB_BACKLOG_LENGTH / 2)) {
- pengine = eng_sel_avoid_first(cp);
+ }
+
+ if (atomic_read(&cp->resp_cnt) < COMPLETION_CB_BACKLOG_LENGTH_START &&
+ (cmpxchg(&cp->ce_req_proc_sts, STOPPED, IN_PROGRESS)
+ == STOPPED)) {
+ cp->resp_start++;
+ for (total_unit = cp->total_units; total_unit-- > 0;) {
+ spin_lock_irqsave(&cp->lock, flags);
+ pengine = _avail_eng(cp);
+ spin_unlock_irqrestore(&cp->lock, flags);
if (pengine)
_start_qcrypto_process(cp, pengine);
+ else
+ break;
}
}
end:
@@ -1512,12 +1555,19 @@ end:
goto end;
}
-static void _qcrypto_tfm_complete(struct crypto_priv *cp, u32 type,
- void *tfm_ctx)
+#define SCHEUDLE_RSP_QLEN_THRESHOLD 64
+
+static void _qcrypto_tfm_complete(struct crypto_engine *pengine, u32 type,
+ void *tfm_ctx,
+ struct qcrypto_resp_ctx *cur_arsp,
+ int res)
{
+ struct crypto_priv *cp = pengine->pcp;
unsigned long flags;
struct qcrypto_resp_ctx *arsp;
struct list_head *plist;
+ unsigned int resp_qlen;
+ unsigned int cnt = 0;
switch (type) {
case CRYPTO_ALG_TYPE_AHASH:
@@ -1531,6 +1581,8 @@ static void _qcrypto_tfm_complete(struct crypto_priv *cp, u32 type,
}
spin_lock_irqsave(&cp->lock, flags);
+
+ cur_arsp->res = res;
while (!list_empty(plist)) {
arsp = list_first_entry(plist,
struct qcrypto_resp_ctx, list);
@@ -1539,16 +1591,51 @@ static void _qcrypto_tfm_complete(struct crypto_priv *cp, u32 type,
else {
list_del(&arsp->list);
llist_add(&arsp->llist, &cp->ordered_resp_list);
+ atomic_inc(&cp->resp_cnt);
+ cnt++;
}
}
+ resp_qlen = atomic_read(&cp->resp_cnt);
+ if (resp_qlen > cp->max_resp_qlen)
+ cp->max_resp_qlen = resp_qlen;
+ if (cnt > cp->max_reorder_cnt)
+ cp->max_reorder_cnt = cnt;
+ if ((resp_qlen >= COMPLETION_CB_BACKLOG_LENGTH_STOP) &&
+ cmpxchg(&cp->ce_req_proc_sts, IN_PROGRESS,
+ STOPPED) == IN_PROGRESS) {
+ cp->resp_stop++;
+ }
+
spin_unlock_irqrestore(&cp->lock, flags);
retry:
if (!llist_empty(&cp->ordered_resp_list)) {
+ unsigned int cpu;
+
+ if (pengine->first_engine) {
+ cpu = WORK_CPU_UNBOUND;
+ cp->queue_work_eng3++;
+ } else {
+ cp->queue_work_not_eng3++;
+ cpu = cp->cpu_getting_irqs_frm_first_ce;
+ /*
+ * If source not the first engine, and there
+ * are outstanding requests going on first engine,
+ * skip scheduling of work queue to anticipate
+ * more may be coming. If the response queue
+ * length exceeds threshold, to avoid further
+ * delay, schedule work queue immediately.
+ */
+ if (cp->first_engine && atomic_read(
+ &cp->first_engine->req_count)) {
+ if (resp_qlen < SCHEUDLE_RSP_QLEN_THRESHOLD)
+ return;
+ cp->queue_work_not_eng3_nz++;
+ }
+ }
if (cmpxchg(&cp->sched_resp_workq_status, NOT_SCHEDULED,
IS_SCHEDULED) == NOT_SCHEDULED)
- queue_work_on(cp->cpu_getting_irqs_frm_first_ce,
- cp->resp_wq, &cp->resp_work);
+ queue_work_on(cpu, cp->resp_wq, &cp->resp_work);
else if (cmpxchg(&cp->sched_resp_workq_status, IS_SCHEDULED,
SCHEDULE_AGAIN) == NOT_SCHEDULED)
goto retry;
@@ -1559,36 +1646,34 @@ static void req_done(struct qcrypto_req_control *pqcrypto_req_control)
{
struct crypto_engine *pengine;
struct crypto_async_request *areq;
- struct crypto_engine *pe;
struct crypto_priv *cp;
- unsigned long flags;
struct qcrypto_resp_ctx *arsp;
u32 type = 0;
void *tfm_ctx = NULL;
+ unsigned int cpu;
+ int res;
pengine = pqcrypto_req_control->pce;
cp = pengine->pcp;
- spin_lock_irqsave(&cp->lock, flags);
areq = pqcrypto_req_control->req;
arsp = pqcrypto_req_control->arsp;
+ res = pqcrypto_req_control->res;
qcrypto_free_req_control(pengine, pqcrypto_req_control);
if (areq) {
type = crypto_tfm_alg_type(areq->tfm);
tfm_ctx = crypto_tfm_ctx(areq->tfm);
}
- pe = list_first_entry(&cp->engine_list, struct crypto_engine, elist);
- if (pe == pengine)
- if (cp->cpu_getting_irqs_frm_first_ce != smp_processor_id())
- cp->cpu_getting_irqs_frm_first_ce = smp_processor_id();
- spin_unlock_irqrestore(&cp->lock, flags);
- if (atomic_read(&cp->resp_cnt) <= COMPLETION_CB_BACKLOG_LENGTH) {
- cmpxchg(&cp->ce_req_proc_sts, STOPPED, IN_PROGRESS);
- _start_qcrypto_process(cp, pengine);
- } else
- cmpxchg(&cp->ce_req_proc_sts, IN_PROGRESS, STOPPED);
+ cpu = smp_processor_id();
+ pengine->irq_cpu = cpu;
+ if (pengine->first_engine) {
+ if (cpu != cp->cpu_getting_irqs_frm_first_ce)
+ cp->cpu_getting_irqs_frm_first_ce = cpu;
+ }
if (areq)
- _qcrypto_tfm_complete(cp, type, tfm_ctx);
+ _qcrypto_tfm_complete(pengine, type, tfm_ctx, arsp, res);
+ if (ACCESS_ONCE(cp->ce_req_proc_sts) == IN_PROGRESS)
+ _start_qcrypto_process(cp, pengine);
}
static void _qce_ahash_complete(void *cookie, unsigned char *digest,
@@ -1639,10 +1724,10 @@ static void _qce_ahash_complete(void *cookie, unsigned char *digest,
rctx->first_blk = 0;
if (ret) {
- pqcrypto_req_control->arsp->res = -ENXIO;
+ pqcrypto_req_control->res = -ENXIO;
pstat->ahash_op_fail++;
} else {
- pqcrypto_req_control->arsp->res = 0;
+ pqcrypto_req_control->res = 0;
pstat->ahash_op_success++;
}
if (cp->ce_support.aligned_only) {
@@ -1684,10 +1769,10 @@ static void _qce_ablk_cipher_complete(void *cookie, unsigned char *icb,
memcpy(ctx->iv, iv, crypto_ablkcipher_ivsize(ablk));
if (ret) {
- pqcrypto_req_control->arsp->res = -ENXIO;
+ pqcrypto_req_control->res = -ENXIO;
pstat->ablk_cipher_op_fail++;
} else {
- pqcrypto_req_control->arsp->res = 0;
+ pqcrypto_req_control->res = 0;
pstat->ablk_cipher_op_success++;
}
@@ -1773,7 +1858,7 @@ static void _qce_aead_complete(void *cookie, unsigned char *icv,
else
pstat->aead_op_success++;
- pqcrypto_req_control->arsp->res = ret;
+ pqcrypto_req_control->res = ret;
req_done(pqcrypto_req_control);
}
@@ -2100,12 +2185,24 @@ static int _start_qcrypto_process(struct crypto_priv *cp,
struct aead_request *aead_req;
struct qcrypto_resp_ctx *arsp;
struct qcrypto_req_control *pqcrypto_req_control;
+ unsigned int cpu = MAX_SMP_CPU;
+
+ if (ACCESS_ONCE(cp->ce_req_proc_sts) == STOPPED)
+ return 0;
+
+ if (in_interrupt()) {
+ cpu = smp_processor_id();
+ if (cpu >= MAX_SMP_CPU)
+ cpu = MAX_SMP_CPU - 1;
+ } else
+ cpu = MAX_SMP_CPU;
pstat = &_qcrypto_stat;
again:
spin_lock_irqsave(&cp->lock, flags);
- if (atomic_read(&pengine->req_count) >= (pengine->max_req)) {
+ if (pengine->issue_req ||
+ atomic_read(&pengine->req_count) >= (pengine->max_req)) {
spin_unlock_irqrestore(&cp->lock, flags);
return 0;
}
@@ -2176,7 +2273,6 @@ again:
break;
}
- atomic_inc(&cp->resp_cnt);
arsp->res = -EINPROGRESS;
arsp->async_req = async_req;
pqcrypto_req_control->pce = pengine;
@@ -2185,6 +2281,10 @@ again:
pengine->active_seq++;
pengine->check_flag = true;
+ pengine->issue_req = true;
+ cp->cpu_req[cpu]++;
+ smp_mb(); /* make it visible */
+
spin_unlock_irqrestore(&cp->lock, flags);
if (backlog_eng)
backlog_eng->complete(backlog_eng, -EINPROGRESS);
@@ -2204,9 +2304,12 @@ again:
default:
ret = -EINVAL;
};
+
+ pengine->issue_req = false;
+ smp_mb(); /* make it visible */
+
pengine->total_req++;
if (ret) {
- arsp->res = ret;
pengine->err_req++;
qcrypto_free_req_control(pengine, pqcrypto_req_control);
@@ -2218,32 +2321,48 @@ again:
else
pstat->aead_op_fail++;
- _qcrypto_tfm_complete(cp, type, tfm_ctx);
+ _qcrypto_tfm_complete(pengine, type, tfm_ctx, arsp, ret);
goto again;
};
return ret;
}
+static inline struct crypto_engine *_next_eng(struct crypto_priv *cp,
+ struct crypto_engine *p)
+{
+
+ if (p == NULL || list_is_last(&p->elist, &cp->engine_list))
+ p = list_first_entry(&cp->engine_list, struct crypto_engine,
+ elist);
+ else
+ p = list_entry(p->elist.next, struct crypto_engine, elist);
+ return p;
+}
static struct crypto_engine *_avail_eng(struct crypto_priv *cp)
{
/* call this function with spinlock set */
- struct crypto_engine *p;
struct crypto_engine *q = NULL;
- int max_user = QCRYPTO_BIG_NUMBER;
- int use_cnt;
+ struct crypto_engine *p = cp->scheduled_eng;
+ struct crypto_engine *q1;
+ int eng_cnt = cp->total_units;
if (unlikely(list_empty(&cp->engine_list))) {
pr_err("%s: no valid ce to schedule\n", __func__);
return NULL;
}
- list_for_each_entry(p, &cp->engine_list, elist) {
- use_cnt = atomic_read(&p->req_count);
- if ((use_cnt < p->max_req) && (use_cnt < max_user)) {
+ p = _next_eng(cp, p);
+ q1 = p;
+ while (eng_cnt-- > 0) {
+ if (!p->issue_req && atomic_read(&p->req_count) < p->max_req) {
q = p;
- max_user = use_cnt;
+ break;
}
+ p = _next_eng(cp, p);
+ if (q1 == p)
+ break;
}
+ cp->scheduled_eng = q;
return q;
}
@@ -2261,6 +2380,8 @@ static int _qcrypto_queue_req(struct crypto_priv *cp,
} else {
ret = crypto_enqueue_request(&cp->req_queue, req);
pengine = _avail_eng(cp);
+ if (cp->req_queue.qlen > cp->max_qlen)
+ cp->max_qlen = cp->req_queue.qlen;
}
if (pengine) {
switch (pengine->bw_state) {
@@ -2286,16 +2407,12 @@ static int _qcrypto_queue_req(struct crypto_priv *cp,
pengine = NULL;
break;
}
+ } else {
+ cp->no_avail++;
}
spin_unlock_irqrestore(&cp->lock, flags);
- if (pengine) {
- if (atomic_read(&cp->resp_cnt) <=
- COMPLETION_CB_BACKLOG_LENGTH) {
- cmpxchg(&cp->ce_req_proc_sts, STOPPED, IN_PROGRESS);
- _start_qcrypto_process(cp, pengine);
- } else
- cmpxchg(&cp->ce_req_proc_sts, IN_PROGRESS, STOPPED);
- }
+ if (pengine && (ACCESS_ONCE(cp->ce_req_proc_sts) == IN_PROGRESS))
+ _start_qcrypto_process(cp, pengine);
return ret;
}
@@ -4762,6 +4879,8 @@ static int _qcrypto_probe(struct platform_device *pdev)
pengine->active_seq = 0;
pengine->last_active_seq = 0;
pengine->check_flag = false;
+ pengine->max_req_used = 0;
+ pengine->issue_req = false;
crypto_init_queue(&pengine->req_queue, MSM_QCRYPTO_REQ_QUEUE_LENGTH);
@@ -4770,6 +4889,9 @@ static int _qcrypto_probe(struct platform_device *pdev)
pengine->unit = cp->total_units;
spin_lock_irqsave(&cp->lock, flags);
+ pengine->first_engine = list_empty(&cp->engine_list);
+ if (pengine->first_engine)
+ cp->first_engine = pengine;
list_add_tail(&pengine->elist, &cp->engine_list);
cp->next_engine = pengine;
spin_unlock_irqrestore(&cp->lock, flags);
@@ -5292,6 +5414,7 @@ static ssize_t _debug_stats_write(struct file *file, const char __user *buf,
unsigned long flags;
struct crypto_priv *cp = &qcrypto_dev;
struct crypto_engine *pe;
+ int i;
memset((char *)&_qcrypto_stat, 0, sizeof(struct crypto_stat));
spin_lock_irqsave(&cp->lock, flags);
@@ -5299,7 +5422,19 @@ static ssize_t _debug_stats_write(struct file *file, const char __user *buf,
pe->total_req = 0;
pe->err_req = 0;
qce_clear_driver_stats(pe->qce);
+ pe->max_req_used = 0;
}
+ cp->max_qlen = 0;
+ cp->resp_start = 0;
+ cp->resp_stop = 0;
+ cp->no_avail = 0;
+ cp->max_resp_qlen = 0;
+ cp->queue_work_eng3 = 0;
+ cp->queue_work_not_eng3 = 0;
+ cp->queue_work_not_eng3_nz = 0;
+ cp->max_reorder_cnt = 0;
+ for (i = 0; i < MAX_SMP_CPU + 1; i++)
+ cp->cpu_req[i] = 0;
spin_unlock_irqrestore(&cp->lock, flags);
return count;
}
@@ -5362,6 +5497,8 @@ static int __init _qcrypto_init(void)
pcp->total_units = 0;
pcp->platform_support.bus_scale_table = NULL;
pcp->next_engine = NULL;
+ pcp->scheduled_eng = NULL;
+ pcp->ce_req_proc_sts = IN_PROGRESS;
crypto_init_queue(&pcp->req_queue, MSM_QCRYPTO_REQ_QUEUE_LENGTH);
return platform_driver_register(&_qualcomm_crypto);
}
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 918231b73215..3052166c7a18 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -1760,6 +1760,30 @@ static int adreno_getproperty(struct kgsl_device *device,
status = 0;
}
break;
+ case KGSL_PROP_DEVICE_QDSS_STM:
+ {
+ struct kgsl_qdss_stm_prop qdssprop = {0};
+ struct kgsl_memdesc *qdss_desc =
+ kgsl_mmu_get_qdss_global_entry(device);
+
+ if (sizebytes != sizeof(qdssprop)) {
+ status = -EINVAL;
+ break;
+ }
+
+ if (qdss_desc) {
+ qdssprop.gpuaddr = qdss_desc->gpuaddr;
+ qdssprop.size = qdss_desc->size;
+ }
+
+ if (copy_to_user(value, &qdssprop,
+ sizeof(qdssprop))) {
+ status = -EFAULT;
+ break;
+ }
+ status = 0;
+ }
+ break;
case KGSL_PROP_MMU_ENABLE:
{
/* Report MMU only if we can handle paged memory */
diff --git a/drivers/gpu/msm/adreno_compat.c b/drivers/gpu/msm/adreno_compat.c
index 582cbfb61e78..d86a0c60f0b4 100644
--- a/drivers/gpu/msm/adreno_compat.c
+++ b/drivers/gpu/msm/adreno_compat.c
@@ -89,6 +89,30 @@ int adreno_getproperty_compat(struct kgsl_device *device,
status = 0;
}
break;
+ case KGSL_PROP_DEVICE_QDSS_STM:
+ {
+ struct kgsl_qdss_stm_prop qdssprop = {0};
+ struct kgsl_memdesc *qdss_desc =
+ kgsl_mmu_get_qdss_global_entry(device);
+
+ if (sizebytes != sizeof(qdssprop)) {
+ status = -EINVAL;
+ break;
+ }
+
+ if (qdss_desc) {
+ qdssprop.gpuaddr = qdss_desc->gpuaddr;
+ qdssprop.size = qdss_desc->size;
+ }
+
+ if (copy_to_user(value, &qdssprop,
+ sizeof(qdssprop))) {
+ status = -EFAULT;
+ break;
+ }
+ status = 0;
+ }
+ break;
default:
/*
* Call the adreno_getproperty to check if the property type
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 103d290eb681..865cd9d8f498 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -96,6 +96,7 @@ static struct kgsl_memdesc *global_pt_entries[GLOBAL_PT_ENTRIES];
static struct kgsl_memdesc *kgsl_global_secure_pt_entry;
static int global_pt_count;
uint64_t global_pt_alloc;
+static struct kgsl_memdesc gpu_qdss_desc;
static void kgsl_iommu_unmap_globals(struct kgsl_pagetable *pagetable)
{
@@ -183,6 +184,51 @@ void kgsl_add_global_secure_entry(struct kgsl_device *device,
kgsl_global_secure_pt_entry = memdesc;
}
+struct kgsl_memdesc *kgsl_iommu_get_qdss_global_entry(void)
+{
+ return &gpu_qdss_desc;
+}
+
+static void kgsl_setup_qdss_desc(struct kgsl_device *device)
+{
+ int result = 0;
+ uint32_t gpu_qdss_entry[2];
+
+ if (!of_find_property(device->pdev->dev.of_node,
+ "qcom,gpu-qdss-stm", NULL))
+ return;
+
+ if (of_property_read_u32_array(device->pdev->dev.of_node,
+ "qcom,gpu-qdss-stm", gpu_qdss_entry, 2)) {
+ KGSL_CORE_ERR("Failed to read gpu qdss dts entry\n");
+ return;
+ }
+
+ gpu_qdss_desc.flags = 0;
+ gpu_qdss_desc.priv = 0;
+ gpu_qdss_desc.physaddr = gpu_qdss_entry[0];
+ gpu_qdss_desc.size = gpu_qdss_entry[1];
+ gpu_qdss_desc.pagetable = NULL;
+ gpu_qdss_desc.ops = NULL;
+ gpu_qdss_desc.dev = device->dev->parent;
+ gpu_qdss_desc.hostptr = NULL;
+
+ result = memdesc_sg_dma(&gpu_qdss_desc, gpu_qdss_desc.physaddr,
+ gpu_qdss_desc.size);
+ if (result) {
+ KGSL_CORE_ERR("memdesc_sg_dma failed: %d\n", result);
+ return;
+ }
+
+ kgsl_mmu_add_global(device, &gpu_qdss_desc);
+}
+
+static inline void kgsl_cleanup_qdss_desc(struct kgsl_mmu *mmu)
+{
+ kgsl_iommu_remove_global(mmu, &gpu_qdss_desc);
+ kgsl_sharedmem_free(&gpu_qdss_desc);
+}
+
static inline void _iommu_sync_mmu_pc(bool lock)
{
@@ -1265,6 +1311,7 @@ static void kgsl_iommu_close(struct kgsl_mmu *mmu)
kgsl_iommu_remove_global(mmu, &iommu->setstate);
kgsl_sharedmem_free(&iommu->setstate);
+ kgsl_cleanup_qdss_desc(mmu);
}
static int _setstate_alloc(struct kgsl_device *device,
@@ -1336,6 +1383,7 @@ static int kgsl_iommu_init(struct kgsl_mmu *mmu)
}
kgsl_iommu_add_global(mmu, &iommu->setstate);
+ kgsl_setup_qdss_desc(device);
done:
if (status)
@@ -2341,6 +2389,7 @@ struct kgsl_mmu_ops kgsl_iommu_ops = {
.mmu_add_global = kgsl_iommu_add_global,
.mmu_remove_global = kgsl_iommu_remove_global,
.mmu_getpagetable = kgsl_iommu_getpagetable,
+ .mmu_get_qdss_global_entry = kgsl_iommu_get_qdss_global_entry,
.probe = kgsl_iommu_probe,
};
diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c
index f8315090ff06..8b0d93fda32c 100644
--- a/drivers/gpu/msm/kgsl_mmu.c
+++ b/drivers/gpu/msm/kgsl_mmu.c
@@ -546,6 +546,17 @@ bool kgsl_mmu_gpuaddr_in_range(struct kgsl_pagetable *pagetable,
}
EXPORT_SYMBOL(kgsl_mmu_gpuaddr_in_range);
+struct kgsl_memdesc *kgsl_mmu_get_qdss_global_entry(struct kgsl_device *device)
+{
+ struct kgsl_mmu *mmu = &device->mmu;
+
+ if (MMU_OP_VALID(mmu, mmu_get_qdss_global_entry))
+ return mmu->mmu_ops->mmu_get_qdss_global_entry();
+
+ return NULL;
+}
+EXPORT_SYMBOL(kgsl_mmu_get_qdss_global_entry);
+
/*
* NOMMU defintions - NOMMU really just means that the MMU is kept in pass
* through and the GPU directly accesses physical memory. Used in debug mode and
diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h
index 5339917911b1..588777af353f 100644
--- a/drivers/gpu/msm/kgsl_mmu.h
+++ b/drivers/gpu/msm/kgsl_mmu.h
@@ -80,6 +80,7 @@ struct kgsl_mmu_ops {
struct kgsl_memdesc *memdesc);
struct kgsl_pagetable * (*mmu_getpagetable)(struct kgsl_mmu *mmu,
unsigned long name);
+ struct kgsl_memdesc* (*mmu_get_qdss_global_entry)(void);
};
struct kgsl_mmu_pt_ops {
@@ -227,6 +228,8 @@ int kgsl_mmu_unmap_offset(struct kgsl_pagetable *pagetable,
struct kgsl_memdesc *memdesc, uint64_t addr, uint64_t offset,
uint64_t size);
+struct kgsl_memdesc *kgsl_mmu_get_qdss_global_entry(struct kgsl_device *device);
+
/*
* Static inline functions of MMU that simply call the SMMU specific
* function using a function pointer. These functions can be thought
diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c
index ba54e7bde795..3776e748647e 100644
--- a/drivers/hwmon/qpnp-adc-common.c
+++ b/drivers/hwmon/qpnp-adc-common.c
@@ -38,6 +38,7 @@
#define PMI_CHG_SCALE_1 -138890
#define PMI_CHG_SCALE_2 391750000000
#define QPNP_VADC_HC_VREF_CODE 0x4000
+#define QPNP_VADC_HC_VDD_REFERENCE_MV 1875
/* Units for temperature below (on x axis) is in 0.1DegC as
required by the battery driver. Note the resolution used
@@ -789,41 +790,51 @@ int32_t qpnp_adc_scale_millidegc_pmic_voltage_thr(struct qpnp_vadc_chip *chip,
int64_t low_output = 0, high_output = 0;
int rc = 0, sign = 0;
- rc = qpnp_get_vadc_gain_and_offset(chip, &btm_param, CALIB_ABSOLUTE);
- if (rc < 0) {
- pr_err("Could not acquire gain and offset\n");
- return rc;
- }
-
/* Convert to Kelvin and account for voltage to be written as 2mV/K */
low_output = (param->low_temp + KELVINMIL_DEGMIL) * 2;
- /* Convert to voltage threshold */
- low_output = (low_output - QPNP_ADC_625_UV) * btm_param.dy;
- if (low_output < 0) {
- sign = 1;
- low_output = -low_output;
- }
- do_div(low_output, QPNP_ADC_625_UV);
- if (sign)
- low_output = -low_output;
- low_output += btm_param.adc_gnd;
-
- sign = 0;
/* Convert to Kelvin and account for voltage to be written as 2mV/K */
high_output = (param->high_temp + KELVINMIL_DEGMIL) * 2;
- /* Convert to voltage threshold */
- high_output = (high_output - QPNP_ADC_625_UV) * btm_param.dy;
- if (high_output < 0) {
- sign = 1;
- high_output = -high_output;
+
+ if (param->adc_tm_hc) {
+ low_output *= QPNP_VADC_HC_VREF_CODE;
+ do_div(low_output, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
+ high_output *= QPNP_VADC_HC_VREF_CODE;
+ do_div(high_output, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
+ } else {
+ rc = qpnp_get_vadc_gain_and_offset(chip, &btm_param,
+ CALIB_ABSOLUTE);
+ if (rc < 0) {
+ pr_err("Could not acquire gain and offset\n");
+ return rc;
+ }
+
+ /* Convert to voltage threshold */
+ low_output = (low_output - QPNP_ADC_625_UV) * btm_param.dy;
+ if (low_output < 0) {
+ sign = 1;
+ low_output = -low_output;
+ }
+ do_div(low_output, QPNP_ADC_625_UV);
+ if (sign)
+ low_output = -low_output;
+ low_output += btm_param.adc_gnd;
+
+ sign = 0;
+ /* Convert to voltage threshold */
+ high_output = (high_output - QPNP_ADC_625_UV) * btm_param.dy;
+ if (high_output < 0) {
+ sign = 1;
+ high_output = -high_output;
+ }
+ do_div(high_output, QPNP_ADC_625_UV);
+ if (sign)
+ high_output = -high_output;
+ high_output += btm_param.adc_gnd;
}
- do_div(high_output, QPNP_ADC_625_UV);
- if (sign)
- high_output = -high_output;
- high_output += btm_param.adc_gnd;
*low_threshold = (uint32_t) low_output;
*high_threshold = (uint32_t) high_output;
+
pr_debug("high_temp:%d, low_temp:%d\n", param->high_temp,
param->low_temp);
pr_debug("adc_code_high:%x, adc_code_low:%x\n", *high_threshold,
@@ -1079,29 +1090,34 @@ int32_t qpnp_adc_tm_scale_voltage_therm_pu2(struct qpnp_vadc_chip *chip,
{
int64_t adc_voltage = 0;
struct qpnp_vadc_linear_graph param1;
- int negative_offset;
-
- qpnp_get_vadc_gain_and_offset(chip, &param1, CALIB_RATIOMETRIC);
+ int negative_offset = 0;
- adc_voltage = (reg - param1.adc_gnd) * param1.adc_vref;
- if (adc_voltage < 0) {
- negative_offset = 1;
- adc_voltage = -adc_voltage;
- }
-
- do_div(adc_voltage, param1.dy);
-
- if (adc_properties->adc_hc)
+ if (adc_properties->adc_hc) {
+ /* (ADC code * vref_vadc (1.875V)) / 0x4000 */
+ adc_voltage = (int64_t) reg;
+ adc_voltage *= QPNP_VADC_HC_VDD_REFERENCE_MV;
+ adc_voltage = div64_s64(adc_voltage,
+ QPNP_VADC_HC_VREF_CODE);
qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref,
ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
adc_voltage, result);
- else
+ } else {
+ qpnp_get_vadc_gain_and_offset(chip, &param1, CALIB_RATIOMETRIC);
+
+ adc_voltage = (reg - param1.adc_gnd) * param1.adc_vref;
+ if (adc_voltage < 0) {
+ negative_offset = 1;
+ adc_voltage = -adc_voltage;
+ }
+
+ do_div(adc_voltage, param1.dy);
+
qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb,
ARRAY_SIZE(adcmap_100k_104ef_104fb),
adc_voltage, result);
-
- if (negative_offset)
- adc_voltage = -adc_voltage;
+ if (negative_offset)
+ adc_voltage = -adc_voltage;
+ }
return 0;
}
@@ -1114,8 +1130,6 @@ int32_t qpnp_adc_tm_scale_therm_voltage_pu2(struct qpnp_vadc_chip *chip,
struct qpnp_vadc_linear_graph param1;
int rc;
- qpnp_get_vadc_gain_and_offset(chip, &param1, CALIB_RATIOMETRIC);
-
if (adc_properties->adc_hc) {
rc = qpnp_adc_map_temp_voltage(
adcmap_100k_104ef_104fb_1875_vref,
@@ -1123,27 +1137,40 @@ int32_t qpnp_adc_tm_scale_therm_voltage_pu2(struct qpnp_vadc_chip *chip,
param->low_thr_temp, &param->low_thr_voltage);
if (rc)
return rc;
+ param->low_thr_voltage *= QPNP_VADC_HC_VREF_CODE;
+ do_div(param->low_thr_voltage, QPNP_VADC_HC_VDD_REFERENCE_MV);
+
+ rc = qpnp_adc_map_temp_voltage(
+ adcmap_100k_104ef_104fb_1875_vref,
+ ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
+ param->high_thr_temp, &param->high_thr_voltage);
+ if (rc)
+ return rc;
+ param->high_thr_voltage *= QPNP_VADC_HC_VREF_CODE;
+ do_div(param->high_thr_voltage, QPNP_VADC_HC_VDD_REFERENCE_MV);
} else {
+ qpnp_get_vadc_gain_and_offset(chip, &param1, CALIB_RATIOMETRIC);
+
rc = qpnp_adc_map_temp_voltage(adcmap_100k_104ef_104fb,
ARRAY_SIZE(adcmap_100k_104ef_104fb),
param->low_thr_temp, &param->low_thr_voltage);
if (rc)
return rc;
- }
- param->low_thr_voltage *= param1.dy;
- do_div(param->low_thr_voltage, param1.adc_vref);
- param->low_thr_voltage += param1.adc_gnd;
+ param->low_thr_voltage *= param1.dy;
+ do_div(param->low_thr_voltage, param1.adc_vref);
+ param->low_thr_voltage += param1.adc_gnd;
- rc = qpnp_adc_map_temp_voltage(adcmap_100k_104ef_104fb,
- ARRAY_SIZE(adcmap_100k_104ef_104fb),
- param->high_thr_temp, &param->high_thr_voltage);
- if (rc)
- return rc;
+ rc = qpnp_adc_map_temp_voltage(adcmap_100k_104ef_104fb,
+ ARRAY_SIZE(adcmap_100k_104ef_104fb),
+ param->high_thr_temp, &param->high_thr_voltage);
+ if (rc)
+ return rc;
- param->high_thr_voltage *= param1.dy;
- do_div(param->high_thr_voltage, param1.adc_vref);
- param->high_thr_voltage += param1.adc_gnd;
+ param->high_thr_voltage *= param1.dy;
+ do_div(param->high_thr_voltage, param1.adc_vref);
+ param->high_thr_voltage += param1.adc_gnd;
+ }
return 0;
}
@@ -1251,7 +1278,7 @@ int32_t qpnp_adc_usb_scaler(struct qpnp_vadc_chip *chip,
}
EXPORT_SYMBOL(qpnp_adc_usb_scaler);
-int32_t qpnp_adc_vbatt_rscaler(struct qpnp_vadc_chip *chip,
+int32_t qpnp_adc_absolute_rthr(struct qpnp_vadc_chip *chip,
struct qpnp_adc_tm_btm_param *param,
uint32_t *low_threshold, uint32_t *high_threshold)
{
@@ -1259,32 +1286,49 @@ int32_t qpnp_adc_vbatt_rscaler(struct qpnp_vadc_chip *chip,
int rc = 0, sign = 0;
int64_t low_thr = 0, high_thr = 0;
- rc = qpnp_get_vadc_gain_and_offset(chip, &vbatt_param, CALIB_ABSOLUTE);
- if (rc < 0)
- return rc;
-
- low_thr = (((param->low_thr/param->gain_den) - QPNP_ADC_625_UV) *
- vbatt_param.dy);
- if (low_thr < 0) {
- sign = 1;
- low_thr = -low_thr;
- }
- do_div(low_thr, QPNP_ADC_625_UV);
- if (sign)
- low_thr = -low_thr;
- *low_threshold = low_thr + vbatt_param.adc_gnd;
+ if (param->adc_tm_hc) {
+ low_thr = (param->low_thr/param->gain_den);
+ low_thr *= param->gain_num;
+ low_thr *= QPNP_VADC_HC_VREF_CODE;
+ do_div(low_thr, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
+ *low_threshold = low_thr;
+
+ high_thr = (param->high_thr/param->gain_den);
+ high_thr *= param->gain_num;
+ high_thr *= QPNP_VADC_HC_VREF_CODE;
+ do_div(high_thr, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
+ *high_threshold = high_thr;
+ } else {
+ rc = qpnp_get_vadc_gain_and_offset(chip, &vbatt_param,
+ CALIB_ABSOLUTE);
+ if (rc < 0)
+ return rc;
- sign = 0;
- high_thr = (((param->high_thr/param->gain_den) - QPNP_ADC_625_UV) *
- vbatt_param.dy);
- if (high_thr < 0) {
- sign = 1;
- high_thr = -high_thr;
+ low_thr = (((param->low_thr/param->gain_den) -
+ QPNP_ADC_625_UV) * vbatt_param.dy);
+ if (low_thr < 0) {
+ sign = 1;
+ low_thr = -low_thr;
+ }
+ low_thr = low_thr * param->gain_num;
+ do_div(low_thr, QPNP_ADC_625_UV);
+ if (sign)
+ low_thr = -low_thr;
+ *low_threshold = low_thr + vbatt_param.adc_gnd;
+
+ sign = 0;
+ high_thr = (((param->high_thr/param->gain_den) -
+ QPNP_ADC_625_UV) * vbatt_param.dy);
+ if (high_thr < 0) {
+ sign = 1;
+ high_thr = -high_thr;
+ }
+ high_thr = high_thr * param->gain_num;
+ do_div(high_thr, QPNP_ADC_625_UV);
+ if (sign)
+ high_thr = -high_thr;
+ *high_threshold = high_thr + vbatt_param.adc_gnd;
}
- do_div(high_thr, QPNP_ADC_625_UV);
- if (sign)
- high_thr = -high_thr;
- *high_threshold = high_thr + vbatt_param.adc_gnd;
pr_debug("high_volt:%d, low_volt:%d\n", param->high_thr,
param->low_thr);
@@ -1292,48 +1336,16 @@ int32_t qpnp_adc_vbatt_rscaler(struct qpnp_vadc_chip *chip,
*low_threshold);
return 0;
}
-EXPORT_SYMBOL(qpnp_adc_vbatt_rscaler);
+EXPORT_SYMBOL(qpnp_adc_absolute_rthr);
-int32_t qpnp_adc_absolute_rthr(struct qpnp_vadc_chip *chip,
+int32_t qpnp_adc_vbatt_rscaler(struct qpnp_vadc_chip *chip,
struct qpnp_adc_tm_btm_param *param,
uint32_t *low_threshold, uint32_t *high_threshold)
{
- struct qpnp_vadc_linear_graph vbatt_param;
- int rc = 0, sign = 0;
- int64_t low_thr = 0, high_thr = 0;
-
- rc = qpnp_get_vadc_gain_and_offset(chip, &vbatt_param, CALIB_ABSOLUTE);
- if (rc < 0)
- return rc;
-
- low_thr = (((param->low_thr) - QPNP_ADC_625_UV) * vbatt_param.dy);
- if (low_thr < 0) {
- sign = 1;
- low_thr = -low_thr;
- }
- do_div(low_thr, QPNP_ADC_625_UV);
- if (sign)
- low_thr = -low_thr;
- *low_threshold = low_thr + vbatt_param.adc_gnd;
-
- sign = 0;
- high_thr = (((param->high_thr) - QPNP_ADC_625_UV) * vbatt_param.dy);
- if (high_thr < 0) {
- sign = 1;
- high_thr = -high_thr;
- }
- do_div(high_thr, QPNP_ADC_625_UV);
- if (sign)
- high_thr = -high_thr;
- *high_threshold = high_thr + vbatt_param.adc_gnd;
-
- pr_debug("high_volt:%d, low_volt:%d\n", param->high_thr,
- param->low_thr);
- pr_debug("adc_code_high:%x, adc_code_low:%x\n", *high_threshold,
- *low_threshold);
- return 0;
+ return qpnp_adc_absolute_rthr(chip, param, low_threshold,
+ high_threshold);
}
-EXPORT_SYMBOL(qpnp_adc_absolute_rthr);
+EXPORT_SYMBOL(qpnp_adc_vbatt_rscaler);
int32_t qpnp_vadc_absolute_rthr(struct qpnp_vadc_chip *chip,
const struct qpnp_vadc_chan_properties *chan_prop,
@@ -1393,6 +1405,11 @@ int32_t qpnp_adc_btm_scaler(struct qpnp_vadc_chip *chip,
int64_t low_output = 0, high_output = 0;
int rc = 0;
+ if (param->adc_tm_hc) {
+ pr_err("Update scaling for VADC_TM_HC\n");
+ return -EINVAL;
+ }
+
qpnp_get_vadc_gain_and_offset(chip, &btm_param, CALIB_RATIOMETRIC);
pr_debug("warm_temp:%d and cool_temp:%d\n", param->high_temp,
@@ -1446,6 +1463,11 @@ int32_t qpnp_adc_qrd_skuh_btm_scaler(struct qpnp_vadc_chip *chip,
int64_t low_output = 0, high_output = 0;
int rc = 0;
+ if (param->adc_tm_hc) {
+ pr_err("Update scaling for VADC_TM_HC\n");
+ return -EINVAL;
+ }
+
qpnp_get_vadc_gain_and_offset(chip, &btm_param, CALIB_RATIOMETRIC);
pr_debug("warm_temp:%d and cool_temp:%d\n", param->high_temp,
@@ -1499,6 +1521,11 @@ int32_t qpnp_adc_qrd_skut1_btm_scaler(struct qpnp_vadc_chip *chip,
int64_t low_output = 0, high_output = 0;
int rc = 0;
+ if (param->adc_tm_hc) {
+ pr_err("Update scaling for VADC_TM_HC\n");
+ return -EINVAL;
+ }
+
qpnp_get_vadc_gain_and_offset(chip, &btm_param, CALIB_RATIOMETRIC);
pr_debug("warm_temp:%d and cool_temp:%d\n", param->high_temp,
@@ -1552,6 +1579,11 @@ int32_t qpnp_adc_smb_btm_rscaler(struct qpnp_vadc_chip *chip,
int64_t low_output = 0, high_output = 0;
int rc = 0;
+ if (param->adc_tm_hc) {
+ pr_err("Update scaling for VADC_TM_HC\n");
+ return -EINVAL;
+ }
+
qpnp_get_vadc_gain_and_offset(chip, &btm_param, CALIB_RATIOMETRIC);
pr_debug("warm_temp:%d and cool_temp:%d\n", param->high_temp,
diff --git a/drivers/hwmon/qpnp-adc-voltage.c b/drivers/hwmon/qpnp-adc-voltage.c
index f36bb933a03e..6ed947e5603b 100644
--- a/drivers/hwmon/qpnp-adc-voltage.c
+++ b/drivers/hwmon/qpnp-adc-voltage.c
@@ -1642,8 +1642,12 @@ static int32_t qpnp_vadc_calib_device(struct qpnp_vadc_chip *vadc)
vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dy =
(calib_read_1 - calib_read_2);
- vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dx
+ if (calib_type == CALIB_ABSOLUTE)
+ vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dx
= QPNP_ADC_625_UV;
+ else if (calib_type == ADC_HC_ABS_CAL)
+ vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dx
+ = QPNP_ADC_1P25_UV;
vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].adc_vref =
calib_read_1;
vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].adc_gnd =
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c
index 7e93f7654347..617c766f032e 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x.c
@@ -68,24 +68,8 @@ static bool etm4_arch_supported(u8 arch)
static int etm4_trace_id(struct coresight_device *csdev)
{
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
- unsigned long flags;
- int trace_id = -1;
- if (!drvdata->enable)
- return drvdata->trcid;
-
- pm_runtime_get_sync(drvdata->dev);
- spin_lock_irqsave(&drvdata->spinlock, flags);
-
- CS_UNLOCK(drvdata->base);
- trace_id = readl_relaxed(drvdata->base + TRCTRACEIDR);
- trace_id &= ETM_TRACEID_MASK;
- CS_LOCK(drvdata->base);
-
- spin_unlock_irqrestore(&drvdata->spinlock, flags);
- pm_runtime_put(drvdata->dev);
-
- return trace_id;
+ return drvdata->trcid;
}
static void etm4_enable_hw(void *info)
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index e0a50e814d44..10e50df1e6d5 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -1837,7 +1837,11 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
drvdata->size = SZ_1M;
drvdata->mem_size = drvdata->size;
- drvdata->memtype = TMC_ETR_MEM_TYPE_CONTIG;
+
+ if (of_property_read_bool(np, "arm,sg-enable"))
+ drvdata->memtype = TMC_ETR_MEM_TYPE_SG;
+ else
+ drvdata->memtype = TMC_ETR_MEM_TYPE_CONTIG;
drvdata->mem_type = drvdata->memtype;
} else {
drvdata->size = readl_relaxed(drvdata->base + TMC_RSZ) * 4;
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 93ad9df1f294..529edb16565a 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -1153,4 +1153,17 @@ config TOUCHSCREEN_GEN_VKEYS
To compile this driver as a module, choose M here: the
module will be called gen_vkeys.
+config TOUCHSCREEN_FT5X06
+ tristate "FocalTech touchscreens"
+ depends on I2C
+ help
+ Say Y here if you have a ft5X06 touchscreen.
+ Ft5x06 controllers are multi touch controllers which can
+ report 5 touches at a time.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ft5x06_ts.
+
endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index aaf7f587ed19..e04e787cea6e 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o
obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o
obj-$(CONFIG_TOUCHSCREEN_FT6236) += ft6236.o
+obj-$(CONFIG_TOUCHSCREEN_FT5X06) += ft5x06_ts.o
obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o
obj-$(CONFIG_TOUCHSCREEN_GEN_VKEYS) += gen_vkeys.o
diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c
new file mode 100644
index 000000000000..d619c1d06e9e
--- /dev/null
+++ b/drivers/input/touchscreen/ft5x06_ts.c
@@ -0,0 +1,654 @@
+/*
+ *
+ * FocalTech ft5x06 TouchScreen driver.
+ *
+ * Copyright (c) 2010 Focal tech Ltd.
+ * Copyright (c) 2012, Code Aurora Forum. 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.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/input/ft5x06_ts.h>
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+/* Early-suspend level */
+#define FT5X06_SUSPEND_LEVEL 1
+#endif
+
+#define CFG_MAX_TOUCH_POINTS 5
+
+#define FT_STARTUP_DLY 150
+#define FT_RESET_DLY 20
+
+#define FT_PRESS 0x7F
+#define FT_MAX_ID 0x0F
+#define FT_TOUCH_STEP 6
+#define FT_TOUCH_X_H_POS 3
+#define FT_TOUCH_X_L_POS 4
+#define FT_TOUCH_Y_H_POS 5
+#define FT_TOUCH_Y_L_POS 6
+#define FT_TOUCH_EVENT_POS 3
+#define FT_TOUCH_ID_POS 5
+
+#define POINT_READ_BUF (3 + FT_TOUCH_STEP * CFG_MAX_TOUCH_POINTS)
+
+/*register address*/
+#define FT5X06_REG_PMODE 0xA5
+#define FT5X06_REG_FW_VER 0xA6
+#define FT5X06_REG_POINT_RATE 0x88
+#define FT5X06_REG_THGROUP 0x80
+
+/* power register bits*/
+#define FT5X06_PMODE_ACTIVE 0x00
+#define FT5X06_PMODE_MONITOR 0x01
+#define FT5X06_PMODE_STANDBY 0x02
+#define FT5X06_PMODE_HIBERNATE 0x03
+
+#define FT5X06_VTG_MIN_UV 2600000
+#define FT5X06_VTG_MAX_UV 3300000
+#define FT5X06_I2C_VTG_MIN_UV 1800000
+#define FT5X06_I2C_VTG_MAX_UV 1800000
+
+struct ts_event {
+ u16 x[CFG_MAX_TOUCH_POINTS]; /*x coordinate */
+ u16 y[CFG_MAX_TOUCH_POINTS]; /*y coordinate */
+ /* touch event: 0 -- down; 1-- contact; 2 -- contact */
+ u8 touch_event[CFG_MAX_TOUCH_POINTS];
+ u8 finger_id[CFG_MAX_TOUCH_POINTS]; /*touch ID */
+ u16 pressure;
+ u8 touch_point;
+};
+
+struct ft5x06_ts_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct ts_event event;
+ const struct ft5x06_ts_platform_data *pdata;
+ struct regulator *vdd;
+ struct regulator *vcc_i2c;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+};
+
+static int ft5x06_i2c_read(struct i2c_client *client, char *writebuf,
+ int writelen, char *readbuf, int readlen)
+{
+ int ret;
+
+ if (writelen > 0) {
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = writelen,
+ .buf = writebuf,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = readlen,
+ .buf = readbuf,
+ },
+ };
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ if (ret < 0)
+ dev_err(&client->dev, "%s: i2c read error.\n",
+ __func__);
+ } else {
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = readlen,
+ .buf = readbuf,
+ },
+ };
+ ret = i2c_transfer(client->adapter, msgs, 1);
+ if (ret < 0)
+ dev_err(&client->dev, "%s:i2c read error.\n", __func__);
+ }
+ return ret;
+}
+
+static int ft5x06_i2c_write(struct i2c_client *client, char *writebuf,
+ int writelen)
+{
+ int ret;
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = writelen,
+ .buf = writebuf,
+ },
+ };
+ ret = i2c_transfer(client->adapter, msgs, 1);
+ if (ret < 0)
+ dev_err(&client->dev, "%s: i2c write error.\n", __func__);
+
+ return ret;
+}
+
+static void ft5x06_report_value(struct ft5x06_ts_data *data)
+{
+ struct ts_event *event = &data->event;
+ int i;
+ int fingerdown = 0;
+
+ for (i = 0; i < event->touch_point; i++) {
+ if (event->touch_event[i] == 0 || event->touch_event[i] == 2) {
+ event->pressure = FT_PRESS;
+ fingerdown++;
+ } else {
+ event->pressure = 0;
+ }
+
+ input_report_abs(data->input_dev, ABS_MT_POSITION_X,
+ event->x[i]);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_Y,
+ event->y[i]);
+ input_report_abs(data->input_dev, ABS_MT_PRESSURE,
+ event->pressure);
+ input_report_abs(data->input_dev, ABS_MT_TRACKING_ID,
+ event->finger_id[i]);
+ input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR,
+ event->pressure);
+ input_mt_sync(data->input_dev);
+ }
+
+ input_report_key(data->input_dev, BTN_TOUCH, !!fingerdown);
+ input_sync(data->input_dev);
+}
+
+static int ft5x06_handle_touchdata(struct ft5x06_ts_data *data)
+{
+ struct ts_event *event = &data->event;
+ int ret, i;
+ u8 buf[POINT_READ_BUF] = { 0 };
+ u8 pointid = FT_MAX_ID;
+
+ ret = ft5x06_i2c_read(data->client, buf, 1, buf, POINT_READ_BUF);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "%s read touchdata failed.\n",
+ __func__);
+ return ret;
+ }
+ memset(event, 0, sizeof(struct ts_event));
+
+ event->touch_point = 0;
+ for (i = 0; i < CFG_MAX_TOUCH_POINTS; i++) {
+ pointid = (buf[FT_TOUCH_ID_POS + FT_TOUCH_STEP * i]) >> 4;
+ if (pointid >= FT_MAX_ID)
+ break;
+
+ event->touch_point++;
+
+ event->x[i] =
+ (s16) (buf[FT_TOUCH_X_H_POS + FT_TOUCH_STEP * i] & 0x0F) <<
+ 8 | (s16) buf[FT_TOUCH_X_L_POS + FT_TOUCH_STEP * i];
+ event->y[i] =
+ (s16) (buf[FT_TOUCH_Y_H_POS + FT_TOUCH_STEP * i] & 0x0F) <<
+ 8 | (s16) buf[FT_TOUCH_Y_L_POS + FT_TOUCH_STEP * i];
+ event->touch_event[i] =
+ buf[FT_TOUCH_EVENT_POS + FT_TOUCH_STEP * i] >> 6;
+ event->finger_id[i] =
+ (buf[FT_TOUCH_ID_POS + FT_TOUCH_STEP * i]) >> 4;
+ }
+
+ ft5x06_report_value(data);
+
+ return 0;
+}
+
+static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id)
+{
+ struct ft5x06_ts_data *data = dev_id;
+ int rc;
+
+ rc = ft5x06_handle_touchdata(data);
+ if (rc)
+ pr_err("%s: handling touchdata failed\n", __func__);
+
+ return IRQ_HANDLED;
+}
+
+static int ft5x06_power_on(struct ft5x06_ts_data *data, bool on)
+{
+ int rc;
+
+ if (!on)
+ goto power_off;
+
+ rc = regulator_enable(data->vdd);
+ if (rc) {
+ dev_err(&data->client->dev,
+ "Regulator vdd enable failed rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = regulator_enable(data->vcc_i2c);
+ if (rc) {
+ dev_err(&data->client->dev,
+ "Regulator vcc_i2c enable failed rc=%d\n", rc);
+ regulator_disable(data->vdd);
+ }
+
+ return rc;
+
+power_off:
+ rc = regulator_disable(data->vdd);
+ if (rc) {
+ dev_err(&data->client->dev,
+ "Regulator vdd disable failed rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = regulator_disable(data->vcc_i2c);
+ if (rc) {
+ dev_err(&data->client->dev,
+ "Regulator vcc_i2c disable failed rc=%d\n", rc);
+ regulator_enable(data->vdd);
+ }
+
+ return rc;
+}
+
+static int ft5x06_power_init(struct ft5x06_ts_data *data, bool on)
+{
+ int rc;
+
+ if (!on)
+ goto pwr_deinit;
+
+ data->vdd = regulator_get(&data->client->dev, "vdd");
+ if (IS_ERR(data->vdd)) {
+ rc = PTR_ERR(data->vdd);
+ dev_err(&data->client->dev,
+ "Regulator get failed vdd rc=%d\n", rc);
+ return rc;
+ }
+
+ if (regulator_count_voltages(data->vdd) > 0) {
+ rc = regulator_set_voltage(data->vdd, FT5X06_VTG_MIN_UV,
+ FT5X06_VTG_MAX_UV);
+ if (rc) {
+ dev_err(&data->client->dev,
+ "Regulator set_vtg failed vdd rc=%d\n", rc);
+ goto reg_vdd_put;
+ }
+ }
+
+ data->vcc_i2c = regulator_get(&data->client->dev, "vcc_i2c");
+ if (IS_ERR(data->vcc_i2c)) {
+ rc = PTR_ERR(data->vcc_i2c);
+ dev_err(&data->client->dev,
+ "Regulator get failed vcc_i2c rc=%d\n", rc);
+ goto reg_vdd_set_vtg;
+ }
+
+ if (regulator_count_voltages(data->vcc_i2c) > 0) {
+ rc = regulator_set_voltage(data->vcc_i2c, FT5X06_I2C_VTG_MIN_UV,
+ FT5X06_I2C_VTG_MAX_UV);
+ if (rc) {
+ dev_err(&data->client->dev,
+ "Regulator set_vtg failed vcc_i2c rc=%d\n", rc);
+ goto reg_vcc_i2c_put;
+ }
+ }
+
+ return 0;
+
+reg_vcc_i2c_put:
+ regulator_put(data->vcc_i2c);
+reg_vdd_set_vtg:
+ if (regulator_count_voltages(data->vdd) > 0)
+ regulator_set_voltage(data->vdd, 0, FT5X06_VTG_MAX_UV);
+reg_vdd_put:
+ regulator_put(data->vdd);
+ return rc;
+
+pwr_deinit:
+ if (regulator_count_voltages(data->vdd) > 0)
+ regulator_set_voltage(data->vdd, 0, FT5X06_VTG_MAX_UV);
+
+ regulator_put(data->vdd);
+
+ if (regulator_count_voltages(data->vcc_i2c) > 0)
+ regulator_set_voltage(data->vcc_i2c, 0, FT5X06_I2C_VTG_MAX_UV);
+
+ regulator_put(data->vcc_i2c);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ft5x06_ts_suspend(struct device *dev)
+{
+ struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+ char txbuf[2];
+
+ disable_irq(data->client->irq);
+
+ if (gpio_is_valid(data->pdata->reset_gpio)) {
+ txbuf[0] = FT5X06_REG_PMODE;
+ txbuf[1] = FT5X06_PMODE_HIBERNATE;
+ ft5x06_i2c_write(data->client, txbuf, sizeof(txbuf));
+ }
+
+ return 0;
+}
+
+static int ft5x06_ts_resume(struct device *dev)
+{
+ struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+
+ if (gpio_is_valid(data->pdata->reset_gpio)) {
+ gpio_set_value_cansleep(data->pdata->reset_gpio, 0);
+ msleep(FT_RESET_DLY);
+ gpio_set_value_cansleep(data->pdata->reset_gpio, 1);
+ }
+ enable_irq(data->client->irq);
+
+ return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void ft5x06_ts_early_suspend(struct early_suspend *handler)
+{
+ struct ft5x06_ts_data *data = container_of(handler,
+ struct ft5x06_ts_data,
+ early_suspend);
+
+ ft5x06_ts_suspend(&data->client->dev);
+}
+
+static void ft5x06_ts_late_resume(struct early_suspend *handler)
+{
+ struct ft5x06_ts_data *data = container_of(handler,
+ struct ft5x06_ts_data,
+ early_suspend);
+
+ ft5x06_ts_resume(&data->client->dev);
+}
+#endif
+
+static const struct dev_pm_ops ft5x06_ts_pm_ops = {
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ .suspend = ft5x06_ts_suspend,
+ .resume = ft5x06_ts_resume,
+#endif
+};
+#endif
+
+static int ft5x06_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct ft5x06_ts_platform_data *pdata = client->dev.platform_data;
+ struct ft5x06_ts_data *data;
+ struct input_dev *input_dev;
+ u8 reg_value;
+ u8 reg_addr;
+ int err;
+
+ if (!pdata) {
+ dev_err(&client->dev, "Invalid pdata\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "I2C not supported\n");
+ return -ENODEV;
+ }
+
+ data = kzalloc(sizeof(struct ft5x06_ts_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ err = -ENOMEM;
+ dev_err(&client->dev, "failed to allocate input device\n");
+ goto free_mem;
+ }
+
+ data->input_dev = input_dev;
+ data->client = client;
+ data->pdata = pdata;
+
+ input_dev->name = "ft5x06_ts";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+
+ input_set_drvdata(input_dev, data);
+ i2c_set_clientdata(client, data);
+
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+ __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
+ pdata->x_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
+ pdata->y_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0,
+ CFG_MAX_TOUCH_POINTS, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, FT_PRESS, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, FT_PRESS, 0, 0);
+
+ err = input_register_device(input_dev);
+ if (err) {
+ dev_err(&client->dev, "Input device registration failed\n");
+ goto free_inputdev;
+ }
+
+ if (pdata->power_init) {
+ err = pdata->power_init(true);
+ if (err) {
+ dev_err(&client->dev, "power init failed");
+ goto unreg_inputdev;
+ }
+ } else {
+ err = ft5x06_power_init(data, true);
+ if (err) {
+ dev_err(&client->dev, "power init failed");
+ goto unreg_inputdev;
+ }
+ }
+
+ if (pdata->power_on) {
+ err = pdata->power_on(true);
+ if (err) {
+ dev_err(&client->dev, "power on failed");
+ goto pwr_deinit;
+ }
+ } else {
+ err = ft5x06_power_on(data, true);
+ if (err) {
+ dev_err(&client->dev, "power on failed");
+ goto pwr_deinit;
+ }
+ }
+
+ if (gpio_is_valid(pdata->irq_gpio)) {
+ err = gpio_request(pdata->irq_gpio, "ft5x06_irq_gpio");
+ if (err) {
+ dev_err(&client->dev, "irq gpio request failed");
+ goto pwr_off;
+ }
+ err = gpio_direction_input(pdata->irq_gpio);
+ if (err) {
+ dev_err(&client->dev,
+ "set_direction for irq gpio failed\n");
+ goto free_irq_gpio;
+ }
+ }
+
+ if (gpio_is_valid(pdata->reset_gpio)) {
+ err = gpio_request(pdata->reset_gpio, "ft5x06_reset_gpio");
+ if (err) {
+ dev_err(&client->dev, "reset gpio request failed");
+ goto free_irq_gpio;
+ }
+
+ err = gpio_direction_output(pdata->reset_gpio, 0);
+ if (err) {
+ dev_err(&client->dev,
+ "set_direction for reset gpio failed\n");
+ goto free_reset_gpio;
+ }
+ msleep(FT_RESET_DLY);
+ gpio_set_value_cansleep(data->pdata->reset_gpio, 1);
+ }
+
+ /* make sure CTP already finish startup process */
+ msleep(FT_STARTUP_DLY);
+
+ /*get some register information */
+ reg_addr = FT5X06_REG_FW_VER;
+ err = ft5x06_i2c_read(client, &reg_addr, 1, &reg_value, 1);
+ if (err)
+ dev_err(&client->dev, "version read failed");
+
+ dev_info(&client->dev, "[FTS] Firmware version = 0x%x\n", reg_value);
+
+ reg_addr = FT5X06_REG_POINT_RATE;
+ ft5x06_i2c_read(client, &reg_addr, 1, &reg_value, 1);
+ if (err)
+ dev_err(&client->dev, "report rate read failed");
+ dev_info(&client->dev, "[FTS] report rate is %dHz.\n", reg_value * 10);
+
+ reg_addr = FT5X06_REG_THGROUP;
+ err = ft5x06_i2c_read(client, &reg_addr, 1, &reg_value, 1);
+ if (err)
+ dev_err(&client->dev, "threshold read failed");
+ dev_dbg(&client->dev, "[FTS] touch threshold is %d.\n", reg_value * 4);
+
+ err = request_threaded_irq(client->irq, NULL,
+ ft5x06_ts_interrupt, pdata->irqflags,
+ client->dev.driver->name, data);
+ if (err) {
+ dev_err(&client->dev, "request irq failed\n");
+ goto free_reset_gpio;
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN +
+ FT5X06_SUSPEND_LEVEL;
+ data->early_suspend.suspend = ft5x06_ts_early_suspend;
+ data->early_suspend.resume = ft5x06_ts_late_resume;
+ register_early_suspend(&data->early_suspend);
+#endif
+
+ return 0;
+
+free_reset_gpio:
+ if (gpio_is_valid(pdata->reset_gpio))
+ gpio_free(pdata->reset_gpio);
+free_irq_gpio:
+ if (gpio_is_valid(pdata->irq_gpio))
+ gpio_free(pdata->reset_gpio);
+pwr_off:
+ if (pdata->power_on)
+ pdata->power_on(false);
+ else
+ ft5x06_power_on(data, false);
+pwr_deinit:
+ if (pdata->power_init)
+ pdata->power_init(false);
+ else
+ ft5x06_power_init(data, false);
+unreg_inputdev:
+ input_unregister_device(input_dev);
+ input_dev = NULL;
+free_inputdev:
+ input_free_device(input_dev);
+free_mem:
+ kfree(data);
+ return err;
+}
+
+static int ft5x06_ts_remove(struct i2c_client *client)
+{
+ struct ft5x06_ts_data *data = i2c_get_clientdata(client);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&data->early_suspend);
+#endif
+ free_irq(client->irq, data);
+
+ if (gpio_is_valid(data->pdata->reset_gpio))
+ gpio_free(data->pdata->reset_gpio);
+
+ if (gpio_is_valid(data->pdata->irq_gpio))
+ gpio_free(data->pdata->reset_gpio);
+
+ if (data->pdata->power_on)
+ data->pdata->power_on(false);
+ else
+ ft5x06_power_on(data, false);
+
+ if (data->pdata->power_init)
+ data->pdata->power_init(false);
+ else
+ ft5x06_power_init(data, false);
+
+ input_unregister_device(data->input_dev);
+ kfree(data);
+
+ return 0;
+}
+
+static const struct i2c_device_id ft5x06_ts_id[] = {
+ {"ft5x06_ts", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ft5x06_ts_id);
+
+static struct i2c_driver ft5x06_ts_driver = {
+ .probe = ft5x06_ts_probe,
+ .remove = __devexit_p(ft5x06_ts_remove),
+ .driver = {
+ .name = "ft5x06_ts",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &ft5x06_ts_pm_ops,
+#endif
+ },
+ .id_table = ft5x06_ts_id,
+};
+
+static int __init ft5x06_ts_init(void)
+{
+ return i2c_add_driver(&ft5x06_ts_driver);
+}
+module_init(ft5x06_ts_init);
+
+static void __exit ft5x06_ts_exit(void)
+{
+ i2c_del_driver(&ft5x06_ts_driver);
+}
+module_exit(ft5x06_ts_exit);
+
+MODULE_DESCRIPTION("FocalTech ft5x06 TouchScreen driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.c b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.c
index 57e389b4c497..b8840115b674 100644
--- a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.c
+++ b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.c
@@ -1009,15 +1009,9 @@ static int msm_jpegdma_s_crop(struct file *file, void *fh,
if (crop->c.width % formats[ctx->format_idx].h_align)
return -EINVAL;
- if (crop->c.left % formats[ctx->format_idx].h_align)
- return -EINVAL;
-
if (crop->c.height % formats[ctx->format_idx].v_align)
return -EINVAL;
- if (crop->c.top % formats[ctx->format_idx].v_align)
- return -EINVAL;
-
ctx->crop = crop->c;
if (atomic_read(&ctx->active))
ret = msm_jpegdma_update_hw_config(ctx);
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.h b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.h
index 0a9cab6e4322..6a1205daf1d2 100644
--- a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.h
+++ b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.h
@@ -36,9 +36,9 @@
/* Dma input output size limitations */
#define MSM_JPEGDMA_MAX_WIDTH 65536
-#define MSM_JPEGDMA_MIN_WIDTH 32
+#define MSM_JPEGDMA_MIN_WIDTH 8
#define MSM_JPEGDMA_MAX_HEIGHT 65536
-#define MSM_JPEGDMA_MIN_HEIGHT 32
+#define MSM_JPEGDMA_MIN_HEIGHT 8
#define MSM_JPEGDMA_STRIDE_ALIGN 8
/*
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 273728482227..c994f7e00a16 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -4152,41 +4152,80 @@ static int qseecom_reentrancy_send_resp(struct qseecom_dev_handle *data)
return 0;
}
-static int __qseecom_send_modfd_resp(struct qseecom_dev_handle *data,
- void __user *argp, bool is_64bit_addr)
+static int __validate_send_modfd_resp_inputs(struct qseecom_dev_handle *data,
+ struct qseecom_send_modfd_listener_resp *resp,
+ struct qseecom_registered_listener_list *this_lstnr)
{
- struct qseecom_send_modfd_listener_resp resp;
int i;
- struct qseecom_registered_listener_list *this_lstnr = NULL;
- if (copy_from_user(&resp, argp, sizeof(resp))) {
- pr_err("copy_from_user failed");
+ if (!data || !resp || !this_lstnr) {
+ pr_err("listener handle or resp msg is null\n");
return -EINVAL;
}
- this_lstnr = __qseecom_find_svc(data->listener.id);
- if (this_lstnr == NULL)
+
+ if (resp->resp_buf_ptr == NULL) {
+ pr_err("resp buffer is null\n");
+ return -EINVAL;
+ }
+ /* validate resp buf length */
+ if ((resp->resp_len == 0) ||
+ (resp->resp_len > this_lstnr->sb_length)) {
+ pr_err("resp buf length %d not valid\n", resp->resp_len);
return -EINVAL;
+ }
- if (resp.resp_buf_ptr == NULL) {
- pr_err("Invalid resp_buf_ptr\n");
+ if ((uintptr_t)resp->resp_buf_ptr > (ULONG_MAX - resp->resp_len)) {
+ pr_err("Integer overflow in resp_len & resp_buf\n");
+ return -EINVAL;
+ }
+ if ((uintptr_t)this_lstnr->user_virt_sb_base >
+ (ULONG_MAX - this_lstnr->sb_length)) {
+ pr_err("Integer overflow in user_virt_sb_base & sb_length\n");
+ return -EINVAL;
+ }
+ /* validate resp buf */
+ if (((uintptr_t)resp->resp_buf_ptr <
+ (uintptr_t)this_lstnr->user_virt_sb_base) ||
+ ((uintptr_t)resp->resp_buf_ptr >=
+ ((uintptr_t)this_lstnr->user_virt_sb_base +
+ this_lstnr->sb_length)) ||
+ (((uintptr_t)resp->resp_buf_ptr + resp->resp_len) >
+ ((uintptr_t)this_lstnr->user_virt_sb_base +
+ this_lstnr->sb_length))) {
+ pr_err("resp buf is out of shared buffer region\n");
return -EINVAL;
}
+
/* validate offsets */
for (i = 0; i < MAX_ION_FD; i++) {
- if (resp.ifd_data[i].cmd_buf_offset >= resp.resp_len) {
+ if (resp->ifd_data[i].cmd_buf_offset >= resp->resp_len) {
pr_err("Invalid offset %d = 0x%x\n",
- i, resp.ifd_data[i].cmd_buf_offset);
+ i, resp->ifd_data[i].cmd_buf_offset);
return -EINVAL;
}
}
- if ((resp.resp_buf_ptr < this_lstnr->user_virt_sb_base) ||
- ((uintptr_t)resp.resp_buf_ptr >=
- ((uintptr_t)this_lstnr->user_virt_sb_base +
- this_lstnr->sb_length))) {
- pr_err("resp_buf_ptr address not within shared buffer\n");
+ return 0;
+}
+
+static int __qseecom_send_modfd_resp(struct qseecom_dev_handle *data,
+ void __user *argp, bool is_64bit_addr)
+{
+ struct qseecom_send_modfd_listener_resp resp;
+ struct qseecom_registered_listener_list *this_lstnr = NULL;
+
+ if (copy_from_user(&resp, argp, sizeof(resp))) {
+ pr_err("copy_from_user failed");
return -EINVAL;
}
+
+ this_lstnr = __qseecom_find_svc(data->listener.id);
+ if (this_lstnr == NULL)
+ return -EINVAL;
+
+ if (__validate_send_modfd_resp_inputs(data, &resp, this_lstnr))
+ return -EINVAL;
+
resp.resp_buf_ptr = this_lstnr->sb_virt +
(uintptr_t)(resp.resp_buf_ptr - this_lstnr->user_virt_sb_base);
diff --git a/drivers/misc/uid_stat.c b/drivers/misc/uid_stat.c
index 8b8c9a22360b..2141124a6c12 100644
--- a/drivers/misc/uid_stat.c
+++ b/drivers/misc/uid_stat.c
@@ -20,7 +20,6 @@
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/stat.h>
@@ -39,44 +38,65 @@ struct uid_stat {
};
static struct uid_stat *find_uid_stat(uid_t uid) {
+ unsigned long flags;
struct uid_stat *entry;
+ spin_lock_irqsave(&uid_lock, flags);
list_for_each_entry(entry, &uid_list, link) {
if (entry->uid == uid) {
+ spin_unlock_irqrestore(&uid_lock, flags);
return entry;
}
}
+ spin_unlock_irqrestore(&uid_lock, flags);
return NULL;
}
-static int uid_stat_atomic_int_show(struct seq_file *m, void *v)
+static int tcp_snd_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
{
+ int len;
unsigned int bytes;
- atomic_t *counter = m->private;
-
- bytes = (unsigned int) (atomic_read(counter) + INT_MIN);
- seq_printf(m, "%u\n", bytes);
- return seq_has_overflowed(m) ? -ENOSPC : 0;
+ char *p = page;
+ struct uid_stat *uid_entry = (struct uid_stat *) data;
+ if (!data)
+ return 0;
+
+ bytes = (unsigned int) (atomic_read(&uid_entry->tcp_snd) + INT_MIN);
+ p += sprintf(p, "%u\n", bytes);
+ len = (p - page) - off;
+ *eof = (len <= count) ? 1 : 0;
+ *start = page + off;
+ return len;
}
-static int uid_stat_read_atomic_int_open(struct inode *inode, struct file *file)
+static int tcp_rcv_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
{
- return single_open(file, uid_stat_atomic_int_show, PDE_DATA(inode));
+ int len;
+ unsigned int bytes;
+ char *p = page;
+ struct uid_stat *uid_entry = (struct uid_stat *) data;
+ if (!data)
+ return 0;
+
+ bytes = (unsigned int) (atomic_read(&uid_entry->tcp_rcv) + INT_MIN);
+ p += sprintf(p, "%u\n", bytes);
+ len = (p - page) - off;
+ *eof = (len <= count) ? 1 : 0;
+ *start = page + off;
+ return len;
}
-static const struct file_operations uid_stat_read_atomic_int_fops = {
- .open = uid_stat_read_atomic_int_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release,
-};
-
/* Create a new entry for tracking the specified uid. */
static struct uid_stat *create_stat(uid_t uid) {
+ unsigned long flags;
+ char uid_s[32];
struct uid_stat *new_uid;
+ struct proc_dir_entry *entry;
+
/* Create the uid stat struct and append it to the list. */
- new_uid = kmalloc(sizeof(struct uid_stat), GFP_ATOMIC);
- if (!new_uid)
+ if ((new_uid = kmalloc(sizeof(struct uid_stat), GFP_KERNEL)) == NULL)
return NULL;
new_uid->uid = uid;
@@ -84,48 +104,30 @@ static struct uid_stat *create_stat(uid_t uid) {
atomic_set(&new_uid->tcp_rcv, INT_MIN);
atomic_set(&new_uid->tcp_snd, INT_MIN);
+ spin_lock_irqsave(&uid_lock, flags);
list_add_tail(&new_uid->link, &uid_list);
- return new_uid;
-}
+ spin_unlock_irqrestore(&uid_lock, flags);
-static void create_stat_proc(struct uid_stat *new_uid)
-{
- char uid_s[32];
- struct proc_dir_entry *entry;
- sprintf(uid_s, "%d", new_uid->uid);
+ sprintf(uid_s, "%d", uid);
entry = proc_mkdir(uid_s, parent);
/* Keep reference to uid_stat so we know what uid to read stats from. */
- proc_create_data("tcp_snd", S_IRUGO, entry,
- &uid_stat_read_atomic_int_fops, &new_uid->tcp_snd);
+ create_proc_read_entry("tcp_snd", S_IRUGO, entry , tcp_snd_read_proc,
+ (void *) new_uid);
- proc_create_data("tcp_rcv", S_IRUGO, entry,
- &uid_stat_read_atomic_int_fops, &new_uid->tcp_rcv);
-}
+ create_proc_read_entry("tcp_rcv", S_IRUGO, entry, tcp_rcv_read_proc,
+ (void *) new_uid);
-static struct uid_stat *find_or_create_uid_stat(uid_t uid)
-{
- struct uid_stat *entry;
- unsigned long flags;
- spin_lock_irqsave(&uid_lock, flags);
- entry = find_uid_stat(uid);
- if (entry) {
- spin_unlock_irqrestore(&uid_lock, flags);
- return entry;
- }
- entry = create_stat(uid);
- spin_unlock_irqrestore(&uid_lock, flags);
- if (entry)
- create_stat_proc(entry);
- return entry;
+ return new_uid;
}
int uid_stat_tcp_snd(uid_t uid, int size) {
struct uid_stat *entry;
activity_stats_update();
- entry = find_or_create_uid_stat(uid);
- if (!entry)
- return -1;
+ if ((entry = find_uid_stat(uid)) == NULL &&
+ ((entry = create_stat(uid)) == NULL)) {
+ return -1;
+ }
atomic_add(size, &entry->tcp_snd);
return 0;
}
@@ -133,9 +135,10 @@ int uid_stat_tcp_snd(uid_t uid, int size) {
int uid_stat_tcp_rcv(uid_t uid, int size) {
struct uid_stat *entry;
activity_stats_update();
- entry = find_or_create_uid_stat(uid);
- if (!entry)
- return -1;
+ if ((entry = find_uid_stat(uid)) == NULL &&
+ ((entry = create_stat(uid)) == NULL)) {
+ return -1;
+ }
atomic_add(size, &entry->tcp_rcv);
return 0;
}
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
index 11daafe43d7d..8e583203abda 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
@@ -162,6 +162,12 @@ struct ipa3_usb_transport_type_ctx {
void *user_data;
enum ipa3_usb_state state;
struct finish_suspend_work_context finish_suspend_work;
+ struct ipa_usb_xdci_chan_params ch_params;
+};
+
+struct ipa3_usb_smmu_reg_map {
+ int cnt;
+ phys_addr_t addr;
};
struct ipa3_usb_context {
@@ -179,6 +185,7 @@ struct ipa3_usb_context {
ttype_ctx[IPA_USB_TRANSPORT_MAX];
struct dentry *dfile_state_info;
struct dentry *dent;
+ struct ipa3_usb_smmu_reg_map smmu_reg_map;
};
enum ipa3_usb_op {
@@ -1112,6 +1119,74 @@ static bool ipa3_usb_check_chan_params(struct ipa_usb_xdci_chan_params *params)
return true;
}
+static int ipa3_usb_smmu_map_xdci_channel(
+ struct ipa_usb_xdci_chan_params *params, bool map)
+{
+ int result;
+ u32 gevntcount_r = rounddown(params->gevntcount_low_addr, PAGE_SIZE);
+ u32 xfer_scratch_r =
+ rounddown(params->xfer_scratch.depcmd_low_addr, PAGE_SIZE);
+
+ if (gevntcount_r != xfer_scratch_r) {
+ IPA_USB_ERR("No support more than 1 page map for USB regs\n");
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ if (map) {
+ if (ipa3_usb_ctx->smmu_reg_map.cnt == 0) {
+ ipa3_usb_ctx->smmu_reg_map.addr = gevntcount_r;
+ result = ipa3_smmu_map_peer_reg(
+ ipa3_usb_ctx->smmu_reg_map.addr, true);
+ if (result) {
+ IPA_USB_ERR("failed to map USB regs %d\n",
+ result);
+ return result;
+ }
+ } else {
+ if (gevntcount_r != ipa3_usb_ctx->smmu_reg_map.addr) {
+ IPA_USB_ERR(
+ "No support for map different reg\n");
+ return -EINVAL;
+ }
+ }
+ ipa3_usb_ctx->smmu_reg_map.cnt++;
+ } else {
+ if (gevntcount_r != ipa3_usb_ctx->smmu_reg_map.addr) {
+ IPA_USB_ERR(
+ "No support for map different reg\n");
+ return -EINVAL;
+ }
+
+ if (ipa3_usb_ctx->smmu_reg_map.cnt == 1) {
+ result = ipa3_smmu_map_peer_reg(
+ ipa3_usb_ctx->smmu_reg_map.addr, false);
+ if (result) {
+ IPA_USB_ERR("failed to unmap USB regs %d\n",
+ result);
+ return result;
+ }
+ }
+ ipa3_usb_ctx->smmu_reg_map.cnt--;
+ }
+
+ result = ipa3_smmu_map_peer_buff(params->xfer_ring_base_addr_iova,
+ params->xfer_ring_base_addr, params->xfer_ring_len, map);
+ if (result) {
+ IPA_USB_ERR("failed to map Xfer ring %d\n", result);
+ return result;
+ }
+
+ result = ipa3_smmu_map_peer_buff(params->data_buff_base_addr_iova,
+ params->data_buff_base_addr, params->data_buff_base_len, map);
+ if (result) {
+ IPA_USB_ERR("failed to map TRBs buff %d\n", result);
+ return result;
+ }
+
+ return 0;
+}
+
static int ipa3_usb_request_xdci_channel(
struct ipa_usb_xdci_chan_params *params,
struct ipa_req_chan_out_params *out_params)
@@ -1186,6 +1261,16 @@ static int ipa3_usb_request_xdci_channel(
default:
break;
}
+
+ result = ipa3_usb_smmu_map_xdci_channel(params, true);
+ if (result) {
+ IPA_USB_ERR("failed to smmu map %d\n", result);
+ return result;
+ }
+
+ /* store channel params for SMMU unmap */
+ ipa3_usb_ctx->ttype_ctx[ttype].ch_params = *params;
+
chan_params.keep_ipa_awake = params->keep_ipa_awake;
chan_params.evt_ring_params.intf = GSI_EVT_CHTYPE_XDCI_EV;
chan_params.evt_ring_params.intr = GSI_INTR_IRQ;
@@ -1243,6 +1328,7 @@ static int ipa3_usb_request_xdci_channel(
result = ipa3_request_gsi_channel(&chan_params, out_params);
if (result) {
IPA_USB_ERR("failed to allocate GSI channel\n");
+ ipa3_usb_smmu_map_xdci_channel(params, false);
return result;
}
@@ -1273,6 +1359,9 @@ static int ipa3_usb_release_xdci_channel(u32 clnt_hdl,
return result;
}
+ result = ipa3_usb_smmu_map_xdci_channel(
+ &ipa3_usb_ctx->ttype_ctx[ttype].ch_params, false);
+
/* Change ipa_usb state to INITIALIZED */
if (!ipa3_usb_set_state(IPA_USB_INITIALIZED, false, ttype))
IPA_USB_ERR("failed to change state to initialized\n");
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c
index 1f9fd7a38a37..cb808bd2a8b7 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c
@@ -1524,7 +1524,7 @@ static void ipa_free_buffer(void *user1, int user2)
kfree(user1);
}
-static int ipa_q6_pipe_delay(bool zip_pipes)
+int ipa_q6_pipe_delay(bool zip_pipes)
{
u32 reg_val = 0;
int client_idx;
@@ -1911,14 +1911,14 @@ int ipa_q6_pre_shutdown_cleanup(void)
BUG();
IPA_ACTIVE_CLIENTS_INC_SPECIAL("Q6");
+
/*
- * pipe delay and holb discard for ZIP pipes are handled
- * in post shutdown callback.
+ * Do not delay Q6 pipes here. This may result in IPA reading a
+ * DMA_TASK with lock bit set and then Q6 pipe delay is set. In this
+ * situation IPA will be remain locked as the DMA_TASK with unlock
+ * bit will not be read by IPA as pipe delay is enabled. IPA uC will
+ * wait for pipe to be empty before issuing a BAM pipe reset.
*/
- if (ipa_q6_pipe_delay(false)) {
- IPAERR("Failed to delay Q6 pipes\n");
- BUG();
- }
if (ipa_q6_monitor_holb_mitigation(false)) {
IPAERR("Failed to disable HOLB monitroing on Q6 pipes\n");
@@ -1958,13 +1958,13 @@ int ipa_q6_post_shutdown_cleanup(void)
int res;
/*
- * pipe delay and holb discard for ZIP pipes are handled in
- * post shutdown.
+ * Do not delay Q6 pipes here. This may result in IPA reading a
+ * DMA_TASK with lock bit set and then Q6 pipe delay is set. In this
+ * situation IPA will be remain locked as the DMA_TASK with unlock
+ * bit will not be read by IPA as pipe delay is enabled. IPA uC will
+ * wait for pipe to be empty before issuing a BAM pipe reset.
*/
- if (ipa_q6_pipe_delay(true)) {
- IPAERR("Failed to delay Q6 ZIP pipes\n");
- BUG();
- }
+
if (ipa_q6_avoid_holb(true)) {
IPAERR("Failed to set HOLB on Q6 ZIP pipes\n");
BUG();
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index c553be1ad717..3dd9738f67c7 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -37,6 +37,7 @@
#include <linux/hashtable.h>
#include <linux/hash.h>
#include <soc/qcom/subsystem_restart.h>
+#include <soc/qcom/smem.h>
#define IPA_SUBSYSTEM_NAME "ipa_fws"
#include "ipa_i.h"
#include "../ipa_rm_i.h"
@@ -75,6 +76,17 @@
#define IPA3_ACTIVE_CLIENT_LOG_TYPE_RESOURCE 2
#define IPA3_ACTIVE_CLIENT_LOG_TYPE_SPECIAL 3
+#define IPA_SMEM_SIZE (8 * 1024)
+
+/* round addresses for closes page per SMMU requirements */
+#define IPA_SMMU_ROUND_TO_PAGE(iova, pa, size, iova_p, pa_p, size_p) \
+ do { \
+ (iova_p) = rounddown((iova), PAGE_SIZE); \
+ (pa_p) = rounddown((pa), PAGE_SIZE); \
+ (size_p) = roundup((size) + (pa) - (pa_p), PAGE_SIZE); \
+ } while (0)
+
+
/* The relative location in /lib/firmware where the FWs will reside */
#define IPA_FWS_PATH "ipa/ipa_fws.elf"
@@ -4813,6 +4825,10 @@ static int ipa_smmu_ap_cb_probe(struct device *dev)
int fast = 1;
int bypass = 1;
u32 iova_ap_mapping[2];
+ u32 add_map_size;
+ const u32 *add_map;
+ void *smem_addr;
+ int i;
IPADBG("AP CB probe: sub pdev=%p\n", dev);
@@ -4902,6 +4918,55 @@ static int ipa_smmu_ap_cb_probe(struct device *dev)
return result;
}
+ add_map = of_get_property(dev->of_node,
+ "qcom,additional-mapping", &add_map_size);
+ if (add_map) {
+ /* mapping size is an array of 3-tuple of u32 */
+ if (add_map_size % (3 * sizeof(u32))) {
+ IPAERR("wrong additional mapping format\n");
+ cb->valid = false;
+ return -EFAULT;
+ }
+
+ /* iterate of each entry of the additional mapping array */
+ for (i = 0; i < add_map_size / sizeof(u32); i += 3) {
+ u32 iova = be32_to_cpu(add_map[i]);
+ u32 pa = be32_to_cpu(add_map[i + 1]);
+ u32 size = be32_to_cpu(add_map[i + 2]);
+ unsigned long iova_p;
+ phys_addr_t pa_p;
+ u32 size_p;
+
+ IPA_SMMU_ROUND_TO_PAGE(iova, pa, size,
+ iova_p, pa_p, size_p);
+ IPADBG("mapping 0x%lx to 0x%pa size %d\n",
+ iova_p, &pa_p, size_p);
+ ipa3_iommu_map(cb->mapping->domain,
+ iova_p, pa_p, size_p,
+ IOMMU_READ | IOMMU_WRITE | IOMMU_DEVICE);
+ }
+ }
+
+ /* map SMEM memory for IPA table accesses */
+ smem_addr = smem_alloc(SMEM_IPA_FILTER_TABLE, IPA_SMEM_SIZE,
+ SMEM_MODEM, 0);
+ if (smem_addr) {
+ phys_addr_t iova = smem_virt_to_phys(smem_addr);
+ phys_addr_t pa = iova;
+ unsigned long iova_p;
+ phys_addr_t pa_p;
+ u32 size_p;
+
+ IPA_SMMU_ROUND_TO_PAGE(iova, pa, IPA_SMEM_SIZE,
+ iova_p, pa_p, size_p);
+ IPADBG("mapping 0x%lx to 0x%pa size %d\n",
+ iova_p, &pa_p, size_p);
+ ipa3_iommu_map(cb->mapping->domain,
+ iova_p, pa_p, size_p,
+ IOMMU_READ | IOMMU_WRITE | IOMMU_DEVICE);
+ }
+
+
smmu_info.present = true;
if (!ipa3_bus_scale_table)
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
index 7b0376ecba7e..8326c3fdd9d1 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
@@ -1055,6 +1055,83 @@ static bool ipa3_is_legal_params(struct ipa_request_gsi_channel_params *params)
return true;
}
+int ipa3_smmu_map_peer_reg(phys_addr_t phys_addr, bool map)
+{
+ struct iommu_domain *smmu_domain;
+ int res;
+
+ if (ipa3_ctx->smmu_s1_bypass)
+ return 0;
+
+ smmu_domain = ipa3_get_smmu_domain();
+ if (!smmu_domain) {
+ IPAERR("invalid smmu domain\n");
+ return -EINVAL;
+ }
+
+ if (map) {
+ res = ipa3_iommu_map(smmu_domain, phys_addr, phys_addr,
+ PAGE_SIZE, IOMMU_READ | IOMMU_WRITE | IOMMU_DEVICE);
+ } else {
+ res = iommu_unmap(smmu_domain, phys_addr, PAGE_SIZE);
+ res = (res != PAGE_SIZE);
+ }
+ if (res) {
+ IPAERR("Fail to %s reg 0x%pa\n", map ? "map" : "unmap",
+ &phys_addr);
+ return -EINVAL;
+ }
+
+ IPADBG("Peer reg 0x%pa %s\n", &phys_addr, map ? "map" : "unmap");
+
+ return 0;
+}
+
+int ipa3_smmu_map_peer_buff(u64 iova, phys_addr_t phys_addr, u32 size, bool map)
+{
+ struct iommu_domain *smmu_domain;
+ int res;
+
+ if (ipa3_ctx->smmu_s1_bypass)
+ return 0;
+
+ smmu_domain = ipa3_get_smmu_domain();
+ if (!smmu_domain) {
+ IPAERR("invalid smmu domain\n");
+ return -EINVAL;
+ }
+
+ if (map) {
+ res = ipa3_iommu_map(smmu_domain,
+ rounddown(iova, PAGE_SIZE),
+ rounddown(phys_addr, PAGE_SIZE),
+ roundup(size + iova - rounddown(iova, PAGE_SIZE),
+ PAGE_SIZE),
+ IOMMU_READ | IOMMU_WRITE);
+ if (res) {
+ IPAERR("Fail to map 0x%llx->0x%pa\n", iova, &phys_addr);
+ return -EINVAL;
+ }
+ } else {
+ res = iommu_unmap(smmu_domain,
+ rounddown(iova, PAGE_SIZE),
+ roundup(size + iova - rounddown(iova, PAGE_SIZE),
+ PAGE_SIZE));
+ if (res != roundup(size + iova - rounddown(iova, PAGE_SIZE),
+ PAGE_SIZE)) {
+ IPAERR("Fail to unmap 0x%llx->0x%pa\n",
+ iova, &phys_addr);
+ return -EINVAL;
+ }
+ }
+
+ IPADBG("Peer buff %s 0x%llx->0x%pa\n", map ? "map" : "unmap",
+ iova, &phys_addr);
+
+ return 0;
+}
+
+
int ipa3_request_gsi_channel(struct ipa_request_gsi_channel_params *params,
struct ipa_req_chan_out_params *out_params)
{
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
index 82df3768ba26..4c600c6131e9 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
@@ -1914,6 +1914,7 @@ static void ipa3_replenish_wlan_rx_cache(struct ipa3_sys_context *sys)
gsi_xfer_elem_one.addr = rx_pkt->data.dma_addr;
gsi_xfer_elem_one.len = IPA_WLAN_RX_BUFF_SZ;
gsi_xfer_elem_one.flags |= GSI_XFER_FLAG_EOT;
+ gsi_xfer_elem_one.flags |= GSI_XFER_FLAG_EOB;
gsi_xfer_elem_one.type = GSI_XFER_ELEM_DATA;
gsi_xfer_elem_one.xfer_user_data = rx_pkt;
@@ -2101,6 +2102,7 @@ static void ipa3_replenish_rx_cache(struct ipa3_sys_context *sys)
gsi_xfer_elem_one.addr = rx_pkt->data.dma_addr;
gsi_xfer_elem_one.len = sys->rx_buff_sz;
gsi_xfer_elem_one.flags |= GSI_XFER_FLAG_EOT;
+ gsi_xfer_elem_one.flags |= GSI_XFER_FLAG_EOB;
gsi_xfer_elem_one.type = GSI_XFER_ELEM_DATA;
gsi_xfer_elem_one.xfer_user_data = rx_pkt;
@@ -2207,6 +2209,7 @@ static void ipa3_replenish_rx_cache_recycle(struct ipa3_sys_context *sys)
gsi_xfer_elem_one.addr = rx_pkt->data.dma_addr;
gsi_xfer_elem_one.len = sys->rx_buff_sz;
gsi_xfer_elem_one.flags |= GSI_XFER_FLAG_EOT;
+ gsi_xfer_elem_one.flags |= GSI_XFER_FLAG_EOB;
gsi_xfer_elem_one.type = GSI_XFER_ELEM_DATA;
gsi_xfer_elem_one.xfer_user_data = rx_pkt;
@@ -2272,6 +2275,7 @@ static void ipa3_fast_replenish_rx_cache(struct ipa3_sys_context *sys)
gsi_xfer_elem_one.addr = rx_pkt->data.dma_addr;
gsi_xfer_elem_one.len = sys->rx_buff_sz;
gsi_xfer_elem_one.flags |= GSI_XFER_FLAG_EOT;
+ gsi_xfer_elem_one.flags |= GSI_XFER_FLAG_EOB;
gsi_xfer_elem_one.type = GSI_XFER_ELEM_DATA;
gsi_xfer_elem_one.xfer_user_data = rx_pkt;
@@ -2407,7 +2411,6 @@ static int ipa3_lan_rx_pyld_hdlr(struct sk_buff *skb,
if (skb->len == 0) {
IPAERR("ZLT\n");
- sys->free_skb(skb);
return rc;
}
@@ -2467,7 +2470,6 @@ static int ipa3_lan_rx_pyld_hdlr(struct sk_buff *skb,
sys->prev_skb = skb2;
}
sys->len_rem -= skb->len;
- sys->free_skb(skb);
return rc;
}
}
@@ -2481,7 +2483,7 @@ begin:
if (skb->len < pkt_status_sz) {
WARN_ON(sys->prev_skb != NULL);
IPADBG_LOW("status straddles buffer\n");
- sys->prev_skb = skb;
+ sys->prev_skb = skb_copy(skb, GFP_KERNEL);
sys->len_partial = skb->len;
return rc;
}
@@ -2573,7 +2575,7 @@ begin:
IPAHAL_PKT_STATUS_EXCEPTION_NONE) {
WARN_ON(sys->prev_skb != NULL);
IPADBG_LOW("Ins header in next buffer\n");
- sys->prev_skb = skb;
+ sys->prev_skb = skb_copy(skb, GFP_KERNEL);
sys->len_partial = skb->len;
return rc;
}
@@ -2594,7 +2596,7 @@ begin:
}
skb2 = ipa3_skb_copy_for_client(skb,
- status.pkt_len + pkt_status_sz);
+ min(status.pkt_len + pkt_status_sz, skb->len));
if (likely(skb2)) {
if (skb->len < len + pkt_status_sz) {
IPADBG_LOW("SPL skb len %d len %d\n",
@@ -3764,6 +3766,7 @@ static void ipa_gsi_irq_rx_notify_cb(struct gsi_chan_xfer_notify *notify)
switch (notify->evt_id) {
case GSI_CHAN_EVT_EOT:
+ case GSI_CHAN_EVT_EOB:
atomic_set(&ipa3_ctx->transport_pm.eot_activity, 1);
if (!atomic_read(&sys->curr_polling_state)) {
/* put the gsi channel into polling mode */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index d2c605d7627c..cce05cf31b3c 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -2194,4 +2194,7 @@ const char *ipa_hw_error_str(enum ipa3_hw_errors err_type);
int ipa_gsi_ch20_wa(void);
int ipa3_rx_poll(u32 clnt_hdl, int budget);
void ipa3_recycle_wan_skb(struct sk_buff *skb);
+int ipa3_smmu_map_peer_reg(phys_addr_t phys_addr, bool map);
+int ipa3_smmu_map_peer_buff(u64 iova, phys_addr_t phys_addr,
+ u32 size, bool map);
#endif /* _IPA3_I_H_ */
diff --git a/drivers/power/qcom-charger/fg-util.c b/drivers/power/qcom-charger/fg-util.c
index fe00dadc3f38..9f2d9973e04b 100644
--- a/drivers/power/qcom-charger/fg-util.c
+++ b/drivers/power/qcom-charger/fg-util.c
@@ -384,7 +384,7 @@ static int print_to_log(struct fg_log_buffer *log, const char *fmt, ...)
static int write_next_line_to_log(struct fg_trans *trans, int offset,
size_t *pcnt)
{
- int i, j;
+ int i;
u8 data[ITEMS_PER_LINE];
u16 address;
struct fg_log_buffer *log = trans->log;
@@ -397,7 +397,6 @@ static int write_next_line_to_log(struct fg_trans *trans, int offset,
goto done;
memcpy(data, trans->data + (offset - trans->addr), items_to_read);
-
*pcnt -= items_to_read;
/* address is in word now and it increments by 1. */
@@ -407,8 +406,8 @@ static int write_next_line_to_log(struct fg_trans *trans, int offset,
goto done;
/* Log the data items */
- for (j = 0; i < items_to_log; ++i, ++j) {
- cnt = print_to_log(log, "%2.2X ", data[j]);
+ for (i = 0; i < items_to_log; ++i) {
+ cnt = print_to_log(log, "%2.2X ", data[i]);
if (cnt == 0)
goto done;
}
@@ -552,7 +551,8 @@ static ssize_t fg_sram_dfs_reg_write(struct file *file, const char __user *buf,
values = kbuf;
/* Parse the data in the buffer. It should be a string of numbers */
- while (sscanf(kbuf + pos, "%i%n", &data, &bytes_read) == 1) {
+ while ((pos < count) &&
+ sscanf(kbuf + pos, "%i%n", &data, &bytes_read) == 1) {
pos += bytes_read;
values[cnt++] = data & 0xff;
}
diff --git a/drivers/power/qcom-charger/qpnp-smb2.c b/drivers/power/qcom-charger/qpnp-smb2.c
index ad00a987ae41..541e40aeb91a 100644
--- a/drivers/power/qcom-charger/qpnp-smb2.c
+++ b/drivers/power/qcom-charger/qpnp-smb2.c
@@ -108,6 +108,13 @@ static struct smb_params v1_params = {
.max_u = 3000000,
.step_u = 25000,
},
+ .jeita_cc_comp = {
+ .name = "jeita fcc reduction",
+ .reg = JEITA_CCCOMP_CFG_REG,
+ .min_u = 0,
+ .max_u = 1575000,
+ .step_u = 25000,
+ },
};
struct smb_dt_props {
@@ -722,49 +729,49 @@ struct smb2_irq_info {
static struct smb2_irq_info smb2_irqs[] = {
/* CHARGER IRQs */
- { "chg-error", smblib_handle_debug },
- { "chg-state-change", smblib_handle_chg_state_change, true },
- { "step-chg-state-change", smblib_handle_debug },
- { "step-chg-soc-update-fail", smblib_handle_debug },
+ { "chg-error", smblib_handle_debug },
+ { "chg-state-change", smblib_handle_chg_state_change, true },
+ { "step-chg-state-change", smblib_handle_debug },
+ { "step-chg-soc-update-fail", smblib_handle_debug },
{ "step-chg-soc-update-request", smblib_handle_debug },
/* OTG IRQs */
- { "otg-fail", smblib_handle_debug },
- { "otg-overcurrent", smblib_handle_debug },
- { "otg-oc-dis-sw-sts", smblib_handle_debug },
- { "testmode-change-detect", smblib_handle_debug },
+ { "otg-fail", smblib_handle_debug },
+ { "otg-overcurrent", smblib_handle_debug },
+ { "otg-oc-dis-sw-sts", smblib_handle_debug },
+ { "testmode-change-detect", smblib_handle_debug },
/* BATTERY IRQs */
- { "bat-temp", smblib_handle_batt_psy_changed },
- { "bat-ocp", smblib_handle_batt_psy_changed },
- { "bat-ov", smblib_handle_batt_psy_changed },
- { "bat-low", smblib_handle_batt_psy_changed },
- { "bat-therm-or-id-missing", smblib_handle_batt_psy_changed },
- { "bat-terminal-missing", smblib_handle_batt_psy_changed },
+ { "bat-temp", smblib_handle_batt_temp_changed },
+ { "bat-ocp", smblib_handle_batt_psy_changed },
+ { "bat-ov", smblib_handle_batt_psy_changed },
+ { "bat-low", smblib_handle_batt_psy_changed },
+ { "bat-therm-or-id-missing", smblib_handle_batt_psy_changed },
+ { "bat-terminal-missing", smblib_handle_batt_psy_changed },
/* USB INPUT IRQs */
- { "usbin-collapse", smblib_handle_debug },
- { "usbin-lt-3p6v", smblib_handle_debug },
- { "usbin-uv", smblib_handle_debug },
- { "usbin-ov", smblib_handle_debug },
- { "usbin-plugin", smblib_handle_usb_plugin, true },
- { "usbin-src-change", smblib_handle_usb_source_change, true },
- { "usbin-icl-change", smblib_handle_icl_change, true },
+ { "usbin-collapse", smblib_handle_debug },
+ { "usbin-lt-3p6v", smblib_handle_debug },
+ { "usbin-uv", smblib_handle_debug },
+ { "usbin-ov", smblib_handle_debug },
+ { "usbin-plugin", smblib_handle_usb_plugin, true },
+ { "usbin-src-change", smblib_handle_usb_source_change, true },
+ { "usbin-icl-change", smblib_handle_icl_change, true },
{ "type-c-change", smblib_handle_usb_typec_change, true },
/* DC INPUT IRQs */
- { "dcin-collapse", smblib_handle_debug },
- { "dcin-lt-3p6v", smblib_handle_debug },
- { "dcin-uv", smblib_handle_debug },
- { "dcin-ov", smblib_handle_debug },
- { "dcin-plugin", smblib_handle_debug },
- { "div2-en-dg", smblib_handle_debug },
- { "dcin-icl-change", smblib_handle_debug },
+ { "dcin-collapse", smblib_handle_debug },
+ { "dcin-lt-3p6v", smblib_handle_debug },
+ { "dcin-uv", smblib_handle_debug },
+ { "dcin-ov", smblib_handle_debug },
+ { "dcin-plugin", smblib_handle_debug },
+ { "div2-en-dg", smblib_handle_debug },
+ { "dcin-icl-change", smblib_handle_debug },
/* MISCELLANEOUS IRQs */
- { "wdog-snarl", NULL },
- { "wdog-bark", NULL },
- { "aicl-fail", smblib_handle_debug },
- { "aicl-done", smblib_handle_debug },
- { "high-duty-cycle", smblib_handle_debug },
- { "input-current-limiting", smblib_handle_debug },
- { "temperature-change", smblib_handle_debug },
- { "switcher-power-ok", smblib_handle_debug },
+ { "wdog-snarl", NULL },
+ { "wdog-bark", NULL },
+ { "aicl-fail", smblib_handle_debug },
+ { "aicl-done", smblib_handle_debug },
+ { "high-duty-cycle", smblib_handle_debug },
+ { "input-current-limiting", smblib_handle_debug },
+ { "temperature-change", smblib_handle_debug },
+ { "switcher-power-ok", smblib_handle_debug },
};
static int smb2_get_irq_index_byname(const char *irq_name)
diff --git a/drivers/power/qcom-charger/smb-lib.c b/drivers/power/qcom-charger/smb-lib.c
index 55bcc9ec443e..dd3ec1eb51e3 100644
--- a/drivers/power/qcom-charger/smb-lib.c
+++ b/drivers/power/qcom-charger/smb-lib.c
@@ -30,7 +30,7 @@
static bool is_secure(struct smb_charger *chg, int addr)
{
/* assume everything above 0xC0 is secure */
- return (bool)(addr >= 0xC0);
+ return (bool)((addr & 0xFF) >= 0xC0);
}
int smblib_read(struct smb_charger *chg, u16 addr, u8 *val)
@@ -85,10 +85,36 @@ unlock:
static void smblib_fcc_split_ua(struct smb_charger *chg, int total_fcc,
int *master_ua, int *slave_ua)
{
+ int rc, cc_reduction_ua = 0;
int master_percent = min(max(*chg->pl.master_percent, 0), 100);
+ union power_supply_propval pval = {0, };
+
+ /*
+ * if master_percent is 0, s/w will configure master's fcc to zero and
+ * slave's fcc to the max. However since master's fcc is zero it
+ * disables its own charging and as a result the slave's charging is
+ * disabled via the fault line.
+ */
+ rc = smblib_get_prop_batt_health(chg, &pval);
+ if (rc == 0) {
+ if (pval.intval == POWER_SUPPLY_HEALTH_WARM
+ || pval.intval == POWER_SUPPLY_HEALTH_COOL) {
+ rc = smblib_get_charge_param(chg,
+ &chg->param.jeita_cc_comp,
+ &cc_reduction_ua);
+ if (rc < 0) {
+ dev_err(chg->dev, "Could not get jeita comp, rc=%d\n",
+ rc);
+ cc_reduction_ua = 0;
+ }
+ }
+ }
+
+ total_fcc = max(0, total_fcc - cc_reduction_ua);
*master_ua = (total_fcc * master_percent) / 100;
*slave_ua = (total_fcc - *master_ua) * chg->pl.taper_percent / 100;
+ *master_ua += cc_reduction_ua;
}
/********************
@@ -846,6 +872,7 @@ int smblib_set_prop_input_suspend(struct smb_charger *chg,
return rc;
}
+ power_supply_changed(chg->batt_psy);
return rc;
}
@@ -1253,6 +1280,16 @@ irqreturn_t smblib_handle_chg_state_change(int irq, void *data)
return IRQ_HANDLED;
}
+irqreturn_t smblib_handle_batt_temp_changed(int irq, void *data)
+{
+ struct smb_irq_data *irq_data = data;
+ struct smb_charger *chg = irq_data->parent_data;
+
+ rerun_election(chg->fcc_votable);
+ power_supply_changed(chg->batt_psy);
+ return IRQ_HANDLED;
+}
+
irqreturn_t smblib_handle_batt_psy_changed(int irq, void *data)
{
struct smb_irq_data *irq_data = data;
@@ -1280,15 +1317,6 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data)
int rc;
u8 stat;
- rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat);
- if (rc < 0) {
- dev_err(chg->dev, "Couldn't read USB_INT_RT_STS rc=%d\n",
- rc);
- return IRQ_HANDLED;
- }
-
- chg->vbus_present = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT);
-
/* fetch the DPDM regulator */
if (!chg->dpdm_reg && of_get_property(chg->dev->of_node,
"dpdm-supply", NULL)) {
@@ -1303,18 +1331,30 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data)
if (!chg->dpdm_reg)
goto skip_dpdm_float;
- if (chg->vbus_present && !regulator_is_enabled(chg->dpdm_reg)) {
- smblib_dbg(chg, PR_MISC, "enabling DPDM regulator\n");
- rc = regulator_enable(chg->dpdm_reg);
- if (rc < 0)
- dev_err(chg->dev, "Couldn't enable dpdm regulator rc=%d\n",
- rc);
- } else if (regulator_is_enabled(chg->dpdm_reg)) {
- smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n");
- rc = regulator_disable(chg->dpdm_reg);
- if (rc < 0)
- dev_err(chg->dev, "Couldn't disable dpdm regulator rc=%d\n",
- rc);
+ rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't read USB_INT_RT_STS rc=%d\n", rc);
+ return IRQ_HANDLED;
+ }
+
+ chg->vbus_present = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT);
+
+ if (chg->vbus_present) {
+ if (!regulator_is_enabled(chg->dpdm_reg)) {
+ smblib_dbg(chg, PR_MISC, "enabling DPDM regulator\n");
+ rc = regulator_enable(chg->dpdm_reg);
+ if (rc < 0)
+ dev_err(chg->dev, "Couldn't enable dpdm regulator rc=%d\n",
+ rc);
+ }
+ } else {
+ if (regulator_is_enabled(chg->dpdm_reg)) {
+ smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n");
+ rc = regulator_disable(chg->dpdm_reg);
+ if (rc < 0)
+ dev_err(chg->dev, "Couldn't disable dpdm regulator rc=%d\n",
+ rc);
+ }
}
skip_dpdm_float:
diff --git a/drivers/power/qcom-charger/smb-lib.h b/drivers/power/qcom-charger/smb-lib.h
index 1ee4945b625f..2e35e1e3b174 100644
--- a/drivers/power/qcom-charger/smb-lib.h
+++ b/drivers/power/qcom-charger/smb-lib.h
@@ -68,6 +68,7 @@ struct smb_params {
struct smb_chg_param dc_icl_div2_mid_lv;
struct smb_chg_param dc_icl_div2_mid_hv;
struct smb_chg_param dc_icl_div2_hv;
+ struct smb_chg_param jeita_cc_comp;
};
struct parallel_params {
@@ -150,6 +151,7 @@ int smblib_vconn_regulator_is_enabled(struct regulator_dev *rdev);
irqreturn_t smblib_handle_debug(int irq, void *data);
irqreturn_t smblib_handle_chg_state_change(int irq, void *data);
+irqreturn_t smblib_handle_batt_temp_changed(int irq, void *data);
irqreturn_t smblib_handle_batt_psy_changed(int irq, void *data);
irqreturn_t smblib_handle_usb_psy_changed(int irq, void *data);
irqreturn_t smblib_handle_usb_plugin(int irq, void *data);
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 3e167f4c0f42..4d406c51d884 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -4092,8 +4092,9 @@ int ufshcd_uic_hibern8_exit(struct ufs_hba *hba)
ufshcd_update_error_stats(hba, UFS_ERR_HIBERN8_EXIT);
dev_err(hba->dev, "%s: hibern8 exit failed. ret = %d",
__func__, ret);
+ ret = ufshcd_link_recovery(hba);
/* Unable to recover the link, so no point proceeding */
- if (ufshcd_link_recovery(hba))
+ if (ret)
BUG();
} else {
dev_dbg(hba->dev, "%s: Hibern8 Exit at %lld us", __func__,
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index 2769d08b3056..98fce5e5c06a 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -711,7 +711,6 @@ static int ngd_bulk_wr(struct slim_controller *ctrl, u8 la, u8 mt, u8 mc,
struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
int i, ret;
struct msm_slim_endp *endpoint = &dev->tx_msgq;
- struct sps_pipe *pipe = endpoint->sps;
u32 *header;
DECLARE_COMPLETION_ONSTACK(done);
@@ -808,8 +807,8 @@ static int ngd_bulk_wr(struct slim_controller *ctrl, u8 la, u8 mt, u8 mc,
goto retpath;
}
- ret = sps_transfer_one(pipe, dev->bulk.wr_dma, dev->bulk.size, NULL,
- SPS_IOVEC_FLAG_EOT);
+ ret = sps_transfer_one(endpoint->sps, dev->bulk.wr_dma, dev->bulk.size,
+ NULL, SPS_IOVEC_FLAG_EOT);
if (ret) {
SLIM_WARN(dev, "sps transfer one returned error:%d", ret);
goto retpath;
@@ -1471,11 +1470,13 @@ static void ngd_adsp_down(struct msm_slim_ctrl *dev)
struct slim_controller *ctrl = &dev->ctrl;
struct slim_device *sbdev;
+ mutex_lock(&dev->ssr_lock);
ngd_slim_enable(dev, false);
/* device up should be called again after SSR */
list_for_each_entry(sbdev, &ctrl->devs, dev_list)
slim_report_absent(sbdev);
SLIM_INFO(dev, "SLIM ADSP SSR (DOWN) done\n");
+ mutex_unlock(&dev->ssr_lock);
}
static void ngd_adsp_up(struct work_struct *work)
@@ -1484,7 +1485,9 @@ static void ngd_adsp_up(struct work_struct *work)
container_of(work, struct msm_slim_qmi, ssr_up);
struct msm_slim_ctrl *dev =
container_of(qmi, struct msm_slim_ctrl, qmi);
+ mutex_lock(&dev->ssr_lock);
ngd_slim_enable(dev, true);
+ mutex_unlock(&dev->ssr_lock);
}
static ssize_t show_mask(struct device *device, struct device_attribute *attr,
@@ -1648,6 +1651,7 @@ static int ngd_slim_probe(struct platform_device *pdev)
init_completion(&dev->reconf);
init_completion(&dev->ctrl_up);
mutex_init(&dev->tx_lock);
+ mutex_init(&dev->ssr_lock);
spin_lock_init(&dev->tx_buf_lock);
spin_lock_init(&dev->rx_lock);
dev->ee = 1;
diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h
index 86d2606182fa..7859d1e79e39 100644
--- a/drivers/slimbus/slim-msm.h
+++ b/drivers/slimbus/slim-msm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2016, 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
@@ -284,6 +284,7 @@ struct msm_slim_ctrl {
struct clk *rclk;
struct clk *hclk;
struct mutex tx_lock;
+ struct mutex ssr_lock;
spinlock_t tx_buf_lock;
u8 pgdla;
enum msm_slim_msgq use_rx_msgqs;
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 36b9a9435910..adbf2dc7a166 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -83,7 +83,9 @@ obj-$(CONFIG_MSM_GLADIATOR_HANG_DETECT) += gladiator_hang_detect.o
obj-$(CONFIG_MSM_RUN_QUEUE_STATS) += msm_rq_stats.o
obj-$(CONFIG_MSM_BOOT_STATS) += boot_stats.o
obj-$(CONFIG_MSM_AVTIMER) += avtimer.o
+ifdef CONFIG_ARCH_MSM8996
obj-$(CONFIG_HW_PERF_EVENTS) += perf_event_kryo.o
+endif
obj-$(CONFIG_MSM_JTAGV8) += jtag-fuse.o jtagv8.o jtagv8-etm.o
obj-$(CONFIG_MSM_KERNEL_PROTECT) += kernel_protect.o
obj-$(CONFIG_MSM_RTB) += msm_rtb-hotplug.o
diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c
index e4d235957981..2bc425a437b2 100644
--- a/drivers/soc/qcom/service-locator.c
+++ b/drivers/soc/qcom/service-locator.c
@@ -48,6 +48,7 @@ module_param_named(enable, locator_status, uint, S_IRUGO | S_IWUSR);
static void service_locator_svc_arrive(struct work_struct *work);
static void service_locator_svc_exit(struct work_struct *work);
static void service_locator_recv_msg(struct work_struct *work);
+static void pd_locator_work(struct work_struct *work);
struct workqueue_struct *servloc_wq;
@@ -61,6 +62,11 @@ struct pd_qmi_data {
struct qmi_handle *clnt_handle;
};
+struct pd_qmi_work {
+ struct work_struct pd_loc_work;
+ struct pd_qmi_client_data *pdc;
+ struct notifier_block *notifier;
+};
DEFINE_MUTEX(service_init_mutex);
struct pd_qmi_data service_locator;
@@ -288,7 +294,6 @@ out:
static int init_service_locator(void)
{
- static bool service_inited;
int rc = 0;
mutex_lock(&service_init_mutex);
@@ -324,50 +329,86 @@ static int init_service_locator(void)
goto inited;
}
- rc = wait_for_completion_timeout(&service_locator.service_available,
- msecs_to_jiffies(QMI_SERVREG_LOC_SERVER_INITIAL_TIMEOUT));
- if (!rc) {
- rc = -ENODEV;
- mutex_unlock(&service_init_mutex);
- pr_err("Process domain service locator response timeout!\n");
- goto error;
- }
+ wait_for_completion(&service_locator.service_available);
service_inited = true;
mutex_unlock(&service_init_mutex);
pr_info("Service locator initialized\n");
return 0;
-error:
- qmi_svc_event_notifier_unregister(SERVREG_LOC_SERVICE_ID_V01,
- SERVREG_LOC_SERVICE_VERS_V01, SERVREG_LOC_SERVICE_INSTANCE_ID,
- &service_locator.notifier);
- destroy_workqueue(servloc_wq);
+
inited:
mutex_unlock(&service_init_mutex);
return rc;
}
-int get_service_location(struct pd_qmi_client_data *data)
+int get_service_location(char *client_name, char *service_name,
+ struct notifier_block *locator_nb)
{
+ struct pd_qmi_client_data *pqcd;
+ struct pd_qmi_work *pqw;
int rc = 0;
- if (!data || !data->client_name || !data->service_name) {
+ if (!locator_nb || !client_name || !service_name) {
rc = -EINVAL;
pr_err("Invalid input!\n");
goto err;
}
+
+ pqcd = kmalloc(sizeof(struct pd_qmi_client_data), GFP_KERNEL);
+ if (!pqcd) {
+ rc = -ENOMEM;
+ pr_err("Allocation failed\n");
+ goto err;
+ }
+ strlcpy(pqcd->client_name, client_name, ARRAY_SIZE(pqcd->client_name));
+ strlcpy(pqcd->service_name, service_name,
+ ARRAY_SIZE(pqcd->service_name));
+
+ pqw = kmalloc(sizeof(struct pd_qmi_work), GFP_KERNEL);
+ if (!pqw) {
+ rc = -ENOMEM;
+ pr_err("Allocation failed\n");
+ goto err;
+ }
+ pqw->notifier = locator_nb;
+ pqw->pdc = pqcd;
+
+ INIT_WORK(&pqw->pd_loc_work, pd_locator_work);
+ schedule_work(&pqw->pd_loc_work);
+
+err:
+ return rc;
+}
+EXPORT_SYMBOL(get_service_location);
+
+static void pd_locator_work(struct work_struct *work)
+{
+ int rc = 0;
+ struct pd_qmi_client_data *data;
+ struct pd_qmi_work *pdqw = container_of(work, struct pd_qmi_work,
+ pd_loc_work);
+
+ data = pdqw->pdc;
rc = init_service_locator();
if (rc) {
pr_err("Unable to connect to service locator!, rc = %d\n", rc);
+ pdqw->notifier->notifier_call(pdqw->notifier,
+ LOCATOR_DOWN, NULL);
goto err;
}
rc = service_locator_send_msg(data);
- if (rc)
+ if (rc) {
pr_err("Failed to get process domains for %s for client %s\n",
data->service_name, data->client_name);
+ pdqw->notifier->notifier_call(pdqw->notifier,
+ LOCATOR_DOWN, NULL);
+ goto err;
+ }
+ pdqw->notifier->notifier_call(pdqw->notifier, LOCATOR_UP, data);
+
err:
- return rc;
+ kfree(data);
+ kfree(pdqw);
}
-EXPORT_SYMBOL(get_service_location);
int find_subsys(const char *pd_path, char *subsys)
{
@@ -391,75 +432,137 @@ EXPORT_SYMBOL(find_subsys);
static struct pd_qmi_client_data test_data;
-static ssize_t show_servloc(struct seq_file *f, void *unused)
+static int servloc_test_pdr_cb(struct notifier_block *this,
+ unsigned long opcode, void *ptr)
{
- int rc = 0, i = 0;
+ int i, rc = 0;
char subsys[QMI_SERVREG_LOC_NAME_LENGTH_V01];
+ struct pd_qmi_client_data *return_data;
- rc = get_service_location(&test_data);
- if (rc) {
- seq_printf(f, "Failed to get process domain!, rc = %d\n", rc);
+ return_data = (struct pd_qmi_client_data *)ptr;
+
+ if (opcode) {
+ pr_err("%s: Failed to get process domain!, opcode = %lu\n",
+ __func__, opcode);
return -EIO;
}
- seq_printf(f, "Service Name: %s\tTotal Domains: %d\n",
- test_data.service_name, test_data.total_domains);
- for (i = 0; i < test_data.total_domains; i++) {
- seq_printf(f, "Instance ID: %d\t ",
- test_data.domain_list[i].instance_id);
- seq_printf(f, "Domain Name: %s\n",
- test_data.domain_list[i].name);
- rc = find_subsys(test_data.domain_list[i].name, subsys);
+ pr_err("Service Name: %s\tTotal Domains: %d\n",
+ return_data->service_name, return_data->total_domains);
+
+ for (i = 0; i < return_data->total_domains; i++) {
+ pr_err("Instance ID: %d\t ",
+ return_data->domain_list[i].instance_id);
+ pr_err("Domain Name: %s\n",
+ return_data->domain_list[i].name);
+ rc = find_subsys(return_data->domain_list[i].name,
+ subsys);
if (rc < 0)
- seq_printf(f, "No valid subsys found for %s!\n",
- test_data.domain_list[i].name);
+ pr_err("No valid subsys found for %s!\n",
+ return_data->domain_list[i].name);
else
- seq_printf(f, "Subsys: %s\n", subsys);
- }
+ pr_err("Subsys: %s\n", subsys);
+ }
return 0;
}
-static ssize_t store_servloc(struct file *fp, const char __user *buf,
- size_t count, loff_t *unused)
+static struct notifier_block pdr_service_nb = {
+ .notifier_call = servloc_test_pdr_cb,
+};
+
+static ssize_t servloc_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
{
+ int rc = 0;
+ char *node_name = filp->private_data;
+
+ if (!strcmp(node_name, "test_servloc_get"))
+ rc = get_service_location(test_data.client_name,
+ test_data.service_name, &pdr_service_nb);
+
+ return rc;
+}
+
+static ssize_t servloc_write(struct file *fp, const char __user *buf,
+ size_t count, loff_t *unused)
+{
+ char *node_name = fp->private_data;
+
if (!buf)
return -EIO;
- snprintf(test_data.service_name, sizeof(test_data.service_name),
+ if (!strcmp(node_name, "service_name")) {
+ snprintf(test_data.service_name, sizeof(test_data.service_name),
"%.*s", (int) min((size_t)count - 1,
(sizeof(test_data.service_name) - 1)), buf);
+ } else {
+ snprintf(test_data.client_name, sizeof(test_data.client_name),
+ "%.*s", (int) min((size_t)count - 1,
+ (sizeof(test_data.client_name) - 1)), buf);
+ }
return count;
}
-static int servloc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, (void *)show_servloc, inode->i_private);
-}
-
static const struct file_operations servloc_fops = {
- .open = servloc_open,
- .read = seq_read,
- .write = store_servloc,
- .llseek = seq_lseek,
- .release = seq_release,
+ .open = simple_open,
+ .read = servloc_read,
+ .write = servloc_write,
};
+static struct dentry *servloc_base_dir;
static struct dentry *test_servloc_file;
+static int __init servloc_debugfs_init(void)
+{
+ servloc_base_dir = debugfs_create_dir("test_servloc", NULL);
+ return !servloc_base_dir ? -ENOMEM : 0;
+}
+
+static void servloc_debugfs_exit(void)
+{
+ debugfs_remove_recursive(servloc_base_dir);
+}
+
+static int servloc_debugfs_add(void)
+{
+ int rc;
+
+ if (!servloc_base_dir)
+ return -ENOMEM;
+
+ test_servloc_file = debugfs_create_file("client_name",
+ S_IRUGO | S_IWUSR, servloc_base_dir,
+ "client_name", &servloc_fops);
+ rc = !test_servloc_file ? -ENOMEM : 0;
+
+ if (rc == 0) {
+ test_servloc_file = debugfs_create_file("service_name",
+ S_IRUGO | S_IWUSR, servloc_base_dir,
+ "service_name", &servloc_fops);
+ rc = !test_servloc_file ? -ENOMEM : 0;
+ }
+
+ if (rc == 0) {
+ test_servloc_file = debugfs_create_file("test_servloc_get",
+ S_IRUGO | S_IWUSR, servloc_base_dir,
+ "test_servloc_get", &servloc_fops);
+ rc = !test_servloc_file ? -ENOMEM : 0;
+ }
+ return rc;
+}
+
static int __init service_locator_init(void)
{
pr_debug("service_locator_status = %d\n", locator_status);
- test_servloc_file = debugfs_create_file("test_servloc",
- S_IRUGO | S_IWUSR, NULL, NULL,
- &servloc_fops);
- if (!test_servloc_file)
- pr_err("Could not create test_servloc debugfs entry!");
+ if (servloc_debugfs_init())
+ pr_err("Could not create test_servloc base directory!");
+ if (servloc_debugfs_add())
+ pr_err("Could not create test_servloc node entries!");
return 0;
}
static void __exit service_locator_exit(void)
{
- debugfs_remove(test_servloc_file);
+ servloc_debugfs_exit();
}
-
module_init(service_locator_init);
module_exit(service_locator_exit);
diff --git a/drivers/thermal/msm-tsens.c b/drivers/thermal/msm-tsens.c
index 9e2ba25ce1ac..73d7435d2eb8 100644
--- a/drivers/thermal/msm-tsens.c
+++ b/drivers/thermal/msm-tsens.c
@@ -759,9 +759,9 @@ enum tsens_trip_type {
};
enum tsens_tm_trip_type {
- TSENS_TM_TRIP_CRITICAL = 0,
- TSENS_TM_TRIP_WARM,
+ TSENS_TM_TRIP_WARM = 0,
TSENS_TM_TRIP_COOL,
+ TSENS_TM_TRIP_CRITICAL,
TSENS_TM_TRIP_NUM,
};
@@ -1575,9 +1575,6 @@ static int tsens_tm_get_trip_type(struct thermal_zone_device *thermal,
case TSENS_TM_TRIP_COOL:
*type = THERMAL_TRIP_CONFIGURABLE_LOW;
break;
- case TSENS_TM_TRIP_CRITICAL:
- *type = THERMAL_TRIP_CRITICAL;
- break;
default:
return -EINVAL;
}
@@ -5717,8 +5714,8 @@ static int tsens_thermal_zone_register(struct tsens_tm_device *tmdev)
tmdev->sensor[i].tm = tmdev;
if (tmdev->tsens_type == TSENS_TYPE3) {
tmdev->sensor[i].tz_dev = thermal_zone_device_register(
- name, TSENS_TM_TRIP_NUM,
- TSENS_TM_WRITABLE_TRIPS_MASK,
+ name, TSENS_TRIP_NUM,
+ TSENS_WRITABLE_TRIPS_MASK,
&tmdev->sensor[i],
&tsens_tm_thermal_zone_ops, NULL, 0, 0);
if (IS_ERR(tmdev->sensor[i].tz_dev)) {
diff --git a/drivers/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c
index bbc6a8e96d41..ff40d6fad922 100644
--- a/drivers/thermal/msm_thermal.c
+++ b/drivers/thermal/msm_thermal.c
@@ -3615,7 +3615,7 @@ static int hotplug_init_cpu_offlined(void)
int temp = 0;
uint32_t cpu = 0;
- if (!hotplug_enabled)
+ if (!hotplug_enabled || !hotplug_task)
return 0;
mutex_lock(&core_control_mutex);
@@ -3632,8 +3632,7 @@ static int hotplug_init_cpu_offlined(void)
if (temp >= msm_thermal_info.hotplug_temp_degC)
cpus[cpu].offline = 1;
- else if (temp <= (msm_thermal_info.hotplug_temp_degC -
- msm_thermal_info.hotplug_temp_hysteresis_degC))
+ else
cpus[cpu].offline = 0;
}
mutex_unlock(&core_control_mutex);
diff --git a/drivers/thermal/qpnp-adc-tm.c b/drivers/thermal/qpnp-adc-tm.c
index 500d47d4ffdf..84ab45fde4ae 100644
--- a/drivers/thermal/qpnp-adc-tm.c
+++ b/drivers/thermal/qpnp-adc-tm.c
@@ -250,6 +250,8 @@ struct qpnp_adc_tm_sensor {
bool thermal_node;
uint32_t scale_type;
struct list_head thr_list;
+ bool high_thr_triggered;
+ bool low_thr_triggered;
};
struct qpnp_adc_tm_chip {
@@ -501,8 +503,8 @@ static int32_t qpnp_adc_tm_rc_check_channel_en(struct qpnp_adc_tm_chip *chip)
}
adc_tm_ctl &= QPNP_BTM_Mn_MEAS_EN;
- status_low &= QPNP_BTM_Mn_LOW_THR_INT_EN;
- status_high &= QPNP_BTM_Mn_HIGH_THR_INT_EN;
+ status_low = adc_tm_ctl & QPNP_BTM_Mn_LOW_THR_INT_EN;
+ status_high = adc_tm_ctl & QPNP_BTM_Mn_HIGH_THR_INT_EN;
/* Enable only if there are pending measurement requests */
if ((adc_tm_ctl && status_high) ||
@@ -1497,11 +1499,13 @@ static int qpnp_adc_tm_set_mode(struct thermal_zone_device *thermal,
if (qpnp_adc_tm_check_revision(chip, adc_tm->btm_channel_num))
return -EINVAL;
+ mutex_lock(&chip->adc->adc_lock);
+
btm_chan = adc_tm->btm_channel_num;
rc = qpnp_adc_tm_get_btm_idx(chip, btm_chan, &btm_chan_idx);
if (rc < 0) {
pr_err("Invalid btm channel idx\n");
- return rc;
+ goto fail;
}
if (mode == THERMAL_DEVICE_ENABLED) {
@@ -1527,14 +1531,14 @@ static int qpnp_adc_tm_set_mode(struct thermal_zone_device *thermal,
rc = qpnp_adc_tm_configure(chip, chip->adc->amux_prop);
if (rc) {
pr_err("adc-tm configure failed with %d\n", rc);
- return -EINVAL;
+ goto fail;
}
} else {
rc = qpnp_adc_tm_hc_configure(chip,
chip->adc->amux_prop);
if (rc) {
pr_err("hc configure failed with %d\n", rc);
- return -EINVAL;
+ goto fail;
}
}
} else if (mode == THERMAL_DEVICE_DISABLED) {
@@ -1545,7 +1549,7 @@ static int qpnp_adc_tm_set_mode(struct thermal_zone_device *thermal,
rc = qpnp_adc_tm_mode_select(chip, mode_ctl);
if (rc < 0) {
pr_err("adc-tm single mode select failed\n");
- return rc;
+ goto fail;
}
}
@@ -1553,7 +1557,7 @@ static int qpnp_adc_tm_set_mode(struct thermal_zone_device *thermal,
rc = qpnp_adc_tm_disable(chip);
if (rc < 0) {
pr_err("adc-tm disable failed\n");
- return rc;
+ goto fail;
}
if (!chip->adc_tm_hc) {
@@ -1561,14 +1565,14 @@ static int qpnp_adc_tm_set_mode(struct thermal_zone_device *thermal,
rc = qpnp_adc_tm_req_sts_check(chip);
if (rc < 0) {
pr_err("adc-tm req_sts check failed\n");
- return rc;
+ goto fail;
}
rc = qpnp_adc_tm_reg_update(chip,
QPNP_ADC_TM_MULTI_MEAS_EN, sensor_mask, false);
if (rc < 0) {
pr_err("multi measurement update failed\n");
- return rc;
+ goto fail;
}
} else {
rc = qpnp_adc_tm_reg_update(chip,
@@ -1576,19 +1580,22 @@ static int qpnp_adc_tm_set_mode(struct thermal_zone_device *thermal,
QPNP_BTM_Mn_MEAS_EN, false);
if (rc < 0) {
pr_err("multi measurement disable failed\n");
- return rc;
+ goto fail;
}
}
rc = qpnp_adc_tm_enable_if_channel_meas(chip);
if (rc < 0) {
pr_err("re-enabling measurement failed\n");
- return rc;
+ goto fail;
}
}
adc_tm->mode = mode;
+fail:
+ mutex_unlock(&chip->adc->adc_lock);
+
return 0;
}
@@ -2096,17 +2103,207 @@ fail:
return rc;
}
-static int qpnp_adc_tm_read_status(struct qpnp_adc_tm_chip *chip)
+static int qpnp_adc_tm_disable_rearm_high_thresholds(
+ struct qpnp_adc_tm_chip *chip, int sensor_num)
{
- u8 sensor_mask = 0, notify_check = 0;
- int rc = 0, sensor_notify_num = 0, i = 0, sensor_num = 0;
+
+ struct qpnp_adc_thr_client_info *client_info = NULL;
+ struct list_head *thr_list;
uint32_t btm_chan_num = 0;
+ u8 sensor_mask = 0, notify_check = 0;
+ int rc = 0;
+
+ btm_chan_num = chip->sensor[sensor_num].btm_channel_num;
+ pr_debug("high:sen:%d, hs:0x%x, ls:0x%x, meas_en:0x%x\n",
+ sensor_num, chip->th_info.adc_tm_high_enable,
+ chip->th_info.adc_tm_low_enable,
+ chip->th_info.qpnp_adc_tm_meas_en);
+ if (!chip->sensor[sensor_num].thermal_node) {
+ /*
+ * For non thermal registered clients such as usb_id,
+ * vbatt, pmic_therm
+ */
+ sensor_mask = 1 << sensor_num;
+ pr_debug("non thermal node - mask:%x\n", sensor_mask);
+ rc = qpnp_adc_tm_recalib_request_check(chip,
+ sensor_num, true, &notify_check);
+ if (rc < 0 || !notify_check) {
+ pr_debug("Calib recheck re-armed rc=%d\n", rc);
+ chip->th_info.adc_tm_high_enable = 0;
+ return rc;
+ }
+ } else {
+ /*
+ * Uses the thermal sysfs registered device to disable
+ * the corresponding high voltage threshold which
+ * is triggered by low temp
+ */
+ sensor_mask = 1 << sensor_num;
+ pr_debug("thermal node with mask:%x\n", sensor_mask);
+ rc = qpnp_adc_tm_activate_trip_type(
+ chip->sensor[sensor_num].tz_dev,
+ ADC_TM_TRIP_LOW_COOL,
+ THERMAL_TRIP_ACTIVATION_DISABLED);
+ if (rc < 0) {
+ pr_err("notify error:%d\n", sensor_num);
+ return rc;
+ }
+ }
+ list_for_each(thr_list, &chip->sensor[sensor_num].thr_list) {
+ client_info = list_entry(thr_list,
+ struct qpnp_adc_thr_client_info, list);
+ if (client_info->high_thr_set) {
+ client_info->high_thr_set = false;
+ client_info->notify_high_thr = true;
+ if (client_info->state_req_copy ==
+ ADC_TM_HIGH_LOW_THR_ENABLE)
+ client_info->state_req_copy =
+ ADC_TM_LOW_THR_ENABLE;
+ else
+ client_info->state_req_copy =
+ ADC_TM_HIGH_THR_DISABLE;
+ }
+ }
+ qpnp_adc_tm_manage_thresholds(chip, sensor_num, btm_chan_num);
+
+ if (!chip->adc_tm_hc) {
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_ADC_TM_MULTI_MEAS_EN,
+ sensor_mask, false);
+ if (rc < 0) {
+ pr_err("multi meas disable failed\n");
+ return rc;
+ }
+ } else {
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_BTM_Mn_EN(sensor_num),
+ QPNP_BTM_Mn_MEAS_EN, false);
+ if (rc < 0) {
+ pr_err("multi meas disable failed\n");
+ return rc;
+ }
+ }
+
+ rc = qpnp_adc_tm_enable_if_channel_meas(chip);
+ if (rc < 0) {
+ pr_err("re-enabling measurement failed\n");
+ return rc;
+ }
+
+ queue_work(chip->sensor[sensor_num].req_wq,
+ &chip->sensor[sensor_num].work);
+
+ return rc;
+}
+
+static int qpnp_adc_tm_disable_rearm_low_thresholds(
+ struct qpnp_adc_tm_chip *chip, int sensor_num)
+{
struct qpnp_adc_thr_client_info *client_info = NULL;
struct list_head *thr_list;
+ uint32_t btm_chan_num = 0;
+ u8 sensor_mask = 0, notify_check = 0;
+ int rc = 0;
+
+ btm_chan_num = chip->sensor[sensor_num].btm_channel_num;
+ pr_debug("low:sen:%d, hs:0x%x, ls:0x%x, meas_en:0x%x\n",
+ sensor_num, chip->th_info.adc_tm_high_enable,
+ chip->th_info.adc_tm_low_enable,
+ chip->th_info.qpnp_adc_tm_meas_en);
+ if (!chip->sensor[sensor_num].thermal_node) {
+ /*
+ * For non thermal registered clients such as usb_id,
+ * vbatt, pmic_therm
+ */
+ pr_debug("non thermal node - mask:%x\n", sensor_mask);
+ rc = qpnp_adc_tm_recalib_request_check(chip,
+ sensor_num, false, &notify_check);
+ if (rc < 0 || !notify_check) {
+ pr_debug("Calib recheck re-armed rc=%d\n", rc);
+ chip->th_info.adc_tm_low_enable = 0;
+ return rc;
+ }
+ sensor_mask = 1 << sensor_num;
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_ADC_TM_LOW_THR_INT_EN,
+ sensor_mask, false);
+ if (rc < 0) {
+ pr_err("low threshold int read failed\n");
+ return rc;
+ }
+ } else {
+ /*
+ * Uses the thermal sysfs registered device to disable
+ * the corresponding high voltage threshold which
+ * is triggered by low temp
+ */
+ sensor_mask = 1 << sensor_num;
+ pr_debug("thermal node with mask:%x\n", sensor_mask);
+ rc = qpnp_adc_tm_activate_trip_type(
+ chip->sensor[sensor_num].tz_dev,
+ ADC_TM_TRIP_HIGH_WARM,
+ THERMAL_TRIP_ACTIVATION_DISABLED);
+ if (rc < 0) {
+ pr_err("notify error:%d\n", sensor_num);
+ return rc;
+ }
+ }
+ list_for_each(thr_list, &chip->sensor[sensor_num].thr_list) {
+ client_info = list_entry(thr_list,
+ struct qpnp_adc_thr_client_info, list);
+ if (client_info->low_thr_set) {
+ client_info->low_thr_set = false;
+ client_info->notify_low_thr = true;
+ if (client_info->state_req_copy ==
+ ADC_TM_HIGH_LOW_THR_ENABLE)
+ client_info->state_req_copy =
+ ADC_TM_HIGH_THR_ENABLE;
+ else
+ client_info->state_req_copy =
+ ADC_TM_LOW_THR_DISABLE;
+ }
+ }
+ qpnp_adc_tm_manage_thresholds(chip, sensor_num, btm_chan_num);
+
+ if (!chip->adc_tm_hc) {
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_ADC_TM_MULTI_MEAS_EN,
+ sensor_mask, false);
+ if (rc < 0) {
+ pr_err("multi meas disable failed\n");
+ return rc;
+ }
+ } else {
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_BTM_Mn_EN(sensor_num),
+ QPNP_BTM_Mn_MEAS_EN, false);
+ if (rc < 0) {
+ pr_err("multi meas disable failed\n");
+ return rc;
+ }
+ }
+
+ rc = qpnp_adc_tm_enable_if_channel_meas(chip);
+ if (rc < 0) {
+ pr_err("re-enabling measurement failed\n");
+ return rc;
+ }
+
+ queue_work(chip->sensor[sensor_num].req_wq,
+ &chip->sensor[sensor_num].work);
+
+ return rc;
+}
+
+static int qpnp_adc_tm_read_status(struct qpnp_adc_tm_chip *chip)
+{
+ int rc = 0, sensor_num = 0;
if (qpnp_adc_tm_is_valid(chip))
return -ENODEV;
+ pr_debug("%s\n", __func__);
+
mutex_lock(&chip->adc->adc_lock);
if (!chip->adc_tm_hc) {
@@ -2117,159 +2314,36 @@ static int qpnp_adc_tm_read_status(struct qpnp_adc_tm_chip *chip)
}
}
- if (chip->th_info.adc_tm_high_enable) {
- sensor_notify_num = chip->th_info.adc_tm_high_enable;
- while (i < chip->max_channels_available) {
- if ((sensor_notify_num & 0x1) == 1)
- sensor_num = i;
- sensor_notify_num >>= 1;
- i++;
- }
-
- btm_chan_num = chip->sensor[sensor_num].btm_channel_num;
- pr_debug("high:sen:%d, hs:0x%x, ls:0x%x, meas_en:0x%x\n",
- sensor_num, chip->th_info.adc_tm_high_enable,
- chip->th_info.adc_tm_low_enable,
- chip->th_info.qpnp_adc_tm_meas_en);
- if (!chip->sensor[sensor_num].thermal_node) {
- /* For non thermal registered clients
- such as usb_id, vbatt, pmic_therm */
- sensor_mask = 1 << sensor_num;
- pr_debug("non thermal node - mask:%x\n", sensor_mask);
- rc = qpnp_adc_tm_recalib_request_check(chip,
- sensor_num, true, &notify_check);
- if (rc < 0 || !notify_check) {
- pr_debug("Calib recheck re-armed rc=%d\n", rc);
- chip->th_info.adc_tm_high_enable = 0;
+ while (sensor_num < chip->max_channels_available) {
+ if (chip->sensor[sensor_num].high_thr_triggered) {
+ rc = qpnp_adc_tm_disable_rearm_high_thresholds(
+ chip, sensor_num);
+ if (rc) {
+ pr_err("rearm threshold failed\n");
goto fail;
}
- } else {
- /* Uses the thermal sysfs registered device to disable
- the corresponding high voltage threshold which
- is triggered by low temp */
- pr_debug("thermal node with mask:%x\n", sensor_mask);
- }
- list_for_each(thr_list, &chip->sensor[sensor_num].thr_list) {
- client_info = list_entry(thr_list,
- struct qpnp_adc_thr_client_info, list);
- if (client_info->high_thr_set) {
- client_info->high_thr_set = false;
- client_info->notify_high_thr = true;
- if (client_info->state_req_copy ==
- ADC_TM_HIGH_LOW_THR_ENABLE)
- client_info->state_req_copy =
- ADC_TM_LOW_THR_ENABLE;
- else
- client_info->state_req_copy =
- ADC_TM_HIGH_THR_DISABLE;
- }
+ chip->sensor[sensor_num].high_thr_triggered = false;
}
+ sensor_num++;
}
- if (chip->th_info.adc_tm_low_enable) {
- sensor_notify_num = chip->th_info.adc_tm_low_enable;
- i = 0;
- while (i < chip->max_channels_available) {
- if ((sensor_notify_num & 0x1) == 1)
- sensor_num = i;
- sensor_notify_num >>= 1;
- i++;
- }
-
- btm_chan_num = chip->sensor[sensor_num].btm_channel_num;
- pr_debug("low:sen:%d, hs:0x%x, ls:0x%x, meas_en:0x%x\n",
- sensor_num, chip->th_info.adc_tm_high_enable,
- chip->th_info.adc_tm_low_enable,
- chip->th_info.qpnp_adc_tm_meas_en);
- if (!chip->sensor[sensor_num].thermal_node) {
- /* For non thermal registered clients
- such as usb_id, vbatt, pmic_therm */
- pr_debug("non thermal node - mask:%x\n", sensor_mask);
- rc = qpnp_adc_tm_recalib_request_check(chip,
- sensor_num, false, &notify_check);
- if (rc < 0 || !notify_check) {
- pr_debug("Calib recheck re-armed rc=%d\n", rc);
- chip->th_info.adc_tm_low_enable = 0;
- goto fail;
- }
- sensor_mask = 1 << sensor_num;
- rc = qpnp_adc_tm_reg_update(chip,
- QPNP_ADC_TM_LOW_THR_INT_EN,
- sensor_mask, false);
- if (rc < 0) {
- pr_err("low threshold int read failed\n");
- goto fail;
- }
- } else {
- /* Uses the thermal sysfs registered device to disable
- the corresponding low voltage threshold which
- is triggered by high temp */
- pr_debug("thermal node with mask:%x\n", sensor_mask);
- rc = qpnp_adc_tm_activate_trip_type(
- chip->sensor[sensor_num].tz_dev,
- ADC_TM_TRIP_HIGH_WARM,
- THERMAL_TRIP_ACTIVATION_DISABLED);
- if (rc < 0) {
- pr_err("notify error:%d\n", sensor_num);
+ sensor_num = 0;
+ while (sensor_num < chip->max_channels_available) {
+ if (chip->sensor[sensor_num].low_thr_triggered) {
+ rc = qpnp_adc_tm_disable_rearm_low_thresholds(
+ chip, sensor_num);
+ if (rc) {
+ pr_err("rearm threshold failed\n");
goto fail;
}
+ chip->sensor[sensor_num].low_thr_triggered = false;
}
- list_for_each(thr_list, &chip->sensor[sensor_num].thr_list) {
- client_info = list_entry(thr_list,
- struct qpnp_adc_thr_client_info, list);
- if (client_info->low_thr_set) {
- /* mark the corresponding clients threshold
- as not set */
- client_info->low_thr_set = false;
- client_info->notify_low_thr = true;
- if (client_info->state_req_copy ==
- ADC_TM_HIGH_LOW_THR_ENABLE)
- client_info->state_req_copy =
- ADC_TM_HIGH_THR_ENABLE;
- else
- client_info->state_req_copy =
- ADC_TM_LOW_THR_DISABLE;
- }
- }
+ sensor_num++;
}
- qpnp_adc_tm_manage_thresholds(chip, sensor_num, btm_chan_num);
-
- if (chip->th_info.adc_tm_high_enable ||
- chip->th_info.adc_tm_low_enable) {
- if (!chip->adc_tm_hc) {
- rc = qpnp_adc_tm_reg_update(chip,
- QPNP_ADC_TM_MULTI_MEAS_EN,
- sensor_mask, false);
- if (rc < 0) {
- pr_err("multi meas disable failed\n");
- goto fail;
- }
- } else {
- rc = qpnp_adc_tm_reg_update(chip,
- QPNP_BTM_Mn_EN(sensor_mask),
- QPNP_BTM_Mn_MEAS_EN, false);
- if (rc < 0) {
- pr_err("multi meas disable failed\n");
- goto fail;
- }
- }
-
- rc = qpnp_adc_tm_enable_if_channel_meas(chip);
- if (rc < 0) {
- pr_err("re-enabling measurement failed\n");
- return rc;
- }
- } else
- pr_debug("No threshold status enable %d for high/low??\n",
- sensor_mask);
-
fail:
mutex_unlock(&chip->adc->adc_lock);
- if (chip->th_info.adc_tm_high_enable || chip->th_info.adc_tm_low_enable)
- queue_work(chip->sensor[sensor_num].req_wq,
- &chip->sensor[sensor_num].work);
if (rc < 0 || (!chip->th_info.adc_tm_high_enable &&
!chip->th_info.adc_tm_low_enable))
atomic_dec(&chip->wq_cnt);
@@ -2290,6 +2364,8 @@ static void qpnp_adc_tm_high_thr_work(struct work_struct *work)
chip->adc_vote_enable = false;
}
+ pr_debug("thr:0x%x\n", chip->th_info.adc_tm_high_enable);
+
rc = qpnp_adc_tm_read_status(chip);
if (rc < 0)
pr_err("adc-tm high thr work failed\n");
@@ -2393,6 +2469,8 @@ static void qpnp_adc_tm_low_thr_work(struct work_struct *work)
chip->adc_vote_enable = false;
}
+ pr_debug("thr:0x%x\n", chip->th_info.adc_tm_low_enable);
+
rc = qpnp_adc_tm_read_status(chip);
if (rc < 0)
pr_err("adc-tm low thr work failed\n");
@@ -2483,7 +2561,7 @@ static irqreturn_t qpnp_adc_tm_low_thr_isr(int irq, void *data)
static int qpnp_adc_tm_rc_check_sensor_trip(struct qpnp_adc_tm_chip *chip,
u8 status_low, u8 status_high, int i,
- int sensor_low_notify_num, int sensor_high_notify_num)
+ int *sensor_low_notify_num, int *sensor_high_notify_num)
{
int rc = 0;
u8 ctl = 0, sensor_mask = 0;
@@ -2523,7 +2601,8 @@ static int qpnp_adc_tm_rc_check_sensor_trip(struct qpnp_adc_tm_chip *chip,
return IRQ_HANDLED;
}
}
- sensor_low_notify_num |= (status_low & 0x1);
+ *sensor_low_notify_num |= (status_low & 0x1);
+ chip->sensor[i].low_thr_triggered = true;
}
if ((status_high & 0x1) && (ctl & QPNP_BTM_Mn_MEAS_EN) &&
@@ -2553,7 +2632,8 @@ static int qpnp_adc_tm_rc_check_sensor_trip(struct qpnp_adc_tm_chip *chip,
return IRQ_HANDLED;
}
}
- sensor_high_notify_num |= (status_high & 0x1);
+ *sensor_high_notify_num |= (status_high & 0x1);
+ chip->sensor[i].high_thr_triggered = true;
}
}
@@ -2590,7 +2670,8 @@ static irqreturn_t qpnp_adc_tm_rc_thr_isr(int irq, void *data)
while (i < chip->max_channels_available) {
rc = qpnp_adc_tm_rc_check_sensor_trip(chip,
status_low, status_high, i,
- sensor_low_notify_num, sensor_high_notify_num);
+ &sensor_low_notify_num,
+ &sensor_high_notify_num);
if (rc) {
pr_err("Sensor trip read failed\n");
return IRQ_HANDLED;
@@ -2600,14 +2681,15 @@ static irqreturn_t qpnp_adc_tm_rc_thr_isr(int irq, void *data)
i++;
}
- if (sensor_low_notify_num || sensor_high_notify_num)
+ if (sensor_low_notify_num) {
atomic_inc(&chip->wq_cnt);
-
- if (sensor_low_notify_num)
queue_work(chip->low_thr_wq, &chip->trigger_low_thr_work);
+ }
- if (sensor_high_notify_num)
+ if (sensor_high_notify_num) {
+ atomic_inc(&chip->wq_cnt);
queue_work(chip->high_thr_wq, &chip->trigger_high_thr_work);
+ }
return IRQ_HANDLED;
}
@@ -2710,6 +2792,7 @@ int32_t qpnp_adc_tm_channel_measure(struct qpnp_adc_tm_chip *chip,
channel, scale_type, dt_index);
param->gain_num = qpnp_vadc_amux_scaling_ratio[amux_prescaling].num;
param->gain_den = qpnp_vadc_amux_scaling_ratio[amux_prescaling].den;
+ param->adc_tm_hc = chip->adc_tm_hc;
chip->adc->amux_prop->amux_channel = channel;
chip->adc->amux_prop->decimation =
chip->adc->adc_channels[dt_index].adc_decimation;
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 49bed21b1284..4d35de1c14c5 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -92,6 +92,13 @@ MODULE_PARM_DESC(cpu_to_affin, "affin usb irq to this cpu");
#define PIPE3_PHYSTATUS_SW BIT(3)
#define PIPE_UTMI_CLK_DIS BIT(8)
+#define HS_PHY_CTRL_REG (QSCRATCH_REG_OFFSET + 0x10)
+#define UTMI_OTG_VBUS_VALID BIT(20)
+#define SW_SESSVLD_SEL BIT(28)
+
+#define SS_PHY_CTRL_REG (QSCRATCH_REG_OFFSET + 0x30)
+#define LANE0_PWR_PRESENT BIT(24)
+
/* GSI related registers */
#define GSI_TRB_ADDR_BIT_53_MASK (1 << 21)
#define GSI_TRB_ADDR_BIT_55_MASK (1 << 23)
@@ -1796,6 +1803,36 @@ static void dwc3_msm_bus_vote_w(struct work_struct *w)
dev_err(mdwc->dev, "Failed to reset bus bw vote %d\n", ret);
}
+static void dwc3_set_phy_speed_flags(struct dwc3_msm *mdwc)
+{
+ struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
+ int i, num_ports;
+ u32 reg;
+
+ mdwc->hs_phy->flags &= ~(PHY_HSFS_MODE | PHY_LS_MODE);
+ if (mdwc->in_host_mode) {
+ reg = dwc3_msm_read_reg(mdwc->base, USB3_HCSPARAMS1);
+ num_ports = HCS_MAX_PORTS(reg);
+ for (i = 0; i < num_ports; i++) {
+ reg = dwc3_msm_read_reg(mdwc->base,
+ USB3_PORTSC + i*0x10);
+ if (reg & PORT_PE) {
+ if (DEV_HIGHSPEED(reg) || DEV_FULLSPEED(reg))
+ mdwc->hs_phy->flags |= PHY_HSFS_MODE;
+ else if (DEV_LOWSPEED(reg))
+ mdwc->hs_phy->flags |= PHY_LS_MODE;
+ }
+ }
+ } else {
+ if (dwc->gadget.speed == USB_SPEED_HIGH ||
+ dwc->gadget.speed == USB_SPEED_FULL)
+ mdwc->hs_phy->flags |= PHY_HSFS_MODE;
+ else if (dwc->gadget.speed == USB_SPEED_LOW)
+ mdwc->hs_phy->flags |= PHY_LS_MODE;
+ }
+}
+
+
static int dwc3_msm_suspend(struct dwc3_msm *mdwc)
{
int ret, i;
@@ -1869,6 +1906,7 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc)
/* disable power event irq, hs and ss phy irq is used as wake up src */
disable_irq(mdwc->pwr_event_irq);
+ dwc3_set_phy_speed_flags(mdwc);
/* Suspend HS PHY */
usb_phy_set_suspend(mdwc->hs_phy, 1);
@@ -2018,6 +2056,7 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc)
mdwc->lpm_flags &= ~MDWC3_SS_PHY_SUSPEND;
}
+ mdwc->hs_phy->flags &= ~(PHY_HSFS_MODE | PHY_LS_MODE);
/* Resume HS PHY */
usb_phy_set_suspend(mdwc->hs_phy, 0);
@@ -2334,11 +2373,18 @@ static int dwc3_msm_get_clk_gdsc(struct dwc3_msm *mdwc)
if (IS_ERR(mdwc->bus_aggr_clk))
mdwc->bus_aggr_clk = NULL;
- mdwc->cfg_ahb_clk = devm_clk_get(mdwc->dev, "cfg_ahb_clk");
- if (IS_ERR(mdwc->cfg_ahb_clk)) {
- dev_err(mdwc->dev, "failed to get cfg_ahb_clk\n");
- ret = PTR_ERR(mdwc->cfg_ahb_clk);
- return ret;
+ if (of_property_match_string(mdwc->dev->of_node,
+ "clock-names", "cfg_ahb_clk") >= 0) {
+ mdwc->cfg_ahb_clk = devm_clk_get(mdwc->dev, "cfg_ahb_clk");
+ if (IS_ERR(mdwc->cfg_ahb_clk)) {
+ ret = PTR_ERR(mdwc->cfg_ahb_clk);
+ mdwc->cfg_ahb_clk = NULL;
+ if (ret != -EPROBE_DEFER)
+ dev_err(mdwc->dev,
+ "failed to get cfg_ahb_clk ret %d\n",
+ ret);
+ return ret;
+ }
}
return 0;
@@ -3051,6 +3097,25 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on)
return 0;
}
+static void dwc3_override_vbus_status(struct dwc3_msm *mdwc, bool vbus_present)
+{
+ struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
+
+ /* Update OTG VBUS Valid from HSPHY to controller */
+ dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG,
+ vbus_present ? UTMI_OTG_VBUS_VALID | SW_SESSVLD_SEL :
+ UTMI_OTG_VBUS_VALID,
+ vbus_present ? UTMI_OTG_VBUS_VALID | SW_SESSVLD_SEL : 0);
+
+ /* Update only if Super Speed is supported */
+ if (dwc->maximum_speed == USB_SPEED_SUPER) {
+ /* Update VBUS Valid from SSPHY to controller */
+ dwc3_msm_write_readback(mdwc->base, SS_PHY_CTRL_REG,
+ LANE0_PWR_PRESENT,
+ vbus_present ? LANE0_PWR_PRESENT : 0);
+ }
+}
+
/**
* dwc3_otg_start_peripheral - bind/unbind the peripheral controller.
*
@@ -3071,6 +3136,7 @@ static int dwc3_otg_start_peripheral(struct dwc3_msm *mdwc, int on)
dev_dbg(mdwc->dev, "%s: turn on gadget %s\n",
__func__, dwc->gadget.name);
+ dwc3_override_vbus_status(mdwc, true);
usb_phy_notify_connect(mdwc->hs_phy, USB_SPEED_HIGH);
usb_phy_notify_connect(mdwc->ss_phy, USB_SPEED_SUPER);
@@ -3086,6 +3152,7 @@ static int dwc3_otg_start_peripheral(struct dwc3_msm *mdwc, int on)
usb_gadget_vbus_disconnect(&dwc->gadget);
usb_phy_notify_disconnect(mdwc->hs_phy, USB_SPEED_HIGH);
usb_phy_notify_disconnect(mdwc->ss_phy, USB_SPEED_SUPER);
+ dwc3_override_vbus_status(mdwc, false);
dwc3_usb3_phy_suspend(dwc, false);
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 35994b827549..7d97aeb21340 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -723,7 +723,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
* due to stale trbs with HWO bit set from previous composition when update
* transfer cmd is issued.
*/
- if (dep->number > 1) {
+ if (dep->number > 1 && dep->trb_pool) {
memset(&dep->trb_pool[0], 0,
sizeof(struct dwc3_trb) * dep->num_trbs);
dbg_event(dep->number, "Clr_TRB", 0);
diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c
index 58b847a4a5f4..5612645d7237 100644
--- a/drivers/usb/gadget/function/f_gsi.c
+++ b/drivers/usb/gadget/function/f_gsi.c
@@ -257,7 +257,13 @@ static int ipa_connect_channels(struct gsi_data_port *d_port)
in_params->xfer_ring_len = gsi_channel_info.xfer_ring_len;
in_params->xfer_ring_base_addr = gsi_channel_info.xfer_ring_base_addr;
in_params->xfer_scratch.last_trb_addr_iova =
- d_port->in_last_trb_addr = gsi_channel_info.last_trb_addr;
+ gsi_channel_info.last_trb_addr;
+ in_params->xfer_ring_base_addr = in_params->xfer_ring_base_addr_iova =
+ gsi_channel_info.xfer_ring_base_addr;
+ in_params->data_buff_base_len = d_port->in_request.buf_len *
+ d_port->in_request.num_bufs;
+ in_params->data_buff_base_addr = in_params->data_buff_base_addr_iova =
+ d_port->in_request.dma;
in_params->xfer_scratch.const_buffer_size =
gsi_channel_info.const_buffer_size;
in_params->xfer_scratch.depcmd_low_addr =
@@ -289,7 +295,13 @@ static int ipa_connect_channels(struct gsi_data_port *d_port)
out_params->xfer_ring_len =
gsi_channel_info.xfer_ring_len;
out_params->xfer_ring_base_addr =
+ out_params->xfer_ring_base_addr_iova =
gsi_channel_info.xfer_ring_base_addr;
+ out_params->data_buff_base_len = d_port->out_request.buf_len *
+ d_port->out_request.num_bufs;
+ out_params->data_buff_base_addr =
+ out_params->data_buff_base_addr_iova =
+ d_port->out_request.dma;
out_params->xfer_scratch.last_trb_addr_iova =
gsi_channel_info.last_trb_addr;
out_params->xfer_scratch.const_buffer_size =
diff --git a/drivers/usb/phy/phy-msm-qusb-v2.c b/drivers/usb/phy/phy-msm-qusb-v2.c
index 1bc6b6ba4517..dd8149ef097d 100644
--- a/drivers/usb/phy/phy-msm-qusb-v2.c
+++ b/drivers/usb/phy/phy-msm-qusb-v2.c
@@ -54,10 +54,6 @@
#define QUSB2PHY_PORT_TUNE2 0x240
-#define HS_PHY_CTRL_REG 0x10
-#define UTMI_OTG_VBUS_VALID BIT(20)
-#define SW_SESSVLD_SEL BIT(28)
-
#define QUSB2PHY_1P8_VOL_MIN 1800000 /* uV */
#define QUSB2PHY_1P8_VOL_MAX 1800000 /* uV */
#define QUSB2PHY_1P8_HPM_LOAD 30000 /* uA */
@@ -66,6 +62,9 @@
#define QUSB2PHY_3P3_VOL_MAX 3200000 /* uV */
#define QUSB2PHY_3P3_HPM_LOAD 30000 /* uA */
+#define LINESTATE_DP BIT(0)
+#define LINESTATE_DM BIT(1)
+
unsigned int phy_tune2;
module_param(phy_tune2, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(phy_tune2, "QUSB PHY v2 TUNE2");
@@ -73,7 +72,6 @@ MODULE_PARM_DESC(phy_tune2, "QUSB PHY v2 TUNE2");
struct qusb_phy {
struct usb_phy phy;
void __iomem *base;
- void __iomem *qscratch_base;
void __iomem *tune2_efuse_reg;
struct clk *ref_clk_src;
@@ -87,6 +85,8 @@ struct qusb_phy {
int vdd_levels[3]; /* none, low, high */
int init_seq_len;
int *qusb_phy_init_seq;
+ int host_init_seq_len;
+ int *qusb_phy_host_init_seq;
u32 tune2_val;
int tune2_efuse_bit_pos;
@@ -374,6 +374,35 @@ static void qusb_phy_write_seq(void __iomem *base, u32 *seq, int cnt,
}
}
+static void qusb_phy_host_init(struct usb_phy *phy)
+{
+ u8 reg;
+ struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy);
+
+ dev_dbg(phy->dev, "%s\n", __func__);
+
+ /* Perform phy reset */
+ clk_reset(qphy->phy_reset, CLK_RESET_ASSERT);
+ usleep_range(100, 150);
+ clk_reset(qphy->phy_reset, CLK_RESET_DEASSERT);
+
+ qusb_phy_write_seq(qphy->base, qphy->qusb_phy_host_init_seq,
+ qphy->host_init_seq_len, 0);
+
+ /* Ensure above write is completed before turning ON ref clk */
+ wmb();
+
+ /* Require to get phy pll lock successfully */
+ usleep_range(150, 160);
+
+ reg = readb_relaxed(qphy->base + QUSB2PHY_PLL_COMMON_STATUS_ONE);
+ dev_dbg(phy->dev, "QUSB2PHY_PLL_COMMON_STATUS_ONE:%x\n", reg);
+ if (!(reg & CORE_READY_STATUS)) {
+ dev_err(phy->dev, "QUSB PHY PLL LOCK fails:%x\n", reg);
+ WARN_ON(1);
+ }
+}
+
static int qusb_phy_init(struct usb_phy *phy)
{
struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy);
@@ -489,6 +518,20 @@ static void qusb_phy_shutdown(struct usb_phy *phy)
qusb_phy_enable_clocks(qphy, false);
}
+
+static u32 qusb_phy_get_linestate(struct qusb_phy *qphy)
+{
+ u32 linestate = 0;
+
+ if (qphy->cable_connected) {
+ if (qphy->phy.flags & PHY_HSFS_MODE)
+ linestate |= LINESTATE_DP;
+ else if (qphy->phy.flags & PHY_LS_MODE)
+ linestate |= LINESTATE_DM;
+ }
+ return linestate;
+}
+
/**
* Performs QUSB2 PHY suspend/resume functionality.
*
@@ -515,8 +558,7 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend)
writel_relaxed(0x00,
qphy->base + QUSB2PHY_INTR_CTRL);
- linestate = readl_relaxed(qphy->base +
- QUSB2PHY_INTR_STAT);
+ linestate = qusb_phy_get_linestate(qphy);
/*
* D+/D- interrupts are level-triggered, but we are
* only interested if the line state changes, so enable
@@ -527,14 +569,17 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend)
* configure the mask to trigger on D+ low OR D- high
*/
intr_mask = DMSE_INTERRUPT | DPSE_INTERRUPT;
- if (!(linestate & DPSE_INTR_EN)) /* D+ low */
+ if (!(linestate & LINESTATE_DP)) /* D+ low */
intr_mask |= DPSE_INTR_HIGH_SEL;
- if (!(linestate & DMSE_INTR_EN)) /* D- low */
+ if (!(linestate & LINESTATE_DM)) /* D- low */
intr_mask |= DMSE_INTR_HIGH_SEL;
writel_relaxed(intr_mask,
qphy->base + QUSB2PHY_INTR_CTRL);
+ dev_dbg(phy->dev, "%s: intr_mask = %x\n",
+ __func__, intr_mask);
+
/* Makes sure that above write goes through */
wmb();
qusb_phy_enable_clocks(qphy, false);
@@ -575,25 +620,6 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend)
return 0;
}
-static void qusb_write_readback(void *base, u32 offset,
- const u32 mask, u32 val)
-{
- u32 write_val, tmp = readl_relaxed(base + offset);
-
- tmp &= ~mask; /* retain other bits */
- write_val = tmp | val;
-
- writel_relaxed(write_val, base + offset);
-
- /* Read back to see if val was written */
- tmp = readl_relaxed(base + offset);
- tmp &= mask; /* clear other bits */
-
- if (tmp != val)
- pr_err("%s: write: %x to QSCRATCH: %x FAILED\n",
- __func__, val, offset);
-}
-
static int qusb_phy_notify_connect(struct usb_phy *phy,
enum usb_device_speed speed)
{
@@ -601,18 +627,11 @@ static int qusb_phy_notify_connect(struct usb_phy *phy,
qphy->cable_connected = true;
- dev_dbg(phy->dev, " cable_connected=%d\n", qphy->cable_connected);
+ if (qphy->qusb_phy_host_init_seq && qphy->phy.flags & PHY_HOST_MODE)
+ qusb_phy_host_init(phy);
- /* Set OTG VBUS Valid from HSPHY to controller */
- qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG,
- UTMI_OTG_VBUS_VALID,
- UTMI_OTG_VBUS_VALID);
-
- /* Indicate value is driven by UTMI_OTG_VBUS_VALID bit */
- qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG,
- SW_SESSVLD_SEL, SW_SESSVLD_SEL);
-
- dev_dbg(phy->dev, "QUSB2 phy connect notification\n");
+ dev_dbg(phy->dev, "QUSB PHY: connect notification cable_connected=%d\n",
+ qphy->cable_connected);
return 0;
}
@@ -623,17 +642,8 @@ static int qusb_phy_notify_disconnect(struct usb_phy *phy,
qphy->cable_connected = false;
- dev_dbg(phy->dev, " cable_connected=%d\n", qphy->cable_connected);
-
- /* Set OTG VBUS Valid from HSPHY to controller */
- qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG,
- UTMI_OTG_VBUS_VALID, 0);
-
- /* Indicate value is driven by UTMI_OTG_VBUS_VALID bit */
- qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG,
- SW_SESSVLD_SEL, 0);
-
- dev_dbg(phy->dev, "QUSB2 phy disconnect notification\n");
+ dev_dbg(phy->dev, "QUSB PHY: connect notification cable_connected=%d\n",
+ qphy->cable_connected);
return 0;
}
@@ -715,16 +725,6 @@ static int qusb_phy_probe(struct platform_device *pdev)
return PTR_ERR(qphy->base);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "qscratch_base");
- if (res) {
- qphy->qscratch_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(qphy->qscratch_base)) {
- dev_dbg(dev, "couldn't ioremap qscratch_base\n");
- qphy->qscratch_base = NULL;
- }
- }
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"emu_phy_base");
if (res) {
qphy->emu_phy_base = devm_ioremap_resource(dev, res);
@@ -767,9 +767,17 @@ static int qusb_phy_probe(struct platform_device *pdev)
else
clk_set_rate(qphy->ref_clk, 19200000);
- qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb_clk");
- if (IS_ERR(qphy->cfg_ahb_clk))
- return PTR_ERR(qphy->cfg_ahb_clk);
+ if (of_property_match_string(pdev->dev.of_node,
+ "clock-names", "cfg_ahb_clk") >= 0) {
+ qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb_clk");
+ if (IS_ERR(qphy->cfg_ahb_clk)) {
+ ret = PTR_ERR(qphy->cfg_ahb_clk);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev,
+ "clk get failed for cfg_ahb_clk ret %d\n", ret);
+ return ret;
+ }
+ }
qphy->phy_reset = devm_clk_get(dev, "phy_reset");
if (IS_ERR(qphy->phy_reset))
@@ -866,6 +874,23 @@ static int qusb_phy_probe(struct platform_device *pdev)
}
}
+ qphy->host_init_seq_len = of_property_count_elems_of_size(dev->of_node,
+ "qcom,qusb-phy-host-init-seq",
+ sizeof(*qphy->qusb_phy_host_init_seq));
+ if (qphy->host_init_seq_len > 0) {
+ qphy->qusb_phy_host_init_seq = devm_kcalloc(dev,
+ qphy->host_init_seq_len,
+ sizeof(*qphy->qusb_phy_host_init_seq),
+ GFP_KERNEL);
+ if (qphy->qusb_phy_host_init_seq)
+ of_property_read_u32_array(dev->of_node,
+ "qcom,qusb-phy-host-init-seq",
+ qphy->qusb_phy_host_init_seq,
+ qphy->host_init_seq_len);
+ else
+ return -ENOMEM;
+ }
+
ret = of_property_read_u32_array(dev->of_node, "qcom,vdd-voltage-level",
(u32 *) qphy->vdd_levels,
ARRAY_SIZE(qphy->vdd_levels));
@@ -899,11 +924,8 @@ static int qusb_phy_probe(struct platform_device *pdev)
qphy->phy.set_suspend = qusb_phy_set_suspend;
qphy->phy.shutdown = qusb_phy_shutdown;
qphy->phy.type = USB_PHY_TYPE_USB2;
-
- if (qphy->qscratch_base) {
- qphy->phy.notify_connect = qusb_phy_notify_connect;
- qphy->phy.notify_disconnect = qusb_phy_notify_disconnect;
- }
+ qphy->phy.notify_connect = qusb_phy_notify_connect;
+ qphy->phy.notify_disconnect = qusb_phy_notify_disconnect;
ret = usb_add_phy_dev(&qphy->phy);
if (ret)
diff --git a/drivers/usb/phy/phy-msm-qusb.c b/drivers/usb/phy/phy-msm-qusb.c
index 1eb0c8d6b62f..325f5fcf161b 100644
--- a/drivers/usb/phy/phy-msm-qusb.c
+++ b/drivers/usb/phy/phy-msm-qusb.c
@@ -88,9 +88,6 @@
#define LINESTATE_DP BIT(0)
#define LINESTATE_DM BIT(1)
-#define HS_PHY_CTRL_REG 0x10
-#define UTMI_OTG_VBUS_VALID BIT(20)
-#define SW_SESSVLD_SEL BIT(28)
#define QUSB2PHY_1P8_VOL_MIN 1800000 /* uV */
#define QUSB2PHY_1P8_VOL_MAX 1800000 /* uV */
@@ -109,7 +106,6 @@ MODULE_PARM_DESC(tune2, "QUSB PHY TUNE2");
struct qusb_phy {
struct usb_phy phy;
void __iomem *base;
- void __iomem *qscratch_base;
void __iomem *tune2_efuse_reg;
void __iomem *ref_clk_base;
@@ -686,24 +682,6 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend)
return 0;
}
-static void qusb_write_readback(void *base, u32 offset,
- const u32 mask, u32 val)
-{
- u32 write_val, tmp = readl_relaxed(base + offset);
- tmp &= ~mask; /* retain other bits */
- write_val = tmp | val;
-
- writel_relaxed(write_val, base + offset);
-
- /* Read back to see if val was written */
- tmp = readl_relaxed(base + offset);
- tmp &= mask; /* clear other bits */
-
- if (tmp != val)
- pr_err("%s: write: %x to QSCRATCH: %x FAILED\n",
- __func__, val, offset);
-}
-
static int qusb_phy_notify_connect(struct usb_phy *phy,
enum usb_device_speed speed)
{
@@ -711,18 +689,8 @@ static int qusb_phy_notify_connect(struct usb_phy *phy,
qphy->cable_connected = true;
- dev_dbg(phy->dev, " cable_connected=%d\n", qphy->cable_connected);
-
- /* Set OTG VBUS Valid from HSPHY to controller */
- qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG,
- UTMI_OTG_VBUS_VALID,
- UTMI_OTG_VBUS_VALID);
-
- /* Indicate value is driven by UTMI_OTG_VBUS_VALID bit */
- qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG,
- SW_SESSVLD_SEL, SW_SESSVLD_SEL);
-
- dev_dbg(phy->dev, "QUSB2 phy connect notification\n");
+ dev_dbg(phy->dev, "QUSB PHY: connect notification cable_connected=%d\n",
+ qphy->cable_connected);
return 0;
}
@@ -733,17 +701,8 @@ static int qusb_phy_notify_disconnect(struct usb_phy *phy,
qphy->cable_connected = false;
- dev_dbg(phy->dev, " cable_connected=%d\n", qphy->cable_connected);
-
- /* Set OTG VBUS Valid from HSPHY to controller */
- qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG,
- UTMI_OTG_VBUS_VALID, 0);
-
- /* Indicate value is driven by UTMI_OTG_VBUS_VALID bit */
- qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG,
- SW_SESSVLD_SEL, 0);
-
- dev_dbg(phy->dev, "QUSB2 phy disconnect notification\n");
+ dev_dbg(phy->dev, "QUSB PHY: connect notification cable_connected=%d\n",
+ qphy->cable_connected);
return 0;
}
@@ -827,16 +786,6 @@ static int qusb_phy_probe(struct platform_device *pdev)
return PTR_ERR(qphy->base);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "qscratch_base");
- if (res) {
- qphy->qscratch_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(qphy->qscratch_base)) {
- dev_dbg(dev, "couldn't ioremap qscratch_base\n");
- qphy->qscratch_base = NULL;
- }
- }
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"emu_phy_base");
if (res) {
qphy->emu_phy_base = devm_ioremap_resource(dev, res);
@@ -1051,11 +1000,8 @@ static int qusb_phy_probe(struct platform_device *pdev)
qphy->phy.set_suspend = qusb_phy_set_suspend;
qphy->phy.shutdown = qusb_phy_shutdown;
qphy->phy.type = USB_PHY_TYPE_USB2;
-
- if (qphy->qscratch_base) {
- qphy->phy.notify_connect = qusb_phy_notify_connect;
- qphy->phy.notify_disconnect = qusb_phy_notify_disconnect;
- }
+ qphy->phy.notify_connect = qusb_phy_notify_connect;
+ qphy->phy.notify_disconnect = qusb_phy_notify_disconnect;
/*
* On some platforms multiple QUSB PHYs are available. If QUSB PHY is
diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c
index 50b63f912638..794bc2e33695 100644
--- a/drivers/usb/phy/phy-msm-ssusb-qmp.c
+++ b/drivers/usb/phy/phy-msm-ssusb-qmp.c
@@ -547,13 +547,16 @@ static int msm_ssphy_qmp_probe(struct platform_device *pdev)
clk_set_rate(phy->aux_clk, clk_round_rate(phy->aux_clk, ULONG_MAX));
- phy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb_clk");
- if (IS_ERR(phy->cfg_ahb_clk)) {
- ret = PTR_ERR(phy->cfg_ahb_clk);
- phy->cfg_ahb_clk = NULL;
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get cfg_ahb_clk\n");
- goto err;
+ if (of_property_match_string(pdev->dev.of_node,
+ "clock-names", "cfg_ahb_clk") >= 0) {
+ phy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb_clk");
+ if (IS_ERR(phy->cfg_ahb_clk)) {
+ ret = PTR_ERR(phy->cfg_ahb_clk);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev,
+ "failed to get cfg_ahb_clk ret %d\n", ret);
+ goto err;
+ }
}
phy->pipe_clk = devm_clk_get(dev, "pipe_clk");
diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c
index 07e3445c51f7..a99ae97cdb80 100644
--- a/drivers/video/fbdev/msm/mdss_dp.c
+++ b/drivers/video/fbdev/msm/mdss_dp.c
@@ -199,6 +199,97 @@ exit:
return rc;
} /* mdss_dp_get_dt_clk_data */
+static int mdss_dp_clk_init(struct mdss_dp_drv_pdata *dp_drv,
+ struct device *dev, bool initialize)
+{
+ struct dss_module_power *core_power_data = NULL;
+ struct dss_module_power *ctrl_power_data = NULL;
+ int rc = 0;
+
+ if (!dp_drv || !dev) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ core_power_data = &dp_drv->power_data[DP_CORE_PM];
+ ctrl_power_data = &dp_drv->power_data[DP_CTRL_PM];
+
+ if (!core_power_data || !ctrl_power_data) {
+ pr_err("invalid power_data\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if (initialize) {
+ rc = msm_dss_get_clk(dev, core_power_data->clk_config,
+ core_power_data->num_clk);
+ if (rc) {
+ DEV_ERR("Failed to get %s clk. Err=%d\n",
+ __mdss_dp_pm_name(DP_CORE_PM), rc);
+ goto exit;
+ }
+
+ rc = msm_dss_get_clk(dev, ctrl_power_data->clk_config,
+ ctrl_power_data->num_clk);
+ if (rc) {
+ DEV_ERR("Failed to get %s clk. Err=%d\n",
+ __mdss_dp_pm_name(DP_CTRL_PM), rc);
+ goto ctrl_get_error;
+ }
+
+ } else {
+ msm_dss_put_clk(ctrl_power_data->clk_config,
+ ctrl_power_data->num_clk);
+ msm_dss_put_clk(core_power_data->clk_config,
+ core_power_data->num_clk);
+ }
+
+ return rc;
+
+ctrl_get_error:
+ msm_dss_put_clk(core_power_data->clk_config,
+ core_power_data->num_clk);
+
+exit:
+ return rc;
+}
+
+static int mdss_dp_clk_set_rate_enable(
+ struct dss_module_power *power_data,
+ bool enable)
+{
+ int ret = 0;
+
+ if (enable) {
+ ret = msm_dss_clk_set_rate(
+ power_data->clk_config,
+ power_data->num_clk);
+ if (ret) {
+ pr_err("failed to set clks rate.\n");
+ goto exit;
+ }
+
+ ret = msm_dss_enable_clk(
+ power_data->clk_config,
+ power_data->num_clk, 1);
+ if (ret) {
+ pr_err("failed to enable clks\n");
+ goto exit;
+ }
+ } else {
+ ret = msm_dss_enable_clk(
+ power_data->clk_config,
+ power_data->num_clk, 0);
+ if (ret) {
+ pr_err("failed to disable clks\n");
+ goto exit;
+ }
+ }
+exit:
+ return ret;
+}
+
/*
* This clock control function supports enabling/disabling
* of core and ctrl power module clocks
@@ -232,35 +323,27 @@ static int mdss_dp_clk_ctrl(struct mdss_dp_drv_pdata *dp_drv,
&& (!dp_drv->core_clks_on)) {
pr_debug("Need to enable core clks before link clks\n");
- ret = msm_dss_enable_clk(
- dp_drv->power_data[DP_CORE_PM].clk_config,
- dp_drv->power_data[DP_CORE_PM].num_clk, 1);
+ ret = mdss_dp_clk_set_rate_enable(
+ &dp_drv->power_data[DP_CORE_PM],
+ enable);
if (ret) {
- pr_err("failed to enable clks for %s\n",
- __mdss_dp_pm_name(pm_type));
+ pr_err("failed to enable clks: %s. err=%d\n",
+ __mdss_dp_pm_name(DP_CORE_PM), ret);
goto error;
} else {
dp_drv->core_clks_on = true;
}
}
+ }
- ret = msm_dss_enable_clk(
- dp_drv->power_data[pm_type].clk_config,
- dp_drv->power_data[pm_type].num_clk, 1);
- if (ret) {
- pr_err("failed to enable clks for %s\n",
- __mdss_dp_pm_name(pm_type));
- goto error;
- }
- } else {
- ret = msm_dss_enable_clk(
- dp_drv->power_data[pm_type].clk_config,
- dp_drv->power_data[pm_type].num_clk, 0);
- if (ret) {
- pr_err("failed to disable clks for %s\n",
- __mdss_dp_pm_name(pm_type));
- goto error;
- }
+ ret = mdss_dp_clk_set_rate_enable(
+ &dp_drv->power_data[pm_type],
+ enable);
+ if (ret) {
+ pr_err("failed to '%s' clks for: %s. err=%d\n",
+ enable ? "enable" : "disable",
+ __mdss_dp_pm_name(pm_type), ret);
+ goto error;
}
if (pm_type == DP_CORE_PM)
@@ -275,7 +358,7 @@ error:
static int mdss_dp_regulator_ctrl(struct mdss_dp_drv_pdata *dp_drv,
bool enable)
{
- int i, ret = 0;
+ int ret = 0, i = 0, j = 0;
if (dp_drv->core_power == enable) {
pr_debug("regulators already %s\n",
@@ -283,27 +366,23 @@ static int mdss_dp_regulator_ctrl(struct mdss_dp_drv_pdata *dp_drv,
return 0;
}
- if (enable) {
- for (i = DP_CORE_PM; i < DP_MAX_PM; i++) {
- ret = msm_dss_enable_vreg(
- dp_drv->power_data[i].vreg_config,
- dp_drv->power_data[i].num_vreg, 1);
- if (ret) {
- pr_err("failed to enable vregs for %s\n",
+ for (i = DP_CORE_PM; i < DP_MAX_PM; i++) {
+ ret = msm_dss_enable_vreg(
+ dp_drv->power_data[i].vreg_config,
+ dp_drv->power_data[i].num_vreg, enable);
+ if (ret) {
+ pr_err("failed to '%s' vregs for %s\n",
+ enable ? "enable" : "disable",
__mdss_dp_pm_name(i));
- goto error;
- }
- }
- } else {
- for (i = DP_CORE_PM; i < DP_MAX_PM; i++) {
- ret = msm_dss_enable_vreg(
- dp_drv->power_data[i].vreg_config,
- dp_drv->power_data[i].num_vreg, 1);
- if (ret) {
- pr_err("failed to disable vregs for %s\n",
- __mdss_dp_pm_name(i));
- goto error;
+ if (enable) {
+ /* Disabling the enabled vregs */
+ for (j = i-1; j >= DP_CORE_PM; j--) {
+ msm_dss_enable_vreg(
+ dp_drv->power_data[j].vreg_config,
+ dp_drv->power_data[j].num_vreg, 0);
+ }
}
+ goto error;
}
}
@@ -472,6 +551,218 @@ static int mdss_dp_regulator_init(struct platform_device *pdev,
return rc;
}
+static int mdss_dp_pinctrl_set_state(
+ struct mdss_dp_drv_pdata *dp,
+ bool active)
+{
+ struct pinctrl_state *pin_state;
+ int rc = -EFAULT;
+
+ if (IS_ERR_OR_NULL(dp->pin_res.pinctrl))
+ return PTR_ERR(dp->pin_res.pinctrl);
+
+ pin_state = active ? dp->pin_res.state_active
+ : dp->pin_res.state_suspend;
+ if (!IS_ERR_OR_NULL(pin_state)) {
+ rc = pinctrl_select_state(dp->pin_res.pinctrl,
+ pin_state);
+ if (rc)
+ pr_err("can not set %s pins\n",
+ active ? "mdss_dp_active"
+ : "mdss_dp_sleep");
+ } else {
+ pr_err("invalid '%s' pinstate\n",
+ active ? "mdss_dp_active"
+ : "mdss_dp_sleep");
+ }
+ return rc;
+}
+
+static int mdss_dp_pinctrl_init(struct platform_device *pdev,
+ struct mdss_dp_drv_pdata *dp)
+{
+ dp->pin_res.pinctrl = devm_pinctrl_get(&pdev->dev);
+ if (IS_ERR_OR_NULL(dp->pin_res.pinctrl)) {
+ pr_err("failed to get pinctrl\n");
+ return PTR_ERR(dp->pin_res.pinctrl);
+ }
+
+ dp->pin_res.state_active
+ = pinctrl_lookup_state(dp->pin_res.pinctrl,
+ "mdss_dp_active");
+ if (IS_ERR_OR_NULL(dp->pin_res.state_active)) {
+ pr_err("can not get dp active pinstate\n");
+ return PTR_ERR(dp->pin_res.state_active);
+ }
+
+ dp->pin_res.state_suspend
+ = pinctrl_lookup_state(dp->pin_res.pinctrl,
+ "mdss_dp_sleep");
+ if (IS_ERR_OR_NULL(dp->pin_res.state_suspend)) {
+ pr_err("can not get dp sleep pinstate\n");
+ return PTR_ERR(dp->pin_res.state_suspend);
+ }
+
+ return 0;
+}
+
+static int mdss_dp_request_gpios(struct mdss_dp_drv_pdata *dp)
+{
+ int rc = 0;
+ struct device *dev = NULL;
+
+ if (!dp) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ dev = &dp->pdev->dev;
+ if (gpio_is_valid(dp->aux_en_gpio)) {
+ rc = devm_gpio_request(dev, dp->aux_en_gpio,
+ "aux_enable");
+ if (rc) {
+ pr_err("request aux_en gpio failed, rc=%d\n",
+ rc);
+ goto aux_en_gpio_err;
+ }
+ }
+ if (gpio_is_valid(dp->aux_sel_gpio)) {
+ rc = devm_gpio_request(dev, dp->aux_sel_gpio, "aux_sel");
+ if (rc) {
+ pr_err("request aux_sel gpio failed, rc=%d\n",
+ rc);
+ goto aux_sel_gpio_err;
+ }
+ }
+ if (gpio_is_valid(dp->usbplug_cc_gpio)) {
+ rc = devm_gpio_request(dev, dp->usbplug_cc_gpio,
+ "usbplug_cc");
+ if (rc) {
+ pr_err("request usbplug_cc gpio failed, rc=%d\n",
+ rc);
+ goto usbplug_cc_gpio_err;
+ }
+ }
+ if (gpio_is_valid(dp->hpd_gpio)) {
+ rc = devm_gpio_request(dev, dp->hpd_gpio, "hpd");
+ if (rc) {
+ pr_err("request hpd gpio failed, rc=%d\n",
+ rc);
+ goto hpd_gpio_err;
+ }
+ }
+ return rc;
+
+hpd_gpio_err:
+ if (gpio_is_valid(dp->usbplug_cc_gpio))
+ gpio_free(dp->usbplug_cc_gpio);
+usbplug_cc_gpio_err:
+ if (gpio_is_valid(dp->aux_sel_gpio))
+ gpio_free(dp->aux_sel_gpio);
+aux_sel_gpio_err:
+ if (gpio_is_valid(dp->aux_en_gpio))
+ gpio_free(dp->aux_en_gpio);
+aux_en_gpio_err:
+ return rc;
+}
+
+static int mdss_dp_config_gpios(struct mdss_dp_drv_pdata *dp, bool enable)
+{
+ int rc = 0;
+
+ if (enable == true) {
+ rc = mdss_dp_request_gpios(dp);
+ if (rc) {
+ pr_err("gpio request failed\n");
+ return rc;
+ }
+
+ if (gpio_is_valid(dp->aux_en_gpio)) {
+ rc = gpio_direction_output(
+ dp->aux_en_gpio, 0);
+ if (rc)
+ pr_err("unable to set dir for aux_en gpio\n");
+ }
+ if (gpio_is_valid(dp->aux_sel_gpio)) {
+ rc = gpio_direction_output(
+ dp->aux_sel_gpio, 0);
+ if (rc)
+ pr_err("unable to set dir for aux_sel gpio\n");
+ }
+ if (gpio_is_valid(dp->usbplug_cc_gpio)) {
+ gpio_set_value(
+ dp->usbplug_cc_gpio, 0);
+ }
+ if (gpio_is_valid(dp->hpd_gpio)) {
+ gpio_set_value(
+ dp->hpd_gpio, 1);
+ }
+ } else {
+ if (gpio_is_valid(dp->aux_en_gpio)) {
+ gpio_set_value((dp->aux_en_gpio), 0);
+ gpio_free(dp->aux_en_gpio);
+ }
+ if (gpio_is_valid(dp->aux_sel_gpio)) {
+ gpio_set_value((dp->aux_sel_gpio), 0);
+ gpio_free(dp->aux_sel_gpio);
+ }
+ if (gpio_is_valid(dp->usbplug_cc_gpio)) {
+ gpio_set_value((dp->usbplug_cc_gpio), 0);
+ gpio_free(dp->usbplug_cc_gpio);
+ }
+ if (gpio_is_valid(dp->hpd_gpio)) {
+ gpio_set_value((dp->hpd_gpio), 0);
+ gpio_free(dp->hpd_gpio);
+ }
+ }
+ return 0;
+}
+
+static int mdss_dp_parse_gpio_params(struct platform_device *pdev,
+ struct mdss_dp_drv_pdata *dp)
+{
+ dp->aux_en_gpio = of_get_named_gpio(
+ pdev->dev.of_node,
+ "qcom,aux-en-gpio", 0);
+
+ if (!gpio_is_valid(dp->aux_en_gpio)) {
+ pr_err("%d, Aux_en gpio not specified\n",
+ __LINE__);
+ return -EINVAL;
+ }
+
+ dp->aux_sel_gpio = of_get_named_gpio(
+ pdev->dev.of_node,
+ "qcom,aux-sel-gpio", 0);
+
+ if (!gpio_is_valid(dp->aux_sel_gpio)) {
+ pr_err("%d, Aux_sel gpio not specified\n",
+ __LINE__);
+ return -EINVAL;
+ }
+
+ dp->usbplug_cc_gpio = of_get_named_gpio(
+ pdev->dev.of_node,
+ "qcom,usbplug-cc-gpio", 0);
+
+ if (!gpio_is_valid(dp->usbplug_cc_gpio)) {
+ pr_err("%d,usbplug_cc gpio not specified\n",
+ __LINE__);
+ return -EINVAL;
+ }
+
+ dp->hpd_gpio = of_get_named_gpio(
+ pdev->dev.of_node,
+ "qcom,hpd-gpio", 0);
+
+ if (!gpio_is_valid(dp->hpd_gpio)) {
+ pr_info("%d,hpd gpio not specified\n",
+ __LINE__);
+ }
+
+ return 0;
+}
+
void mdss_dp_phy_initialize(struct mdss_dp_drv_pdata *dp)
{
/*
@@ -587,6 +878,8 @@ int mdss_dp_on(struct mdss_panel_data *pdata)
{
struct mdss_dp_drv_pdata *dp_drv = NULL;
int ret = 0;
+ enum plug_orientation orientation = ORIENTATION_NONE;
+ struct lane_mapping ln_map;
if (!pdata) {
pr_err("Invalid input data\n");
@@ -596,7 +889,15 @@ int mdss_dp_on(struct mdss_panel_data *pdata)
dp_drv = container_of(pdata, struct mdss_dp_drv_pdata,
panel_data);
- pr_debug("++ cont_splash=%d\n", dp_drv->cont_splash);
+ /* wait until link training is completed */
+ mutex_lock(&dp_drv->host_mutex);
+
+ pr_debug("Enter++ cont_splash=%d\n", dp_drv->cont_splash);
+ /* Default lane mapping */
+ ln_map.lane0 = 2;
+ ln_map.lane1 = 3;
+ ln_map.lane2 = 1;
+ ln_map.lane3 = 0;
if (!dp_drv->cont_splash) { /* vote for clocks */
ret = mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, true);
@@ -606,15 +907,52 @@ int mdss_dp_on(struct mdss_panel_data *pdata)
}
mdss_dp_phy_reset(&dp_drv->ctrl_io);
mdss_dp_aux_reset(&dp_drv->ctrl_io);
- mdss_dp_mainlink_reset(&dp_drv->ctrl_io);
mdss_dp_aux_ctrl(&dp_drv->ctrl_io, true);
mdss_dp_hpd_configure(&dp_drv->ctrl_io, true);
+ orientation = usbpd_get_plug_orientation(dp_drv->pd);
+ pr_debug("plug Orientation = %d\n", orientation);
+
+ if (orientation == ORIENTATION_CC2) {
+ /* update lane mapping */
+ ln_map.lane0 = 1;
+ ln_map.lane1 = 0;
+ ln_map.lane2 = 2;
+ ln_map.lane3 = 3;
+
+ if (gpio_is_valid(dp_drv->usbplug_cc_gpio)) {
+ gpio_set_value(
+ dp_drv->usbplug_cc_gpio, 1);
+ pr_debug("Configured cc gpio for new Orientation\n");
+ }
+
+ }
+
mdss_dp_phy_aux_setup(&dp_drv->phy_io);
mdss_dp_irq_enable(dp_drv);
pr_debug("irq enabled\n");
mdss_dp_dpcd_cap_read(dp_drv);
+ dp_drv->link_rate =
+ mdss_dp_gen_link_clk(&dp_drv->panel_data.panel_info,
+ dp_drv->dpcd.max_lane_count);
+
+ pr_debug("link_rate=0x%x, Max rate supported by sink=0x%x\n",
+ dp_drv->link_rate, dp_drv->dpcd.max_link_rate);
+ if (!dp_drv->link_rate) {
+ pr_err("Unable to configure required link rate\n");
+ return -EINVAL;
+ }
+
+ pr_debug("link_rate = 0x%x\n", dp_drv->link_rate);
+
+ dp_drv->power_data[DP_CTRL_PM].clk_config[0].rate =
+ dp_drv->link_rate * DP_LINK_RATE_MULTIPLIER;
+
+ dp_drv->pixel_rate = dp_drv->panel_data.panel_info.clk_rate;
+ dp_drv->power_data[DP_CTRL_PM].clk_config[3].rate =
+ dp_drv->pixel_rate;
+
ret = mdss_dp_clk_ctrl(dp_drv, DP_CTRL_PM, true);
if (ret) {
mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, false);
@@ -624,6 +962,7 @@ int mdss_dp_on(struct mdss_panel_data *pdata)
mdss_dp_mainlink_reset(&dp_drv->ctrl_io);
+ mdss_dp_ctrl_lane_mapping(&dp_drv->ctrl_io, ln_map);
reinit_completion(&dp_drv->idle_comp);
mdss_dp_fill_link_cfg(dp_drv);
mdss_dp_mainlink_ctrl(&dp_drv->ctrl_io, true);
@@ -645,7 +984,9 @@ int mdss_dp_on(struct mdss_panel_data *pdata)
if (mdss_dp_mainlink_ready(dp_drv, BIT(0)))
pr_debug("mainlink ready\n");
+ mutex_unlock(&dp_drv->host_mutex);
pr_debug("End-\n");
+
return ret;
}
@@ -680,6 +1021,9 @@ int mdss_dp_off(struct mdss_panel_data *pdata)
mdss_dp_mainlink_reset(&dp_drv->ctrl_io);
mdss_dp_mainlink_ctrl(&dp_drv->ctrl_io, false);
+ mdss_dp_config_gpios(dp_drv, false);
+ mdss_dp_pinctrl_set_state(dp_drv, false);
+
mdss_dp_aux_ctrl(&dp_drv->ctrl_io, false);
mdss_dp_clk_ctrl(dp_drv, DP_CTRL_PM, false);
mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, false);
@@ -712,6 +1056,9 @@ static int mdss_dp_host_init(struct mdss_panel_data *pdata)
goto vreg_error;
}
+ mdss_dp_pinctrl_set_state(dp_drv, true);
+ mdss_dp_config_gpios(dp_drv, true);
+
ret = mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, true);
if (ret) {
pr_err("Unabled to start core clocks\n");
@@ -725,6 +1072,10 @@ static int mdss_dp_host_init(struct mdss_panel_data *pdata)
mdss_dp_phy_initialize(dp_drv);
mdss_dp_aux_ctrl(&dp_drv->ctrl_io, true);
+ pr_debug("Ctrl_hw_rev =0x%x, phy hw_rev =0x%x\n",
+ mdss_dp_get_ctrl_hw_version(&dp_drv->ctrl_io),
+ mdss_dp_get_phy_hw_version(&dp_drv->phy_io));
+
return ret;
clk_error:
@@ -865,7 +1216,7 @@ static void mdss_dp_event_work(struct work_struct *work)
struct mdss_dp_drv_pdata *dp = NULL;
struct delayed_work *dw = to_delayed_work(work);
unsigned long flag;
- u32 todo = 0;
+ u32 todo = 0, dp_config_pkt[2];
if (!dw) {
pr_err("invalid work structure\n");
@@ -882,24 +1233,47 @@ static void mdss_dp_event_work(struct work_struct *work)
pr_debug("todo=%x\n", todo);
switch (todo) {
- case (EV_EDID_READ):
+ case EV_EDID_READ:
mdss_dp_edid_read(dp, 0);
break;
- case (EV_DPCD_CAP_READ):
+ case EV_DPCD_CAP_READ:
mdss_dp_dpcd_cap_read(dp);
break;
- case (EV_DPCD_STATUS_READ):
+ case EV_DPCD_STATUS_READ:
mdss_dp_dpcd_status_read(dp);
break;
- case (EV_LINK_TRAIN):
+ case EV_LINK_TRAIN:
mdss_dp_do_link_train(dp);
break;
- case (EV_VIDEO_READY):
+ case EV_VIDEO_READY:
mdss_dp_video_ready(dp);
break;
- case (EV_IDLE_PATTERNS_SENT):
+ case EV_IDLE_PATTERNS_SENT:
mdss_dp_idle_patterns_sent(dp);
break;
+ case EV_USBPD_DISCOVER_MODES:
+ usbpd_send_svdm(dp->pd, USB_C_DP_SID, USBPD_SVDM_DISCOVER_MODES,
+ SVDM_CMD_TYPE_INITIATOR, 0x0, 0x0, 0x0);
+ break;
+ case EV_USBPD_ENTER_MODE:
+ usbpd_send_svdm(dp->pd, USB_C_DP_SID, USBPD_SVDM_ENTER_MODE,
+ SVDM_CMD_TYPE_INITIATOR, 0x1, 0x0, 0x0);
+ break;
+ case EV_USBPD_EXIT_MODE:
+ usbpd_send_svdm(dp->pd, USB_C_DP_SID, USBPD_SVDM_EXIT_MODE,
+ SVDM_CMD_TYPE_INITIATOR, 0x1, 0x0, 0x0);
+ break;
+ case EV_USBPD_DP_STATUS:
+ usbpd_send_svdm(dp->pd, USB_C_DP_SID, DP_VDM_STATUS,
+ SVDM_CMD_TYPE_INITIATOR, 0x1, 0x0, 0x0);
+ break;
+ case EV_USBPD_DP_CONFIGURE:
+ dp_config_pkt[0] = SVDM_HDR(USB_C_DP_SID, VDM_VERSION, 0x1,
+ SVDM_CMD_TYPE_INITIATOR, DP_VDM_CONFIGURE);
+ dp_config_pkt[1] = mdss_dp_usbpd_gen_config_pkt(dp);
+ usbpd_send_svdm(dp->pd, USB_C_DP_SID, DP_VDM_CONFIGURE,
+ SVDM_CMD_TYPE_INITIATOR, 0x1, dp_config_pkt, 0x2);
+ break;
default:
pr_err("Unknown event:%d\n", todo);
}
@@ -987,6 +1361,165 @@ static int mdss_dp_event_setup(struct mdss_dp_drv_pdata *dp)
return 0;
}
+static void usbpd_connect_callback(struct usbpd_svid_handler *hdlr)
+{
+ struct mdss_dp_drv_pdata *dp_drv;
+
+ dp_drv = container_of(hdlr, struct mdss_dp_drv_pdata, svid_handler);
+ if (!dp_drv->pd) {
+ pr_err("get_usbpd phandle failed\n");
+ return;
+ }
+
+ mutex_lock(&dp_drv->pd_msg_mutex);
+ dp_drv->cable_connected = true;
+ dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES);
+ mutex_unlock(&dp_drv->pd_msg_mutex);
+ pr_debug("discover_mode event sent\n");
+}
+
+static void usbpd_disconnect_callback(struct usbpd_svid_handler *hdlr)
+{
+ struct mdss_dp_drv_pdata *dp_drv;
+
+ dp_drv = container_of(hdlr, struct mdss_dp_drv_pdata, svid_handler);
+ if (!dp_drv->pd) {
+ pr_err("get_usbpd phandle failed\n");
+ return;
+ }
+
+ pr_debug("cable disconnected\n");
+ mutex_lock(&dp_drv->pd_msg_mutex);
+ dp_drv->cable_connected = false;
+ mutex_unlock(&dp_drv->pd_msg_mutex);
+}
+
+static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd,
+ enum usbpd_svdm_cmd_type cmd_type,
+ const u32 *vdos, int num_vdos)
+{
+ struct mdss_dp_drv_pdata *dp_drv;
+
+ dp_drv = container_of(hdlr, struct mdss_dp_drv_pdata, svid_handler);
+ if (!dp_drv->pd) {
+ pr_err("get_usbpd phandle failed\n");
+ return;
+ }
+
+ pr_debug("callback -> cmd: 0x%x, *vdos = 0x%x, num_vdos = %d\n",
+ cmd, *vdos, num_vdos);
+
+ switch (cmd) {
+ case USBPD_SVDM_DISCOVER_MODES:
+ if (cmd_type == SVDM_CMD_TYPE_RESP_ACK) {
+ dp_drv->alt_mode.dp_cap.response = *vdos;
+ mdss_dp_usbpd_ext_capabilities
+ (&dp_drv->alt_mode.dp_cap);
+ dp_drv->alt_mode.current_state = DISCOVER_MODES_DONE;
+ dp_send_events(dp_drv, EV_USBPD_ENTER_MODE);
+ } else {
+ pr_err("unknown response: %d for Discover_modes\n",
+ cmd_type);
+ }
+ break;
+ case USBPD_SVDM_ENTER_MODE:
+ if (cmd_type == SVDM_CMD_TYPE_RESP_ACK) {
+ dp_drv->alt_mode.current_state = ENTER_MODE_DONE;
+ dp_send_events(dp_drv, EV_USBPD_DP_STATUS);
+ } else {
+ pr_err("unknown response: %d for Enter_mode\n",
+ cmd_type);
+ }
+ break;
+ case USBPD_SVDM_ATTENTION:
+ if (cmd_type == SVDM_CMD_TYPE_INITIATOR) {
+ pr_debug("Attention. cmd_type=%d\n",
+ cmd_type);
+ if (!dp_drv->alt_mode.current_state
+ == ENTER_MODE_DONE) {
+ pr_debug("sending discover_mode\n");
+ dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES);
+ break;
+ }
+ if (num_vdos == 1) {
+ dp_drv->alt_mode.dp_status.response = *vdos;
+ mdss_dp_usbpd_ext_dp_status
+ (&dp_drv->alt_mode.dp_status);
+ if (dp_drv->alt_mode.dp_status.hpd_high) {
+ pr_debug("HPD high\n");
+ dp_drv->alt_mode.current_state =
+ DP_STATUS_DONE;
+ dp_send_events
+ (dp_drv, EV_USBPD_DP_CONFIGURE);
+ }
+ }
+ } else {
+ pr_debug("unknown response: %d for Attention\n",
+ cmd_type);
+ }
+ break;
+ case DP_VDM_STATUS:
+ if (cmd_type == SVDM_CMD_TYPE_RESP_ACK) {
+ dp_drv->alt_mode.dp_status.response = *vdos;
+ mdss_dp_usbpd_ext_dp_status
+ (&dp_drv->alt_mode.dp_status);
+ if (dp_drv->alt_mode.dp_status.hpd_high) {
+ pr_debug("HDP high\n");
+ dp_drv->alt_mode.current_state =
+ DP_STATUS_DONE;
+ dp_send_events(dp_drv, EV_USBPD_DP_CONFIGURE);
+ }
+ } else {
+ pr_err("unknown response: %d for DP_Status\n",
+ cmd_type);
+ }
+ break;
+ case DP_VDM_CONFIGURE:
+ if ((dp_drv->cable_connected == true)
+ || (cmd_type == SVDM_CMD_TYPE_RESP_ACK)) {
+ dp_drv->alt_mode.current_state = DP_CONFIGURE_DONE;
+ pr_debug("config USBPD to DP done\n");
+ mdss_dp_host_init(&dp_drv->panel_data);
+ } else {
+ pr_err("unknown response: %d for DP_Configure\n",
+ cmd_type);
+ }
+ break;
+ default:
+ pr_err("unknown cmd: %d\n", cmd);
+ break;
+ }
+}
+
+static int mdss_dp_usbpd_setup(struct mdss_dp_drv_pdata *dp_drv)
+{
+ int ret = 0;
+ const char *pd_phandle = "qcom,dp-usbpd-detection";
+
+ dp_drv->pd = devm_usbpd_get_by_phandle(&dp_drv->pdev->dev,
+ pd_phandle);
+
+ if (IS_ERR(dp_drv->pd)) {
+ pr_err("get_usbpd phandle failed (%ld)\n",
+ PTR_ERR(dp_drv->pd));
+ return PTR_ERR(dp_drv->pd);
+ }
+
+ dp_drv->svid_handler.svid = USB_C_DP_SID;
+ dp_drv->svid_handler.vdm_received = NULL;
+ dp_drv->svid_handler.connect = &usbpd_connect_callback;
+ dp_drv->svid_handler.svdm_received = &usbpd_response_callback;
+ dp_drv->svid_handler.disconnect = &usbpd_disconnect_callback;
+
+ ret = usbpd_register_svid(dp_drv->pd, &dp_drv->svid_handler);
+ if (ret) {
+ pr_err("usbpd registration failed\n");
+ return -ENODEV;
+ }
+
+ return ret;
+}
+
static int mdss_dp_probe(struct platform_device *pdev)
{
int ret, i;
@@ -1030,8 +1563,17 @@ static int mdss_dp_probe(struct platform_device *pdev)
dp_drv->mask1 = EDP_INTR_MASK1;
dp_drv->mask2 = EDP_INTR_MASK2;
mutex_init(&dp_drv->emutex);
+ mutex_init(&dp_drv->host_mutex);
+ mutex_init(&dp_drv->pd_msg_mutex);
spin_lock_init(&dp_drv->lock);
+ if (mdss_dp_usbpd_setup(dp_drv)) {
+ pr_err("Error usbpd setup!\n");
+ devm_kfree(&pdev->dev, dp_drv);
+ dp_drv = NULL;
+ return -EPROBE_DEFER;
+ }
+
ret = mdss_retrieve_dp_ctrl_resources(pdev, dp_drv);
if (ret)
goto probe_err;
@@ -1062,6 +1604,14 @@ static int mdss_dp_probe(struct platform_device *pdev)
if (ret)
goto probe_err;
+ ret = mdss_dp_clk_init(dp_drv,
+ &pdev->dev, true);
+ if (ret) {
+ DEV_ERR("clk_init failed.ret=%d\n",
+ ret);
+ goto probe_err;
+ }
+
ret = mdss_dp_irq_setup(dp_drv);
if (ret)
goto probe_err;
@@ -1070,33 +1620,33 @@ static int mdss_dp_probe(struct platform_device *pdev)
if (ret)
goto probe_err;
- ret = mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, true);
+ dp_drv->cont_splash = dp_drv->mdss_util->panel_intf_status(DISPLAY_1,
+ MDSS_PANEL_INTF_EDP) ? true : false;
+
+ platform_set_drvdata(pdev, dp_drv);
+
+ ret = mdss_dp_pinctrl_init(pdev, dp_drv);
if (ret) {
- pr_err("Unabled to enable core clocks\n");
+ pr_err("pinctrl init failed, ret=%d\n",
+ ret);
goto probe_err;
}
- pr_info("ctrl_hw_rev =0x%x, phy hw_rev =0x%x\n",
- mdss_dp_get_ctrl_hw_version(&dp_drv->ctrl_io),
- mdss_dp_get_phy_hw_version(&dp_drv->phy_io));
-
- ret = mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, false);
+ ret = mdss_dp_parse_gpio_params(pdev, dp_drv);
if (ret) {
- pr_err("Unabled to disable core clocks\n");
+ pr_err("failed to parse gpio params, ret=%d\n",
+ ret);
goto probe_err;
}
- dp_drv->cont_splash = dp_drv->mdss_util->panel_intf_status(DISPLAY_1,
- MDSS_PANEL_INTF_EDP) ? true : false;
-
- platform_set_drvdata(pdev, dp_drv);
-
mdss_dp_device_register(dp_drv);
dp_drv->inited = true;
pr_debug("done\n");
+ dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES);
+
return 0;
probe_err:
diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h
index b63318dcca06..008e7d687dbd 100644
--- a/drivers/video/fbdev/msm/mdss_dp.h
+++ b/drivers/video/fbdev/msm/mdss_dp.h
@@ -20,6 +20,7 @@
#include <linux/pinctrl/consumer.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
+#include <linux/usb/usbpd.h>
#include "mdss_hdmi_util.h"
#include "video/msm_hdmi_modes.h"
@@ -103,10 +104,6 @@
#define EDP_INTR_MASK2 (EDP_INTR_STATUS2 << 2)
-
-#define EDP_PHY_EDPPHY_GLB_VM_CFG0 0x510
-#define EDP_PHY_EDPPHY_GLB_VM_CFG1 0x514
-
struct edp_cmd {
char read; /* 1 == read, 0 == write */
char i2c; /* 1 == i2c cmd, 0 == native cmd */
@@ -126,6 +123,10 @@ struct edp_buf {
char i2c; /* 1 == i2c cmd, 0 == native cmd */
};
+/* USBPD-TypeC specific Macros */
+#define VDM_VERSION 0x0
+#define USB_C_DP_SID 0xFF01
+
enum dp_pm_type {
DP_CORE_PM,
DP_CTRL_PM,
@@ -133,6 +134,64 @@ enum dp_pm_type {
DP_MAX_PM
};
+#define PIN_ASSIGN_A BIT(0)
+#define PIN_ASSIGN_B BIT(1)
+#define PIN_ASSIGN_C BIT(2)
+#define PIN_ASSIGN_D BIT(3)
+#define PIN_ASSIGN_E BIT(4)
+#define PIN_ASSIGN_F BIT(5)
+
+#define SVDM_HDR(svid, ver, mode, cmd_type, cmd) \
+ (((svid) << 16) | (1 << 15) | ((ver) << 13) \
+ | ((mode) << 8) | ((cmd_type) << 6) | (cmd))
+
+/* DP specific VDM commands */
+#define DP_VDM_STATUS 0x10
+#define DP_VDM_CONFIGURE 0x11
+
+enum dp_port_cap {
+ PORT_NONE = 0,
+ PORT_UFP_D,
+ PORT_DFP_D,
+ PORT_D_UFP_D,
+};
+
+struct usbpd_dp_capabilities {
+ u32 response;
+ enum dp_port_cap s_port;
+ bool receptacle_state;
+ u8 ulink_pin_config;
+ u8 dlink_pin_config;
+};
+
+struct usbpd_dp_status {
+ u32 response;
+ enum dp_port_cap c_port;
+ bool low_pow_st;
+ bool adaptor_dp_en;
+ bool multi_func;
+ bool switch_to_usb_config;
+ bool exit_dp_mode;
+ bool hpd_high;
+ bool hpd_irq;
+};
+
+enum dp_alt_mode_state {
+ ALT_MODE_INIT_STATE = 0,
+ DISCOVER_MODES_DONE,
+ ENTER_MODE_DONE,
+ DP_STATUS_DONE,
+ DP_CONFIGURE_DONE,
+ UNKNOWN_STATE,
+};
+
+struct dp_alt_mode {
+ struct usbpd_dp_capabilities dp_cap;
+ struct usbpd_dp_status dp_status;
+ u32 usbpd_dp_config;
+ enum dp_alt_mode_state current_state;
+};
+
#define DPCD_ENHANCED_FRAME BIT(0)
#define DPCD_TPS3 BIT(1)
#define DPCD_MAX_DOWNSPREAD_0_5 BIT(2)
@@ -145,18 +204,26 @@ enum dp_pm_type {
#define EV_DPCD_CAP_READ BIT(2)
#define EV_DPCD_STATUS_READ BIT(3)
#define EV_LINK_TRAIN BIT(4)
-#define EV_IDLE_PATTERNS_SENT BIT(30)
-#define EV_VIDEO_READY BIT(31)
+#define EV_IDLE_PATTERNS_SENT BIT(5)
+#define EV_VIDEO_READY BIT(6)
-/* edp state ctrl */
+#define EV_USBPD_DISCOVER_MODES BIT(7)
+#define EV_USBPD_ENTER_MODE BIT(8)
+#define EV_USBPD_DP_STATUS BIT(9)
+#define EV_USBPD_DP_CONFIGURE BIT(10)
+#define EV_USBPD_CC_PIN_POLARITY BIT(11)
+#define EV_USBPD_EXIT_MODE BIT(12)
+
+/* dp state ctrl */
#define ST_TRAIN_PATTERN_1 BIT(0)
#define ST_TRAIN_PATTERN_2 BIT(1)
#define ST_TRAIN_PATTERN_3 BIT(2)
-#define ST_SYMBOL_ERR_RATE_MEASUREMENT BIT(3)
-#define ST_PRBS7 BIT(4)
-#define ST_CUSTOM_80_BIT_PATTERN BIT(5)
-#define ST_SEND_VIDEO BIT(6)
-#define ST_PUSH_IDLE BIT(7)
+#define ST_TRAIN_PATTERN_4 BIT(3)
+#define ST_SYMBOL_ERR_RATE_MEASUREMENT BIT(4)
+#define ST_PRBS7 BIT(5)
+#define ST_CUSTOM_80_BIT_PATTERN BIT(6)
+#define ST_SEND_VIDEO BIT(7)
+#define ST_PUSH_IDLE BIT(8)
/* sink power state */
#define SINK_POWER_ON 1
@@ -167,6 +234,8 @@ enum dp_pm_type {
#define DP_LINK_RATE_540 20 /* 5.40G = 270M * 20 */
#define DP_LINK_RATE_MAX DP_LINK_RATE_540
+#define DP_LINK_RATE_MULTIPLIER 27000000
+
struct dpcd_cap {
char major;
char minor;
@@ -254,10 +323,16 @@ struct dp_statistic {
u32 aux_native_rx;
};
-
#define DPCD_LINK_VOLTAGE_MAX 4
#define DPCD_LINK_PRE_EMPHASIS_MAX 4
+struct dp_pinctrl_res {
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *state_active;
+ struct pinctrl_state *state_hpd_active;
+ struct pinctrl_state *state_suspend;
+};
+
irqreturn_t dp_isr(int irq, void *ptr);
struct mdss_dp_drv_pdata {
@@ -266,6 +341,10 @@ struct mdss_dp_drv_pdata {
int (*off) (struct mdss_panel_data *pdata);
struct platform_device *pdev;
+ struct usbpd *pd;
+ struct usbpd_svid_handler svid_handler;
+ struct dp_alt_mode alt_mode;
+
struct mutex emutex;
int clk_cnt;
int cont_splash;
@@ -302,6 +381,11 @@ struct mdss_dp_drv_pdata {
/* regulators */
struct dss_module_power power_data[DP_MAX_PM];
+ struct dp_pinctrl_res pin_res;
+ int aux_sel_gpio;
+ int aux_en_gpio;
+ int usbplug_cc_gpio;
+ int hpd_gpio;
int clk_on;
/* hpd */
@@ -316,6 +400,9 @@ struct mdss_dp_drv_pdata {
struct completion video_comp;
struct mutex aux_mutex;
struct mutex train_mutex;
+ struct mutex host_mutex;
+ struct mutex pd_msg_mutex;
+ bool cable_connected;
u32 aux_cmd_busy;
u32 aux_cmd_i2c;
int aux_trans_num;
@@ -379,5 +466,6 @@ void mdss_dp_fill_link_cfg(struct mdss_dp_drv_pdata *ep);
void mdss_dp_sink_power_down(struct mdss_dp_drv_pdata *ep);
void mdss_dp_lane_power_ctrl(struct mdss_dp_drv_pdata *ep, int up);
void mdss_dp_config_ctrl(struct mdss_dp_drv_pdata *ep);
+char mdss_dp_gen_link_clk(struct mdss_panel_info *pinfo, char lane_cnt);
#endif /* MDSS_DP_H */
diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c
index 39f11a8c35d1..7b14a7efb9dc 100644
--- a/drivers/video/fbdev/msm/mdss_dp_aux.c
+++ b/drivers/video/fbdev/msm/mdss_dp_aux.c
@@ -484,6 +484,37 @@ void dp_extract_edid_feature(struct edp_edid *edid, char *buf)
edid->dpm, edid->color_format);
};
+char mdss_dp_gen_link_clk(struct mdss_panel_info *pinfo, char lane_cnt)
+{
+ const u32 encoding_factx10 = 8;
+ const u32 ln_to_link_ratio = 10;
+ u32 min_link_rate;
+ char calc_link_rate = 0;
+
+ pr_debug("clk_rate=%llu, bpp= %d, lane_cnt=%d\n",
+ pinfo->clk_rate, pinfo->bpp, lane_cnt);
+ min_link_rate = (pinfo->clk_rate * 10) /
+ (lane_cnt * encoding_factx10);
+ min_link_rate = (min_link_rate * pinfo->bpp)
+ / (DP_LINK_RATE_MULTIPLIER);
+ min_link_rate /= ln_to_link_ratio;
+
+ pr_debug("min_link_rate = %d\n", min_link_rate);
+
+ if (min_link_rate <= DP_LINK_RATE_162)
+ calc_link_rate = DP_LINK_RATE_162;
+ else if (min_link_rate <= DP_LINK_RATE_270)
+ calc_link_rate = DP_LINK_RATE_270;
+ else if (min_link_rate <= DP_LINK_RATE_540)
+ calc_link_rate = DP_LINK_RATE_540;
+ else {
+ pr_err("link_rate = %d is unsupported\n", min_link_rate);
+ calc_link_rate = 0;
+ }
+
+ return calc_link_rate;
+}
+
void dp_extract_edid_detailed_timing_description(struct edp_edid *edid,
char *buf)
{
@@ -1037,23 +1068,37 @@ char vm_voltage_swing[4][4] = {
{0x1E, 0xFF, 0xFF, 0xFF} /* sw1, 1.2 v, optional */
};
-static void dp_voltage_pre_emphasise_set(struct mdss_dp_drv_pdata *ep)
+static void dp_voltage_pre_emphasise_set(struct mdss_dp_drv_pdata *dp)
{
u32 value0 = 0;
u32 value1 = 0;
- pr_debug("v=%d p=%d\n", ep->v_level, ep->p_level);
+ pr_debug("v=%d p=%d\n", dp->v_level, dp->p_level);
- value0 = vm_pre_emphasis[(int)(ep->v_level)][(int)(ep->p_level)];
- value1 = vm_voltage_swing[(int)(ep->v_level)][(int)(ep->p_level)];
+ value0 = vm_voltage_swing[(int)(dp->v_level)][(int)(dp->p_level)];
+ value1 = vm_pre_emphasis[(int)(dp->v_level)][(int)(dp->p_level)];
+ /* Enable MUX to use Cursor values from these registers */
+ value0 |= BIT(5);
+ value1 |= BIT(5);
/* Configure host and panel only if both values are allowed */
if (value0 != 0xFF && value1 != 0xFF) {
- dp_write(ep->base + EDP_PHY_EDPPHY_GLB_VM_CFG0, value0);
- dp_write(ep->base + EDP_PHY_EDPPHY_GLB_VM_CFG1, value1);
+ dp_write(dp->phy_io.base +
+ QSERDES_TX0_OFFSET + TXn_TX_DRV_LVL,
+ value0);
+ dp_write(dp->phy_io.base +
+ QSERDES_TX1_OFFSET + TXn_TX_DRV_LVL,
+ value0);
+ dp_write(dp->phy_io.base +
+ QSERDES_TX0_OFFSET + TXn_TX_EMP_POST1_LVL,
+ value1);
+ dp_write(dp->phy_io.base +
+ QSERDES_TX1_OFFSET + TXn_TX_EMP_POST1_LVL,
+ value1);
+
pr_debug("value0=0x%x value1=0x%x",
value0, value1);
- dp_lane_set_write(ep, ep->v_level, ep->p_level);
+ dp_lane_set_write(dp, dp->v_level, dp->p_level);
}
}
@@ -1212,34 +1257,36 @@ static void dp_clear_training_pattern(struct mdss_dp_drv_pdata *ep)
usleep_range(usleep_time, usleep_time);
}
-static int dp_aux_link_train(struct mdss_dp_drv_pdata *ep)
+static int dp_aux_link_train(struct mdss_dp_drv_pdata *dp)
{
int ret = 0;
int usleep_time;
- ret = dp_aux_chan_ready(ep);
+ ret = dp_aux_chan_ready(dp);
if (ret) {
pr_err("LINK Train failed: aux chan NOT ready\n");
- complete(&ep->train_comp);
+ complete(&dp->train_comp);
return ret;
}
- dp_write(ep->base + DP_MAINLINK_CTRL, 0x1);
+ dp_write(dp->base + DP_MAINLINK_CTRL, 0x1);
- mdss_dp_sink_power_state(ep, SINK_POWER_ON);
+ mdss_dp_sink_power_state(dp, SINK_POWER_ON);
train_start:
- ep->v_level = 0; /* start from default level */
- ep->p_level = 0;
- dp_cap_lane_rate_set(ep);
-
- dp_clear_training_pattern(ep);
- usleep_time = ep->dpcd.training_read_interval;
+ dp->v_level = 0; /* start from default level */
+ dp->p_level = 0;
+ dp_cap_lane_rate_set(dp);
+ mdss_dp_config_ctrl(dp);
+
+ mdss_dp_state_ctrl(&dp->ctrl_io, 0);
+ dp_clear_training_pattern(dp);
+ usleep_time = dp->dpcd.training_read_interval;
usleep_range(usleep_time, usleep_time);
- ret = dp_start_link_train_1(ep);
+ ret = dp_start_link_train_1(dp);
if (ret < 0) {
- if (dp_link_rate_down_shift(ep) == 0) {
+ if (dp_link_rate_down_shift(dp) == 0) {
goto train_start;
} else {
pr_err("Training 1 failed\n");
@@ -1250,10 +1297,11 @@ train_start:
pr_debug("Training 1 completed successfully\n");
- dp_clear_training_pattern(ep);
- ret = dp_start_link_train_2(ep);
+ mdss_dp_state_ctrl(&dp->ctrl_io, 0);
+ dp_clear_training_pattern(dp);
+ ret = dp_start_link_train_2(dp);
if (ret < 0) {
- if (dp_link_rate_down_shift(ep) == 0) {
+ if (dp_link_rate_down_shift(dp) == 0) {
goto train_start;
} else {
pr_err("Training 2 failed\n");
@@ -1264,10 +1312,16 @@ train_start:
pr_debug("Training 2 completed successfully\n");
+
clear:
- dp_clear_training_pattern(ep);
+ dp_clear_training_pattern(dp);
+ if (ret != -1) {
+ mdss_dp_setup_tr_unit(&dp->ctrl_io);
+ mdss_dp_state_ctrl(&dp->ctrl_io, ST_SEND_VIDEO);
+ pr_debug("State_ctrl set to SEND_VIDEO\n");
+ }
- complete(&ep->train_comp);
+ complete(&dp->train_comp);
return ret;
}
diff --git a/drivers/video/fbdev/msm/mdss_dp_util.c b/drivers/video/fbdev/msm/mdss_dp_util.c
index c1d29987a5fa..f7b27d1e56a1 100644
--- a/drivers/video/fbdev/msm/mdss_dp_util.c
+++ b/drivers/video/fbdev/msm/mdss_dp_util.c
@@ -214,14 +214,24 @@ void mdss_dp_sw_mvid_nvid(struct dss_io_data *ctrl_io)
writel_relaxed(0x3c, ctrl_io->base + DP_SOFTWARE_NVID);
}
+void mdss_dp_setup_tr_unit(struct dss_io_data *ctrl_io)
+{
+ /* Current Tr unit configuration supports only 1080p */
+ writel_relaxed(0x21, ctrl_io->base + DP_MISC1_MISC0);
+ writel_relaxed(0x0f0016, ctrl_io->base + DP_VALID_BOUNDARY);
+ writel_relaxed(0x1f, ctrl_io->base + DP_TU);
+ writel_relaxed(0x0, ctrl_io->base + DP_VALID_BOUNDARY_2);
+}
+
void mdss_dp_ctrl_lane_mapping(struct dss_io_data *ctrl_io,
struct lane_mapping l_map)
{
u8 bits_per_lane = 2;
u32 lane_map = ((l_map.lane0 << (bits_per_lane * 0))
- || (l_map.lane1 << (bits_per_lane * 1))
- || (l_map.lane2 << (bits_per_lane * 2))
- || (l_map.lane3 << (bits_per_lane * 3)));
+ | (l_map.lane1 << (bits_per_lane * 1))
+ | (l_map.lane2 << (bits_per_lane * 2))
+ | (l_map.lane3 << (bits_per_lane * 3)));
+ pr_debug("%s: lane mapping reg = 0x%x\n", __func__, lane_map);
writel_relaxed(lane_map,
ctrl_io->base + DP_LOGICAL2PHYSCIAL_LANE_MAPPING);
}
@@ -282,3 +292,81 @@ void mdss_dp_irq_disable(struct mdss_dp_drv_pdata *dp_drv)
dp_drv->mdss_util->disable_irq(&mdss_dp_hw);
}
+
+static void mdss_dp_initialize_s_port(enum dp_port_cap *s_port, int port)
+{
+ switch (port) {
+ case 0:
+ *s_port = PORT_NONE;
+ break;
+ case 1:
+ *s_port = PORT_UFP_D;
+ break;
+ case 2:
+ *s_port = PORT_DFP_D;
+ break;
+ case 3:
+ *s_port = PORT_D_UFP_D;
+ break;
+ default:
+ *s_port = PORT_NONE;
+ }
+}
+
+void mdss_dp_usbpd_ext_capabilities(struct usbpd_dp_capabilities *dp_cap)
+{
+ u32 buf = dp_cap->response;
+ int port = buf & 0x3;
+
+ dp_cap->receptacle_state =
+ (buf & BIT(6)) ? true : false;
+
+ dp_cap->dlink_pin_config =
+ (buf >> 8) & 0xff;
+
+ dp_cap->ulink_pin_config =
+ (buf >> 16) & 0xff;
+
+ mdss_dp_initialize_s_port(&dp_cap->s_port, port);
+}
+
+void mdss_dp_usbpd_ext_dp_status(struct usbpd_dp_status *dp_status)
+{
+ u32 buf = dp_status->response;
+ int port = buf & 0x3;
+
+ dp_status->low_pow_st =
+ (buf & BIT(2)) ? true : false;
+
+ dp_status->adaptor_dp_en =
+ (buf & BIT(3)) ? true : false;
+
+ dp_status->multi_func =
+ (buf & BIT(4)) ? true : false;
+
+ dp_status->switch_to_usb_config =
+ (buf & BIT(5)) ? true : false;
+
+ dp_status->exit_dp_mode =
+ (buf & BIT(6)) ? true : false;
+
+ dp_status->hpd_high =
+ (buf & BIT(7)) ? true : false;
+
+ dp_status->hpd_irq =
+ (buf & BIT(8)) ? true : false;
+
+ mdss_dp_initialize_s_port(&dp_status->c_port, port);
+}
+
+u32 mdss_dp_usbpd_gen_config_pkt(struct mdss_dp_drv_pdata *dp)
+{
+ u32 config = 0;
+
+ config |= (dp->alt_mode.dp_cap.dlink_pin_config << 8);
+ config |= (0x1 << 2); /* configure for DPv1.3 */
+ config |= 0x2; /* Configuring for UFP_D */
+
+ pr_debug("DP config = 0x%x\n", config);
+ return config;
+}
diff --git a/drivers/video/fbdev/msm/mdss_dp_util.h b/drivers/video/fbdev/msm/mdss_dp_util.h
index d9064cafad9a..8ef00dd7248e 100644
--- a/drivers/video/fbdev/msm/mdss_dp_util.h
+++ b/drivers/video/fbdev/msm/mdss_dp_util.h
@@ -48,10 +48,13 @@
#define DP_START_HOR_VER_FROM_SYNC (0x00000420)
#define DP_HSYNC_VSYNC_WIDTH_POLARITY (0x00000424)
#define DP_ACTIVE_HOR_VER (0x00000428)
-
+#define DP_MISC1_MISC0 (0x0000042C)
+#define DP_VALID_BOUNDARY (0x00000430)
+#define DP_VALID_BOUNDARY_2 (0x00000434)
#define DP_LOGICAL2PHYSCIAL_LANE_MAPPING (0x00000438)
#define DP_MAINLINK_READY (0x00000440)
+#define DP_TU (0x0000044C)
/*DP PHY Register offsets */
#define DP_PHY_REVISION_ID0 (0x00000000)
@@ -76,6 +79,12 @@
#define DP_PHY_AUX_INTERRUPT_MASK (0x00000044)
#define DP_PHY_AUX_INTERRUPT_CLEAR (0x00000048)
+#define QSERDES_TX0_OFFSET 0x0400
+#define QSERDES_TX1_OFFSET 0x0800
+
+#define TXn_TX_EMP_POST1_LVL 0x000C
+#define TXn_TX_DRV_LVL 0x001C
+
#define TCSR_USB3_DP_PHYMODE 0x48
struct lane_mapping {
@@ -85,6 +94,7 @@ struct lane_mapping {
char lane3;
};
+void mdss_dp_state_ctrl(struct dss_io_data *ctrl_io, u32 data);
u32 mdss_dp_get_ctrl_hw_version(struct dss_io_data *ctrl_io);
u32 mdss_dp_get_phy_hw_version(struct dss_io_data *phy_io);
void mdss_dp_aux_reset(struct dss_io_data *ctrl_io);
@@ -92,6 +102,7 @@ void mdss_dp_mainlink_reset(struct dss_io_data *ctrl_io);
void mdss_dp_phy_reset(struct dss_io_data *ctrl_io);
void mdss_dp_switch_usb3_phy_to_dp_mode(struct dss_io_data *tcsr_reg_io);
void mdss_dp_assert_phy_reset(struct dss_io_data *ctrl_io, bool assert);
+void mdss_dp_setup_tr_unit(struct dss_io_data *ctrl_io);
void mdss_dp_phy_aux_setup(struct dss_io_data *phy_io);
void mdss_dp_hpd_configure(struct dss_io_data *ctrl_io, bool enable);
void mdss_dp_aux_ctrl(struct dss_io_data *ctrl_io, bool enable);
@@ -107,5 +118,10 @@ int mdss_dp_irq_setup(struct mdss_dp_drv_pdata *dp_drv);
void mdss_dp_irq_enable(struct mdss_dp_drv_pdata *dp_drv);
void mdss_dp_irq_disable(struct mdss_dp_drv_pdata *dp_drv);
void mdss_dp_sw_mvid_nvid(struct dss_io_data *ctrl_io);
+void mdss_dp_usbpd_ext_capabilities(struct usbpd_dp_capabilities *dp_cap);
+void mdss_dp_usbpd_ext_dp_status(struct usbpd_dp_status *dp_status);
+u32 mdss_dp_usbpd_gen_config_pkt(struct mdss_dp_drv_pdata *dp);
+void mdss_dp_ctrl_lane_mapping(struct dss_io_data *ctrl_io,
+ struct lane_mapping l_map);
#endif /* __DP_UTIL_H__ */
diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h
index d6cd6825e262..da5e7bb8a343 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.h
+++ b/drivers/video/fbdev/msm/mdss_mdp.h
@@ -678,6 +678,8 @@ struct mdss_ad_info {
bool last_calib_valid;
u32 ipc_frame_count;
u32 bl_data;
+ u32 bl_min_delta;
+ u32 bl_low_limit;
u32 calc_itr;
uint32_t bl_lin[AD_BL_LIN_LEN];
uint32_t bl_lin_inv[AD_BL_LIN_LEN];
@@ -1733,6 +1735,8 @@ void mdss_mdp_hist_intr_done(u32 isr);
int mdss_mdp_ad_config(struct msm_fb_data_type *mfd,
struct mdss_ad_init_cfg *init_cfg);
+int mdss_mdp_ad_bl_config(struct msm_fb_data_type *mfd,
+ struct mdss_ad_bl_cfg *ad_bl_cfg);
int mdss_mdp_ad_input(struct msm_fb_data_type *mfd,
struct mdss_ad_input *input, int wait);
int mdss_mdp_ad_addr_setup(struct mdss_data_type *mdata, u32 *ad_offsets);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
index cd8df78bc8c0..3fc6d94393d5 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
@@ -45,7 +45,7 @@ static struct {
[MDSS_MDP_SSPP_VIG0] = { 0, { 0, 3, 0 }, { 0, 1, 3 } },
[MDSS_MDP_SSPP_VIG1] = { 1, { 3, 3, 0 }, { 2, 1, 3 } },
[MDSS_MDP_SSPP_VIG2] = { 2, { 6, 3, 0 }, { 4, 1, 3 } },
- [MDSS_MDP_SSPP_VIG3] = { 18, { 26, 3, 0 }, { 4, 1, 3 } },
+ [MDSS_MDP_SSPP_VIG3] = { 18, { 26, 3, 0 }, { 6, 1, 3 } },
[MDSS_MDP_SSPP_RGB0] = { 3, { 9, 3, 0 }, { 8, 1, 3 } },
[MDSS_MDP_SSPP_RGB1] = { 4, { 12, 3, 0 }, { 10, 1, 3 } },
[MDSS_MDP_SSPP_RGB2] = { 5, { 15, 3, 0 }, { 12, 1, 3 } },
diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c
index 17cbdd44755a..0ae420724d61 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_layer.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c
@@ -114,7 +114,7 @@ static int __dest_scaler_data_setup(struct mdp_destination_scaler_data *ds_data,
}
static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl,
- struct mdp_destination_scaler_data *ds_data)
+ struct mdp_destination_scaler_data *ds_data, int ds_count)
{
struct mdss_data_type *mdata;
struct mdss_panel_info *pinfo;
@@ -127,7 +127,7 @@ static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl,
* when we switch between scaling factor or disabling scaling.
*/
if (test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map)) {
- if (ctl->mixer_left) {
+ if (ctl->mixer_left && ctl->mixer_left->ds) {
/*
* Any scale update from usermode, we will update the
* mixer width and height with the given LM width and
@@ -149,10 +149,25 @@ static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl,
ctl->mixer_left->height = ds_data->lm_height;
pr_debug("Update mixer-left width/height: %dx%d\n",
ds_data->lm_width, ds_data->lm_width);
-
}
- if (ctl->mixer_right) {
+ if (ctl->mixer_right && ctl->mixer_right->ds) {
+ /*
+ * Advanced to next ds_data structure from commit if
+ * there is more than 1 for split display usecase.
+ */
+ if (ds_count > 1)
+ ds_data++;
+
+ pinfo = &ctl->panel_data->panel_info;
+ if ((ds_data->lm_width > get_panel_xres(pinfo)) ||
+ (ds_data->lm_height > get_panel_yres(pinfo)) ||
+ (ds_data->lm_width == 0) ||
+ (ds_data->lm_height == 0)) {
+ pr_err("Invalid LM width / height setting\n");
+ return -EINVAL;
+ }
+
/*
* Split display both left and right should have the
* same width and height
@@ -162,6 +177,15 @@ static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl,
pr_info("Update mixer-right width/height: %dx%d\n",
ds_data->lm_width, ds_data->lm_height);
+ if (ctl->mixer_left &&
+ ((ctl->mixer_right->width !=
+ ctl->mixer_left->width) ||
+ (ctl->mixer_right->height !=
+ ctl->mixer_left->height))) {
+ pr_err("Mismatch width/heigth in LM for split display\n");
+ return -EINVAL;
+ }
+
/*
* For split display, CTL width should be equal to
* whole panel size
@@ -2098,17 +2122,17 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
if (test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map) &&
commit->dest_scaler &&
commit->dest_scaler_cnt) {
+ struct mdp_destination_scaler_data *ds_data =
+ commit->dest_scaler;
+
/*
- * Find out which DS block to use based on LM assignment
+ * Find out which DS block to use based on DS commit info
*/
- if ((left_cnt > 0) && (right_cnt > 0) &&
- (commit->dest_scaler_cnt == 2))
+ if (commit->dest_scaler_cnt == 2)
ds_mode = DS_DUAL_MODE;
- else if ((left_cnt > 0) && (right_cnt == 0) &&
- (commit->dest_scaler_cnt == 1))
+ else if (ds_data->dest_scaler_ndx == 0)
ds_mode = DS_LEFT;
- else if ((left_cnt == 0) && (right_cnt > 0) &&
- (commit->dest_scaler_cnt == 1))
+ else if (ds_data->dest_scaler_ndx == 1)
ds_mode = DS_RIGHT;
else {
pr_err("Commit destination scaler count not matching with LM assignment, DS-cnt:%d\n",
@@ -2396,7 +2420,8 @@ int mdss_mdp_layer_atomic_validate(struct msm_fb_data_type *mfd,
if (commit->dest_scaler && commit->dest_scaler_cnt) {
rc = mdss_mdp_destination_scaler_pre_validate(mdp5_data->ctl,
- commit->dest_scaler);
+ commit->dest_scaler,
+ commit->dest_scaler_cnt);
if (IS_ERR_VALUE(rc)) {
pr_err("Destination scaler pre-validate failed\n");
return -EINVAL;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
index 2baa1b9cd8b5..e5cdc750193e 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
@@ -4109,6 +4109,9 @@ static int mdss_mdp_pp_ioctl(struct msm_fb_data_type *mfd,
case mdp_op_ad_cfg:
ret = mdss_mdp_ad_config(mfd, &mdp_pp.data.ad_init_cfg);
break;
+ case mdp_op_ad_bl_cfg:
+ ret = mdss_mdp_ad_bl_config(mfd, &mdp_pp.data.ad_bl_cfg);
+ break;
case mdp_op_ad_input:
ret = mdss_mdp_ad_input(mfd, &mdp_pp.data.ad_input, 1);
if (ret > 0) {
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c
index c4cbc220baf8..a760711e7501 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c
@@ -3073,22 +3073,22 @@ int mdss_mdp_pp_default_overlay_config(struct msm_fb_data_type *mfd,
return ret;
}
-static bool pp_ad_bl_threshold_check(int al_thresh, int base, int prev_bl,
+static bool pp_ad_bl_threshold_check(int bl_min_delta, int base, int prev_bl,
int curr_bl)
{
- int bl_thresh = 0, diff = 0;
+ int bl_delta_factor = 0, diff = 0;
bool ret = false;
- pr_debug("al_thresh = %d, base = %d\n", al_thresh, base);
+ pr_debug("bl_min_delta = %d, base = %d\n", bl_min_delta, base);
if (base <= 0) {
pr_debug("Invalid base for threshold calculation %d\n", base);
return ret;
}
- bl_thresh = (curr_bl * al_thresh) / (base * 4);
+ bl_delta_factor = (curr_bl * bl_min_delta) / base;
diff = (curr_bl > prev_bl) ? (curr_bl - prev_bl) : (prev_bl - curr_bl);
- ret = (diff > bl_thresh) ? true : false;
- pr_debug("prev_bl =%d, curr_bl = %d, bl_thresh = %d, diff = %d, ret = %d\n",
- prev_bl, curr_bl, bl_thresh, diff, ret);
+ ret = (diff > bl_delta_factor) ? true : false;
+ pr_debug("prev_bl =%d, curr_bl = %d, bl_delta_factor = %d, diff = %d, ret = %d\n",
+ prev_bl, curr_bl, bl_delta_factor, diff, ret);
return ret;
}
@@ -3163,7 +3163,10 @@ static int pp_ad_calc_bl(struct msm_fb_data_type *mfd, int bl_in, int *bl_out,
ad_bl_out = temp;
}
- if (pp_ad_bl_threshold_check(ad->init.al_thresh, ad->init.alpha_base,
+ /* update AD backlight based on AD BL low limit */
+ ad_bl_out = (ad_bl_out < ad->bl_low_limit ?
+ ad->bl_low_limit : ad_bl_out);
+ if (pp_ad_bl_threshold_check(ad->bl_min_delta, ad->init.alpha_base,
ad->last_bl, ad_bl_out)) {
mfd->ad_bl_level = ad_bl_out;
pr_debug("backlight send to AD block: %d\n", mfd->ad_bl_level);
@@ -5747,6 +5750,29 @@ ad_config_exit:
return ret;
}
+int mdss_mdp_ad_bl_config(struct msm_fb_data_type *mfd,
+ struct mdss_ad_bl_cfg *ad_bl_cfg)
+{
+ int ret = 0;
+ struct mdss_ad_info *ad;
+
+ ret = mdss_mdp_get_ad(mfd, &ad);
+ if (ret == -ENODEV || ret == -EPERM) {
+ pr_debug("AD not supported on device, disp num %d\n",
+ mfd->index);
+ return ret;
+ } else if (ret || !ad) {
+ pr_err("Failed to get ad info: ret = %d\n", ret);
+ return ret;
+ }
+
+ mutex_lock(&ad->lock);
+ ad->bl_min_delta = ad_bl_cfg->bl_min_delta;
+ ad->bl_low_limit = ad_bl_cfg->bl_low_limit;
+ mutex_unlock(&ad->lock);
+ return 0;
+}
+
int mdss_mdp_ad_input(struct msm_fb_data_type *mfd,
struct mdss_ad_input *input, int wait) {
int ret = 0;
@@ -6293,6 +6319,8 @@ static int mdss_mdp_ad_setup(struct msm_fb_data_type *mfd)
ad->last_calib_valid = false;
ad->last_ad_data_valid = false;
ad->ipc_frame_count = 0;
+ ad->bl_min_delta = 0;
+ ad->bl_low_limit = 0;
ad->calc_itr = 0;
ad->calc_hw_num = PP_AD_BAD_HW_NUM;
memset(&ad->last_calib, 0, sizeof(ad->last_calib));
@@ -6556,6 +6584,8 @@ int mdss_mdp_ad_addr_setup(struct mdss_data_type *mdata, u32 *ad_offsets)
mdata->ad_cfgs[i].last_str = 0xFFFFFFFF;
mdata->ad_cfgs[i].last_bl = 0;
mdata->ad_cfgs[i].last_ad_data = 0;
+ mdata->ad_cfgs[i].bl_low_limit = 0;
+ mdata->ad_cfgs[i].bl_min_delta = 0;
memset(mdata->ad_cfgs[i].last_calib, 0,
sizeof(mdata->ad_cfgs[i].last_calib));
mdata->ad_cfgs[i].last_calib_valid = false;
diff --git a/include/dt-bindings/clock/msm-clocks-cobalt.h b/include/dt-bindings/clock/msm-clocks-cobalt.h
index 47cd73b08a83..28efd55ea8f6 100644
--- a/include/dt-bindings/clock/msm-clocks-cobalt.h
+++ b/include/dt-bindings/clock/msm-clocks-cobalt.h
@@ -241,7 +241,6 @@
#define clk_gcc_usb30_sleep_clk 0xd0b65c92
#define clk_gcc_usb3_phy_aux_clk 0x0d9a36e0
#define clk_gcc_usb3_phy_pipe_clk 0xf279aff2
-#define clk_gcc_usb_phy_cfg_ahb2phy_clk 0xd1231a0e
#define clk_gcc_wcss_ahb_s0_clk 0x639a01c4
#define clk_gcc_wcss_axi_m_clk 0xabc48ebd
#define clk_gcc_wcss_ecahb_clk 0xf1815ce9
diff --git a/include/dt-bindings/clock/msm-clocks-hwio-cobalt.h b/include/dt-bindings/clock/msm-clocks-hwio-cobalt.h
index 757344602f4a..7ef57256d8f0 100644
--- a/include/dt-bindings/clock/msm-clocks-hwio-cobalt.h
+++ b/include/dt-bindings/clock/msm-clocks-hwio-cobalt.h
@@ -213,7 +213,6 @@
#define GCC_USB3_PHY_AUX_CBCR 0x50000
#define GCC_USB3_PHY_PIPE_CBCR 0x50004
#define GCC_USB3PHY_PHY_BCR 0x50024
-#define GCC_USB_PHY_CFG_AHB2PHY_CBCR 0x6A004
#define GCC_WCSS_AHB_S0_CBCR 0x11004
#define GCC_WCSS_AXI_M_CBCR 0x11008
#define GCC_WCSS_ECAHB_CBCR 0x1100C
diff --git a/include/linux/input/ft5x06_ts.h b/include/linux/input/ft5x06_ts.h
new file mode 100644
index 000000000000..a9577b62cb07
--- /dev/null
+++ b/include/linux/input/ft5x06_ts.h
@@ -0,0 +1,31 @@
+/*
+ *
+ * FocalTech ft5x06 TouchScreen driver header file.
+ *
+ * Copyright (c) 2010 Focal tech Ltd.
+ * Copyright (c) 2012, Code Aurora Forum. 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 __LINUX_FT5X06_TS_H__
+#define __LINUX_FT5X06_TS_H__
+
+struct ft5x06_ts_platform_data {
+ unsigned long irqflags;
+ u32 x_max;
+ u32 y_max;
+ u32 irq_gpio;
+ u32 reset_gpio;
+ int (*power_init)(bool);
+ int (*power_on)(bool);
+};
+
+#endif
diff --git a/include/linux/qpnp/qpnp-adc.h b/include/linux/qpnp/qpnp-adc.h
index 8d51ddcd4246..af25f0c01369 100644
--- a/include/linux/qpnp/qpnp-adc.h
+++ b/include/linux/qpnp/qpnp-adc.h
@@ -242,6 +242,7 @@ enum qpnp_iadc_channels {
#define QPNP_ADC_HWMON_NAME_LENGTH 64
#define QPNP_MAX_PROP_NAME_LEN 32
#define QPNP_THERMALNODE_NAME_LENGTH 25
+#define QPNP_ADC_1P25_UV 1250000
/* Structure device for qpnp vadc */
struct qpnp_vadc_chip;
@@ -950,6 +951,7 @@ enum qpnp_state_request {
* @low_temp: Low temperature threshold for which notification is requested.
* @high_thr_voltage: High voltage for which notification is requested.
* @low_thr_voltage: Low voltage for which notification is requested.
+ * @adc_tm_hc: Represents the refreshed BTM register design.
* @state_request: Enable/disable the corresponding high and low temperature
* thresholds.
* @timer_interval1: Select polling rate from qpnp_adc_meas_timer_1 type.
@@ -972,6 +974,7 @@ struct qpnp_adc_tm_btm_param {
int32_t low_thr;
int32_t gain_num;
int32_t gain_den;
+ bool adc_tm_hc;
enum qpnp_vadc_channels channel;
enum qpnp_state_request state_request;
enum qpnp_adc_meas_timer_1 timer_interval;
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index 89effb61d153..44b6222db9a3 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -176,6 +176,8 @@ struct msm_usb_cable {
#define DEVICE_IN_SS_MODE BIT(5)
#define PHY_LANE_A BIT(6)
#define PHY_LANE_B BIT(7)
+#define PHY_HSFS_MODE BIT(8)
+#define PHY_LS_MODE BIT(9)
#define USB_NUM_BUS_CLOCKS 3
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 63568caf0de8..e0b0d2b12b88 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -3042,6 +3042,24 @@ struct wiphy_vendor_command {
};
/**
+ * struct wiphy_iftype_ext_capab - extended capabilities per interface type
+ * @iftype: interface type
+ * @extended_capabilities: extended capabilities supported by the driver,
+ * additional capabilities might be supported by userspace; these are the
+ * 802.11 extended capabilities ("Extended Capabilities element") and are
+ * in the same format as in the information element. See IEEE Std
+ * 802.11-2012 8.4.2.29 for the defined fields.
+ * @extended_capabilities_mask: mask of the valid values
+ * @extended_capabilities_len: length of the extended capabilities
+ */
+struct wiphy_iftype_ext_capab {
+ enum nl80211_iftype iftype;
+ const u8 *extended_capabilities;
+ const u8 *extended_capabilities_mask;
+ u8 extended_capabilities_len;
+};
+
+/**
* struct wiphy - wireless hardware description
* @reg_notifier: the driver's regulatory notification callback,
* note that if your driver uses wiphy_apply_custom_regulatory()
@@ -3158,9 +3176,14 @@ struct wiphy_vendor_command {
* additional capabilities might be supported by userspace; these are
* the 802.11 extended capabilities ("Extended Capabilities element")
* and are in the same format as in the information element. See
- * 802.11-2012 8.4.2.29 for the defined fields.
+ * 802.11-2012 8.4.2.29 for the defined fields. These are the default
+ * extended capabilities to be used if the capabilities are not specified
+ * for a specific interface type in iftype_ext_capab.
* @extended_capabilities_mask: mask of the valid values
* @extended_capabilities_len: length of the extended capabilities
+ * @iftype_ext_capab: array of extended capabilities per interface type
+ * @num_iftype_ext_capab: number of interface types for which extended
+ * capabilities are specified separately.
* @coalesce: packet coalescing support information
*
* @vendor_commands: array of vendor commands supported by the hardware
@@ -3257,6 +3280,9 @@ struct wiphy {
const u8 *extended_capabilities, *extended_capabilities_mask;
u8 extended_capabilities_len;
+ const struct wiphy_iftype_ext_capab *iftype_ext_capab;
+ unsigned int num_iftype_ext_capab;
+
/* If multiple wiphys are registered and you're handed e.g.
* a regular netdev with assigned ieee80211_ptr, you won't
* know whether it points to a wiphy your driver has registered
diff --git a/include/soc/qcom/service-locator.h b/include/soc/qcom/service-locator.h
index be1a2b431dd9..6bf8ac0be15f 100644
--- a/include/soc/qcom/service-locator.h
+++ b/include/soc/qcom/service-locator.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2016, 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
@@ -51,16 +51,22 @@ struct pd_qmi_client_data {
struct servreg_loc_entry_v01 *domain_list;
};
+enum service_locator_state {
+ LOCATOR_DOWN = 0x0F,
+ LOCATOR_UP = 0x1F,
+};
+
#if defined(CONFIG_MSM_SERVICE_LOCATOR)
/*
- * Use this api to request information regarding the process domains on which
- * a particular service runs. The client name and the service name inside the
- * pd_qmi_client_data structure need to be filled in by the client calling the
- * api. The total domains, db revision and the domain list will be filled in
+ * Use this api to request information regarding the process domains on
+ * which a particular service runs. The client name, the service name
+ * and notifier block pointer need to be provided by client calling the api.
+ * The total domains, db revision and the domain list will be filled in
* by the service locator.
* Returns 0 on success; otherwise a value < 0 if no valid subsystem is found.
*/
-int get_service_location(struct pd_qmi_client_data *data);
+int get_service_location(char *client_name, char *service_name,
+ struct notifier_block *locator_nb);
/*
* Use this api to request information regarding the subsystem the process
@@ -73,9 +79,10 @@ int find_subsys(const char *pd_path, char *subsys);
#else
-static inline int get_service_location(struct pd_qmi_client_data *data)
+static inline int get_service_location(char *client_name,
+ char *service_name, struct notifier_block *locator_nb);
{
- return 0;
+ return -ENODEV;
}
static inline int find_subsys(const char *pd_path, const char *subsys)
diff --git a/include/sound/cpe_cmi.h b/include/sound/cpe_cmi.h
index 5ce52ae912d4..cbf83e7a7e91 100644
--- a/include/sound/cpe_cmi.h
+++ b/include/sound/cpe_cmi.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2015, Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2016, Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -17,6 +17,7 @@
#include <linux/types.h>
#define CPE_AFE_PORT_1_TX 1
+#define CPE_AFE_PORT_3_TX 3
#define CPE_AFE_PORT_ID_2_OUT 0x02
#define CMI_INBAND_MESSAGE_SIZE 127
@@ -80,6 +81,7 @@
#define CPE_LSM_PARAM_ID_LAB_CONFIG 0x00012C0A
#define CPE_LSM_PARAM_ID_REGISTER_SOUND_MODEL (0x00012C14)
#define CPE_LSM_PARAM_ID_DEREGISTER_SOUND_MODEL (0x00012C15)
+#define CPE_LSM_PARAM_ID_MEDIA_FMT (0x00012C1E)
/* AFE Service command opcodes */
#define CPE_AFE_PORT_CMD_START (0x1001)
@@ -125,7 +127,7 @@ enum {
CMI_CPE_SERVICE_ID_MAX,
};
-#define CPE_LSM_SESSION_ID_MAX 1
+#define CPE_LSM_SESSION_ID_MAX 2
#define IS_VALID_SESSION_ID(s_id) \
(s_id <= CPE_LSM_SESSION_ID_MAX)
@@ -418,6 +420,15 @@ struct cpe_lsm_lab_latency_config {
struct cpe_lsm_lab_config latency_cfg;
} __packed;
+struct cpe_lsm_media_fmt_param {
+ struct cmi_hdr hdr;
+ struct cpe_param_data param;
+ u32 minor_version;
+ u32 sample_rate;
+ u16 num_channels;
+ u16 bit_width;
+} __packed;
+
#define CPE_PARAM_LSM_LAB_LATENCY_SIZE (\
sizeof(struct cpe_lsm_lab_latency_config) - \
@@ -474,4 +485,8 @@ struct cpe_lsm_lab_latency_config {
sizeof(struct cmi_hdr))
#define CPE_GAIN_PARAM_SIZE ((CPE_CMD_GAIN_PLD_SIZE) - \
sizeof(struct cpe_param_data))
+#define CPE_MEDIA_FMT_PLD_SIZE (sizeof(struct cpe_lsm_media_fmt_param) - \
+ sizeof(struct cmi_hdr))
+#define CPE_MEDIA_FMT_PARAM_SIZE ((CPE_MEDIA_FMT_PLD_SIZE) - \
+ sizeof(struct cpe_param_data))
#endif /* __CPE_CMI_H__ */
diff --git a/include/sound/cpe_core.h b/include/sound/cpe_core.h
index 289ed1df34f5..323a63fd6238 100644
--- a/include/sound/cpe_core.h
+++ b/include/sound/cpe_core.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2016, 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
@@ -46,6 +46,12 @@ struct lsm_out_fmt_cfg {
u8 transfer_mode;
};
+struct lsm_hw_params {
+ u32 sample_rate;
+ u16 num_chs;
+ u16 bit_width;
+};
+
struct cpe_lsm_session {
/* sound model related */
void *snd_model_data;
@@ -160,6 +166,11 @@ struct wcd_cpe_lsm_ops {
void (*lsm_get_snd_model_offset)
(void *core_handle, struct cpe_lsm_session *,
size_t *offset);
+ int (*lsm_set_media_fmt_params)(void *core_handle,
+ struct cpe_lsm_session *session,
+ struct lsm_hw_params *param);
+ int (*lsm_set_port)(void *core_handle,
+ struct cpe_lsm_session *session, void *data);
};
int wcd_cpe_get_lsm_ops(struct wcd_cpe_lsm_ops *);
diff --git a/include/uapi/linux/msm_kgsl.h b/include/uapi/linux/msm_kgsl.h
index dbba773cd49d..a80278954a77 100644
--- a/include/uapi/linux/msm_kgsl.h
+++ b/include/uapi/linux/msm_kgsl.h
@@ -308,6 +308,7 @@ enum kgsl_timestamp_type {
#define KGSL_PROP_GPMU_VERSION 0x16
#define KGSL_PROP_HIGHEST_BANK_BIT 0x17
#define KGSL_PROP_DEVICE_BITNESS 0x18
+#define KGSL_PROP_DEVICE_QDSS_STM 0x19
struct kgsl_shadowprop {
unsigned long gpuaddr;
@@ -315,6 +316,11 @@ struct kgsl_shadowprop {
unsigned int flags; /* contains KGSL_FLAGS_ values */
};
+struct kgsl_qdss_stm_prop {
+ uint64_t gpuaddr;
+ uint64_t size;
+};
+
struct kgsl_version {
unsigned int drv_major;
unsigned int drv_minor;
diff --git a/include/uapi/linux/msm_mdp.h b/include/uapi/linux/msm_mdp.h
index a1237ed996a5..1df65c7f90b3 100644
--- a/include/uapi/linux/msm_mdp.h
+++ b/include/uapi/linux/msm_mdp.h
@@ -1176,6 +1176,11 @@ struct mdss_ad_cfg {
uint32_t bl_ctrl_mode;
};
+struct mdss_ad_bl_cfg {
+ uint32_t bl_min_delta;
+ uint32_t bl_low_limit;
+};
+
/* ops uses standard MDP_PP_* flags */
struct mdss_ad_init_cfg {
uint32_t ops;
@@ -1220,11 +1225,14 @@ enum {
mdp_op_calib_dcm_state,
mdp_op_max,
mdp_op_pa_dither_cfg,
+ mdp_op_ad_bl_cfg,
mdp_op_pp_max = 255,
};
#define mdp_op_pa_dither_cfg mdp_op_pa_dither_cfg
#define mdp_op_pp_max mdp_op_pp_max
+#define mdp_op_ad_bl_cfg mdp_op_ad_bl_cfg
+
enum {
WB_FORMAT_NV12,
WB_FORMAT_RGB_565,
@@ -1254,6 +1262,7 @@ struct msmfb_mdp_pp {
struct mdss_ad_input ad_input;
struct mdp_calib_config_buffer calib_buffer;
struct mdp_calib_dcm_state calib_dcm;
+ struct mdss_ad_bl_cfg ad_bl_cfg;
} data;
};
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 25627f584405..b5323800eeb5 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -1764,8 +1764,9 @@ enum nl80211_commands {
* over all channels.
*
* @NL80211_ATTR_SCHED_SCAN_DELAY: delay before the first cycle of a
- * scheduled scan (or a WoWLAN net-detect scan) is started, u32
- * in seconds.
+ * scheduled scan is started. Or the delay before a WoWLAN
+ * net-detect scan is started, counting from the moment the
+ * system is suspended. This value is a u32, in seconds.
* @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
* is operating in an indoor environment.
@@ -1782,11 +1783,26 @@ enum nl80211_commands {
* thus it must not specify the number of iterations, only the interval
* between scans. The scan plans are executed sequentially.
* Each scan plan is a nested attribute of &enum nl80211_sched_scan_plan.
- *
* @NL80211_ATTR_PBSS: flag attribute. If set it means operate
* in a PBSS. Specified in %NL80211_CMD_CONNECT to request
* connecting to a PCP, and in %NL80211_CMD_START_AP to start
* a PCP instead of AP. Relevant for DMG networks only.
+ * @NL80211_ATTR_BSS_SELECT: nested attribute for driver supporting the
+ * BSS selection feature. When used with %NL80211_CMD_GET_WIPHY it contains
+ * attributes according &enum nl80211_bss_select_attr to indicate what
+ * BSS selection behaviours are supported. When used with %NL80211_CMD_CONNECT
+ * it contains the behaviour-specific attribute containing the parameters for
+ * BSS selection to be done by driver and/or firmware.
+ *
+ * @NL80211_ATTR_STA_SUPPORT_P2P_PS: whether P2P PS mechanism supported
+ * or not. u8, one of the values of &enum nl80211_sta_p2p_ps_status
+ *
+ * @NL80211_ATTR_PAD: attribute used for padding for 64-bit alignment
+ *
+ * @NL80211_ATTR_IFTYPE_EXT_CAPA: Nested attribute of the following attributes:
+ * %NL80211_ATTR_IFTYPE, %NL80211_ATTR_EXT_CAPA,
+ * %NL80211_ATTR_EXT_CAPA_MASK, to specify the extended capabilities per
+ * interface type.
*
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
@@ -2164,6 +2180,14 @@ enum nl80211_attrs {
NL80211_ATTR_PBSS,
+ NL80211_ATTR_BSS_SELECT,
+
+ NL80211_ATTR_STA_SUPPORT_P2P_PS,
+
+ NL80211_ATTR_PAD,
+
+ NL80211_ATTR_IFTYPE_EXT_CAPA,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
diff --git a/include/uapi/media/msmb_isp.h b/include/uapi/media/msmb_isp.h
index e658f4b56df9..93613855228e 100644
--- a/include/uapi/media/msmb_isp.h
+++ b/include/uapi/media/msmb_isp.h
@@ -6,6 +6,7 @@
#define MAX_PLANES_PER_STREAM 3
#define MAX_NUM_STREAM 7
+#define ISP_VERSION_48 48
#define ISP_VERSION_47 47
#define ISP_VERSION_46 46
#define ISP_VERSION_44 44
diff --git a/kernel/drivers/input/touchscreen/synaptics_fw_update.c b/kernel/drivers/input/touchscreen/synaptics_fw_update.c
index 4867d1f73c4d..8b6d7c7e368d 100644
--- a/kernel/drivers/input/touchscreen/synaptics_fw_update.c
+++ b/kernel/drivers/input/touchscreen/synaptics_fw_update.c
@@ -30,7 +30,10 @@
#define DEBUG_FW_UPDATE
#define SHOW_PROGRESS
-#define FW_IMAGE_NAME "PR12345678.img"
+#define FW_IMAGE_NAME "PR1063486-s7301_00000000.img"
+#define MAX_FIRMWARE_ID_LEN 10
+#define FORCE_UPDATE false
+#define INSIDE_FIRMWARE_UPDATE
#define CHECKSUM_OFFSET 0x00
#define BOOTLOADER_VERSION_OFFSET 0x07
@@ -73,6 +76,12 @@ enum flash_command {
CMD_ENABLE_FLASH_PROG = 0xF,
};
+enum flash_area {
+ NONE,
+ UI_FIRMWARE,
+ CONFIG_AREA
+};
+
#define SLEEP_MODE_NORMAL (0x00)
#define SLEEP_MODE_SENSOR_SLEEP (0x01)
#define SLEEP_MODE_RESERVED0 (0x02)
@@ -81,9 +90,9 @@ enum flash_command {
#define ENABLE_WAIT_MS (1 * 1000)
#define WRITE_WAIT_MS (3 * 1000)
#define ERASE_WAIT_MS (5 * 1000)
+#define RESET_WAIT_MS (500)
-#define MIN_SLEEP_TIME_US 50
-#define MAX_SLEEP_TIME_US 100
+#define SLEEP_TIME_US 50
static ssize_t fwu_sysfs_show_image(struct file *data_file,
struct kobject *kobj, struct bin_attribute *attributes,
@@ -204,6 +213,7 @@ struct f34_flash_properties {
struct synaptics_rmi4_fwu_handle {
bool initialized;
+ bool force_update;
char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1];
unsigned int image_size;
unsigned int data_pos;
@@ -231,6 +241,8 @@ struct synaptics_rmi4_fwu_handle {
struct synaptics_rmi4_data *rmi4_data;
struct f34_flash_control flash_control;
struct f34_flash_properties flash_properties;
+ struct workqueue_struct *fwu_workqueue;
+ struct delayed_work fwu_work;
};
static struct bin_attribute dev_attr_data = {
@@ -313,53 +325,6 @@ static void parse_header(struct image_header *header,
return;
}
-static int fwu_check_version(void)
-{
- int retval;
- unsigned char firmware_id[4];
- unsigned char config_id[4];
- struct i2c_client *i2c_client = fwu->rmi4_data->i2c_client;
-
- /* device firmware id */
- retval = fwu->fn_ptr->read(fwu->rmi4_data,
- fwu->f01_fd.query_base_addr + 18,
- firmware_id,
- sizeof(firmware_id));
- if (retval < 0) {
- dev_err(&i2c_client->dev,
- "Failed to read firmware ID (code %d).\n", retval);
- return retval;
- }
- firmware_id[3] = 0;
-
- dev_info(&i2c_client->dev, "Device firmware ID%d\n",
- extract_uint(firmware_id));
-
- /* device config id */
- retval = fwu->fn_ptr->read(fwu->rmi4_data,
- fwu->f34_fd.ctrl_base_addr,
- config_id,
- sizeof(config_id));
- if (retval < 0) {
- dev_err(&i2c_client->dev,
- "Failed to read config ID (code %d).\n", retval);
- return retval;
- }
-
- dev_info(&i2c_client->dev,
- "Device config ID 0x%02X, 0x%02X, 0x%02X, 0x%02X\n",
- config_id[0], config_id[1], config_id[2], config_id[3]);
-
- /* .img config id */
- dev_info(&i2c_client->dev,
- ".img config ID 0x%02X, 0x%02X, 0x%02X, 0x%02X\n",
- fwu->config_data[0],
- fwu->config_data[1],
- fwu->config_data[2],
- fwu->config_data[3]);
- return 0;
-}
-
static int fwu_read_f01_device_status(struct f01_device_status *status)
{
int retval;
@@ -407,7 +372,7 @@ static int fwu_read_f34_queries(void)
return retval;
}
- dev_info(&i2c_client->dev, "%s perm:%d, bl%d, display:%d\n",
+ dev_info(&i2c_client->dev, "%s perm:%d, bl:%d, display:%d\n",
__func__,
fwu->flash_properties.has_perm_config,
fwu->flash_properties.has_bl_config,
@@ -506,25 +471,13 @@ static int fwu_read_f34_flash_status(void)
static int fwu_reset_device(void)
{
int retval;
- unsigned char reset = 0x01;
#ifdef DEBUG_FW_UPDATE
- dev_info(&fwu->rmi4_data->i2c_client->dev, "Reset device\n");
+ dev_info(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Reset device\n",
+ __func__);
#endif
- retval = fwu->fn_ptr->write(fwu->rmi4_data,
- fwu->f01_fd.cmd_base_addr,
- &reset,
- sizeof(reset));
- if (retval < 0) {
- dev_err(&fwu->rmi4_data->i2c_client->dev,
- "%s: Failed to reset device (addr : 0x%02x)\n",
- __func__, fwu->f01_fd.cmd_base_addr);
- return retval;
- }
-
- fwu_wait_for_idle(WRITE_WAIT_MS);
-
retval = fwu->rmi4_data->reset_device(fwu->rmi4_data);
if (retval < 0) {
dev_err(&fwu->rmi4_data->i2c_client->dev,
@@ -539,37 +492,34 @@ static int fwu_write_f34_command(unsigned char cmd)
{
int retval;
+ fwu->flash_control.data[0] = cmd;
retval = fwu->fn_ptr->write(fwu->rmi4_data,
fwu->addr_f34_flash_control,
- &cmd,
- sizeof(cmd));
+ fwu->flash_control.data,
+ sizeof(fwu->flash_control.data));
if (retval < 0) {
dev_err(&fwu->rmi4_data->i2c_client->dev,
"%s: Failed to write command 0x%02x\n",
- __func__, cmd);
+ __func__, fwu->flash_control.data[0]);
return retval;
}
return 0;
}
-static unsigned char fwu_check_flash_status(void)
-{
- fwu_read_f34_flash_status();
- return fwu->flash_control.status;
-}
-
static int fwu_wait_for_idle(int timeout_ms)
{
int count = 0;
- int timeout_count = ((timeout_ms * 1000) / MAX_SLEEP_TIME_US) + 1;
-
+ int timeout_count = ((timeout_ms * 1000) / SLEEP_TIME_US) + 1;
do {
- if (fwu_read_interrupt_status() > 0)
+ if (fwu->flash_control.command == 0x00)
return 0;
- usleep_range(MIN_SLEEP_TIME_US, MAX_SLEEP_TIME_US);
- count++;
- } while (count < timeout_count);
+ usleep_range(SLEEP_TIME_US, SLEEP_TIME_US + 100);
+ } while (count++ < timeout_count);
+
+ fwu_read_f34_flash_status();
+ if (fwu->flash_control.command == 0x00)
+ return 0;
dev_err(&fwu->rmi4_data->i2c_client->dev,
"%s: Timed out waiting for idle status\n",
@@ -578,6 +528,133 @@ static int fwu_wait_for_idle(int timeout_ms)
return -ETIMEDOUT;
}
+static enum flash_area fwu_go_nogo(void)
+{
+ int retval = 0;
+ int index = 0;
+ int deviceFirmwareID;
+ int imageConfigID;
+ int deviceConfigID;
+ unsigned long imageFirmwareID;
+ unsigned char firmware_id[4];
+ unsigned char config_id[4];
+ char *strptr;
+ char *imagePR = kzalloc(sizeof(MAX_FIRMWARE_ID_LEN), GFP_KERNEL);
+ enum flash_area flash_area = NONE;
+ struct i2c_client *i2c_client = fwu->rmi4_data->i2c_client;
+ struct f01_device_status f01_device_status;
+
+ if (fwu->force_update) {
+ flash_area = UI_FIRMWARE;
+ goto exit;
+ }
+
+ retval = fwu_read_f01_device_status(&f01_device_status);
+ if (retval < 0) {
+ flash_area = NONE;
+ goto exit;
+ }
+
+ imagePR = kzalloc(sizeof(MAX_FIRMWARE_ID_LEN), GFP_KERNEL);
+
+ /* Force update firmware when device is in bootloader mode */
+ if (f01_device_status.flash_prog) {
+ dev_info(&i2c_client->dev,
+ "%s: In flash prog mode\n",
+ __func__);
+ flash_area = UI_FIRMWARE;
+ goto exit;
+ }
+
+
+ /* device firmware id */
+ retval = fwu->fn_ptr->read(fwu->rmi4_data,
+ fwu->f01_fd.query_base_addr + 18,
+ firmware_id,
+ sizeof(firmware_id));
+ if (retval < 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to read firmware ID (code %d).\n", retval);
+ goto exit;
+ }
+ firmware_id[3] = 0;
+ deviceFirmwareID = extract_uint(firmware_id);
+
+ /* .img firmware id */
+ strptr = strstr(FW_IMAGE_NAME, "PR");
+ if (!strptr) {
+ dev_err(&i2c_client->dev,
+ "No valid PR number (PRxxxxxxx)" \
+ "found in image file name...\n");
+ goto exit;
+ }
+
+ strptr += 2;
+ while (strptr[index] >= '0' && strptr[index] <= '9') {
+ imagePR[index] = strptr[index];
+ index++;
+ }
+ imagePR[index] = 0;
+
+ retval = sstrtoul(imagePR, 10, &imageFirmwareID);
+ if (retval == -EINVAL) {
+ dev_err(&i2c_client->dev,
+ "invalid image firmware id...\n");
+ goto exit;
+ }
+
+ dev_info(&i2c_client->dev,
+ "Device firmware id %d, .img firmware id %d\n",
+ deviceFirmwareID,
+ (unsigned int)imageFirmwareID);
+ if (imageFirmwareID > deviceFirmwareID) {
+ flash_area = UI_FIRMWARE;
+ goto exit;
+ }
+
+ /* device config id */
+ retval = fwu->fn_ptr->read(fwu->rmi4_data,
+ fwu->f34_fd.ctrl_base_addr,
+ config_id,
+ sizeof(config_id));
+ if (retval < 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to read config ID (code %d).\n", retval);
+ flash_area = NONE;
+ goto exit;
+ }
+ deviceConfigID = extract_uint(config_id);
+
+ dev_info(&i2c_client->dev,
+ "Device config ID 0x%02X, 0x%02X, 0x%02X, 0x%02X\n",
+ config_id[0], config_id[1], config_id[2], config_id[3]);
+
+ /* .img config id */
+ dev_info(&i2c_client->dev,
+ ".img config ID 0x%02X, 0x%02X, 0x%02X, 0x%02X\n",
+ fwu->config_data[0],
+ fwu->config_data[1],
+ fwu->config_data[2],
+ fwu->config_data[3]);
+ imageConfigID = extract_uint(fwu->config_data);
+
+ if (imageConfigID > deviceConfigID) {
+ flash_area = CONFIG_AREA;
+ goto exit;
+ }
+
+exit:
+ kfree(imagePR);
+ if (flash_area == NONE)
+ dev_info(&i2c_client->dev,
+ "Nothing needs to be updated\n");
+ else
+ dev_info(&i2c_client->dev,
+ "Update %s block\n",
+ flash_area == UI_FIRMWARE ? "UI FW" : "CONFIG");
+ return flash_area;
+}
+
static int fwu_scan_pdt(void)
{
int retval;
@@ -649,16 +726,25 @@ static int fwu_write_blocks(unsigned char *block_ptr, unsigned short block_cnt,
int retval;
unsigned char block_offset[] = {0, 0};
unsigned short block_num;
+ struct i2c_client *i2c_client = fwu->rmi4_data->i2c_client;
#ifdef SHOW_PROGRESS
unsigned int progress = (command == CMD_WRITE_CONFIG_BLOCK) ?
10 : 100;
#endif
+
+#ifdef DEBUG_FW_UPDATE
+ dev_info(&i2c_client->dev,
+ "%s: Start to update %s blocks\n",
+ __func__,
+ command == CMD_WRITE_CONFIG_BLOCK ?
+ "config" : "firmware");
+#endif
retval = fwu->fn_ptr->write(fwu->rmi4_data,
fwu->f34_fd.data_base_addr + BLOCK_NUMBER_OFFSET,
block_offset,
sizeof(block_offset));
if (retval < 0) {
- dev_err(&fwu->rmi4_data->i2c_client->dev,
+ dev_err(&i2c_client->dev,
"%s: Failed to write to block number registers\n",
__func__);
return retval;
@@ -667,20 +753,19 @@ static int fwu_write_blocks(unsigned char *block_ptr, unsigned short block_cnt,
for (block_num = 0; block_num < block_cnt; block_num++) {
#ifdef SHOW_PROGRESS
if (block_num % progress == 0)
- dev_info(&fwu->rmi4_data->i2c_client->dev,
- "%s: update %s %3d / %3d\n",
- __func__,
- command == CMD_WRITE_CONFIG_BLOCK ?
- "config" : "firmware",
- block_num,
- block_cnt);
+ dev_info(&i2c_client->dev,
+ "%s: update %s %3d / %3d\n",
+ __func__,
+ command == CMD_WRITE_CONFIG_BLOCK ?
+ "config" : "firmware",
+ block_num, block_cnt);
#endif
retval = fwu->fn_ptr->write(fwu->rmi4_data,
fwu->f34_fd.data_base_addr + BLOCK_DATA_OFFSET,
block_ptr,
fwu->block_size);
if (retval < 0) {
- dev_err(&fwu->rmi4_data->i2c_client->dev,
+ dev_err(&i2c_client->dev,
"%s: Failed to write block data (block %d)\n",
__func__, block_num);
return retval;
@@ -688,7 +773,7 @@ static int fwu_write_blocks(unsigned char *block_ptr, unsigned short block_cnt,
retval = fwu_write_f34_command(command);
if (retval < 0) {
- dev_err(&fwu->rmi4_data->i2c_client->dev,
+ dev_err(&i2c_client->dev,
"%s: Failed to write command for block %d\n",
__func__, block_num);
return retval;
@@ -696,30 +781,28 @@ static int fwu_write_blocks(unsigned char *block_ptr, unsigned short block_cnt,
retval = fwu_wait_for_idle(WRITE_WAIT_MS);
if (retval < 0) {
- dev_err(&fwu->rmi4_data->i2c_client->dev,
- "%s: Failed to wait for idle status \
- (block %d)\n",
- __func__, block_num);
+ dev_err(&i2c_client->dev,
+ "%s: Failed to wait for idle status (block %d)\n",
+ __func__, block_num);
return retval;
}
- retval = fwu_check_flash_status();
- if (retval != 0) {
- dev_err(&fwu->rmi4_data->i2c_client->dev,
- "%s: Flash block %d status %d\n",
+ if (fwu->flash_control.status != 0x00) {
+ dev_err(&i2c_client->dev,
+ "%s: Flash block %d failed, status 0x%02X\n",
__func__, block_num, retval);
return -1;
}
+
block_ptr += fwu->block_size;
}
#ifdef SHOW_PROGRESS
- dev_info(&fwu->rmi4_data->i2c_client->dev,
- "%s: update %s %3d / %3d\n",
- __func__,
- command == CMD_WRITE_CONFIG_BLOCK ?
- "config" : "firmware",
- block_cnt,
- block_cnt);
+ dev_info(&i2c_client->dev,
+ "%s: update %s %3d / %3d\n",
+ __func__,
+ command == CMD_WRITE_CONFIG_BLOCK ?
+ "config" : "firmware",
+ block_cnt, block_cnt);
#endif
return 0;
}
@@ -741,7 +824,10 @@ static int fwu_write_bootloader_id(void)
int retval;
#ifdef DEBUG_FW_UPDATE
- dev_info(&fwu->rmi4_data->i2c_client->dev, "Write bootloader ID\n");
+ dev_info(&fwu->rmi4_data->i2c_client->dev,
+ "Write bootloader ID 0x%02X 0x%02X\n",
+ fwu->bootloader_id[0],
+ fwu->bootloader_id[1]);
#endif
retval = fwu->fn_ptr->write(fwu->rmi4_data,
fwu->f34_fd.data_base_addr + BLOCK_DATA_OFFSET,
@@ -789,17 +875,6 @@ static int fwu_enter_flash_prog(void)
if (retval < 0)
return retval;
- retval = fwu_read_f01_device_status(&f01_device_status);
- if (retval < 0)
- return retval;
-
- if (!f01_device_status.flash_prog) {
- dev_err(&fwu->rmi4_data->i2c_client->dev,
- "%s: Program enabled bit not set\n",
- __func__);
- return -EINVAL;
- }
-
retval = fwu_scan_pdt();
if (retval < 0)
return retval;
@@ -879,9 +954,12 @@ static int fwu_do_reflash(void)
if (retval < 0)
return retval;
- dev_dbg(&fwu->rmi4_data->i2c_client->dev,
- "%s: Idle status detected\n",
- __func__);
+ if (fwu->flash_control.status != 0x00) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Erase all command failed, status 0x%02X\n",
+ __func__, retval);
+ return -1;
+ }
if (fwu->firmware_data) {
retval = fwu_write_firmware();
@@ -900,90 +978,6 @@ static int fwu_do_reflash(void)
return retval;
}
-static int fwu_start_reflash(void)
-{
- int retval;
- struct image_header header;
- const unsigned char *fw_image;
- const struct firmware *fw_entry = NULL;
- struct f01_device_status f01_device_status;
-
- pr_notice("%s: Start of reflash process\n", __func__);
-
- if (fwu->ext_data_source)
- fw_image = fwu->ext_data_source;
- else {
- dev_dbg(&fwu->rmi4_data->i2c_client->dev,
- "%s: Requesting firmware image %s\n",
- __func__, FW_IMAGE_NAME);
-
- retval = request_firmware(&fw_entry, FW_IMAGE_NAME,
- &fwu->rmi4_data->i2c_client->dev);
- if (retval != 0) {
- dev_err(&fwu->rmi4_data->i2c_client->dev,
- "%s: Firmware image %s not available\n",
- __func__, FW_IMAGE_NAME);
- retval = -EINVAL;
- goto exit;
- }
-
- dev_dbg(&fwu->rmi4_data->i2c_client->dev,
- "%s: Firmware image size = %d\n",
- __func__, fw_entry->size);
-
- fw_image = fw_entry->data;
- }
-
- parse_header(&header, fw_image);
-
- if (header.image_size)
- fwu->firmware_data = fw_image + FW_IMAGE_OFFSET;
- if (header.config_size) {
- fwu->config_data = fw_image + FW_IMAGE_OFFSET +
- header.image_size;
- }
-
- fwu->fn_ptr->enable(fwu->rmi4_data, false);
-
- fwu_check_version();
-
- retval = fwu_do_reflash();
- if (retval < 0) {
- dev_err(&fwu->rmi4_data->i2c_client->dev,
- "%s: Failed to do reflash\n",
- __func__);
- }
-
- /* reset device */
- fwu_reset_device();
-
- /* check device status */
- retval = fwu_read_f01_device_status(&f01_device_status);
- if (retval < 0)
- goto exit;
-
- dev_info(&fwu->rmi4_data->i2c_client->dev, "Device is in %s mode\n",
- f01_device_status.flash_prog == 1 ? "bootloader" : "UI");
- if (f01_device_status.flash_prog)
- dev_info(&fwu->rmi4_data->i2c_client->dev, "Flash status %d\n",
- f01_device_status.status_code);
-
- if (f01_device_status.flash_prog) {
- dev_info(&fwu->rmi4_data->i2c_client->dev,
- "%s: Device is in flash prog mode 0x%02X\n",
- __func__, f01_device_status.status_code);
- retval = 0;
- goto exit;
- }
- fwu->fn_ptr->enable(fwu->rmi4_data, true);
- if (fw_entry)
- release_firmware(fw_entry);
-
- pr_notice("%s: End of reflash process\n", __func__);
-exit:
- return retval;
-}
-
static int fwu_do_write_config(void)
{
int retval;
@@ -1205,6 +1199,110 @@ exit:
return retval;
}
+static int fwu_start_reflash(void)
+{
+ int retval;
+ struct image_header header;
+ const unsigned char *fw_image;
+ const struct firmware *fw_entry = NULL;
+ struct f01_device_status f01_device_status;
+ enum flash_area flash_area;
+
+ pr_notice("%s: Start of reflash process\n", __func__);
+
+ if (fwu->ext_data_source)
+ fw_image = fwu->ext_data_source;
+ else {
+ dev_dbg(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Requesting firmware image %s\n",
+ __func__, FW_IMAGE_NAME);
+
+ retval = request_firmware(&fw_entry, FW_IMAGE_NAME,
+ &fwu->rmi4_data->i2c_client->dev);
+ if (retval != 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Firmware image %s not available\n",
+ __func__, FW_IMAGE_NAME);
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ dev_dbg(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Firmware image size = %d\n",
+ __func__, fw_entry->size);
+
+ fw_image = fw_entry->data;
+ }
+
+ parse_header(&header, fw_image);
+
+ if (header.image_size)
+ fwu->firmware_data = fw_image + FW_IMAGE_OFFSET;
+ if (header.config_size) {
+ fwu->config_data = fw_image + FW_IMAGE_OFFSET +
+ header.image_size;
+ }
+
+ if (fwu->ext_data_source)
+ flash_area = UI_FIRMWARE;
+ else
+ flash_area = fwu_go_nogo();
+
+ switch (flash_area) {
+ case NONE:
+ dev_info(&fwu->rmi4_data->i2c_client->dev,
+ "%s: No need to do reflash.\n",
+ __func__);
+ goto exit;
+ case UI_FIRMWARE:
+ retval = fwu_do_reflash();
+ break;
+ case CONFIG_AREA:
+ retval = fwu_do_write_config();
+ break;
+ default:
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Unknown flash area\n",
+ __func__);
+ goto exit;
+ }
+
+ if (retval < 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Failed to do reflash\n",
+ __func__);
+ }
+
+ /* reset device */
+ fwu_reset_device();
+
+ /* check device status */
+ retval = fwu_read_f01_device_status(&f01_device_status);
+ if (retval < 0)
+ goto exit;
+
+ dev_info(&fwu->rmi4_data->i2c_client->dev, "Device is in %s mode\n",
+ f01_device_status.flash_prog == 1 ? "bootloader" : "UI");
+ if (f01_device_status.flash_prog)
+ dev_info(&fwu->rmi4_data->i2c_client->dev, "Flash status %d\n",
+ f01_device_status.status_code);
+
+ if (f01_device_status.flash_prog) {
+ dev_info(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Device is in flash prog mode 0x%02X\n",
+ __func__, f01_device_status.status_code);
+ retval = 0;
+ goto exit;
+ }
+
+ if (fw_entry)
+ release_firmware(fw_entry);
+
+ pr_notice("%s: End of reflash process\n", __func__);
+exit:
+ return retval;
+}
+
int synaptics_fw_updater(unsigned char *fw_data)
{
int retval;
@@ -1431,6 +1529,11 @@ static void synaptics_rmi4_fwu_attn(struct synaptics_rmi4_data *rmi4_data,
return;
}
+static void synaptics_rmi4_fwu_work(struct work_struct *work)
+{
+ fwu_start_reflash();
+}
+
static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
{
int retval;
@@ -1497,6 +1600,7 @@ static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
goto exit_free_mem;
fwu->initialized = true;
+ fwu->force_update = FORCE_UPDATE;
retval = sysfs_create_bin_file(&rmi4_data->input_dev->dev.kobj,
&dev_attr_data);
@@ -1519,6 +1623,13 @@ static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
}
}
+#ifdef INSIDE_FIRMWARE_UPDATE
+ fwu->fwu_workqueue = create_singlethread_workqueue("fwu_workqueue");
+ INIT_DELAYED_WORK(&fwu->fwu_work, synaptics_rmi4_fwu_work);
+ queue_delayed_work(fwu->fwu_workqueue,
+ &fwu->fwu_work,
+ msecs_to_jiffies(1000));
+#endif
return 0;
exit_remove_attrs:
diff --git a/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.c b/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.c
index 85530225abd2..76f9155bd49c 100644
--- a/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.c
+++ b/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.c
@@ -347,28 +347,32 @@ static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev,
if (rmi4_data->button_0d_enabled == input)
return count;
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) {
- ii = fhandler->intr_reg_num;
+ if (!list_empty(&rmi->support_fn_list)) {
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) {
+ ii = fhandler->intr_reg_num;
- retval = synaptics_rmi4_i2c_read(rmi4_data,
- rmi4_data->f01_ctrl_base_addr + 1 + ii,
- &intr_enable,
- sizeof(intr_enable));
- if (retval < 0)
- return retval;
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ rmi4_data->f01_ctrl_base_addr +
+ 1 + ii,
+ &intr_enable,
+ sizeof(intr_enable));
+ if (retval < 0)
+ return retval;
- if (input == 1)
- intr_enable |= fhandler->intr_mask;
- else
- intr_enable &= ~fhandler->intr_mask;
+ if (input == 1)
+ intr_enable |= fhandler->intr_mask;
+ else
+ intr_enable &= ~fhandler->intr_mask;
- retval = synaptics_rmi4_i2c_write(rmi4_data,
- rmi4_data->f01_ctrl_base_addr + 1 + ii,
- &intr_enable,
- sizeof(intr_enable));
- if (retval < 0)
- return retval;
+ retval = synaptics_rmi4_i2c_write(rmi4_data,
+ rmi4_data->f01_ctrl_base_addr +
+ 1 + ii,
+ &intr_enable,
+ sizeof(intr_enable));
+ if (retval < 0)
+ return retval;
+ }
}
}
@@ -637,28 +641,22 @@ static int synaptics_rmi4_f11_abs_report(struct synaptics_rmi4_data *rmi4_data,
finger_status,
x, y, wx, wy);
-#ifdef TYPE_B_PROTOCOL
- input_report_abs(rmi4_data->input_dev,
- ABS_MT_POSITION_X, x);
- input_report_abs(rmi4_data->input_dev,
- ABS_MT_POSITION_Y, y);
-#ifdef REPORT_2D_W
- input_report_abs(rmi4_data->input_dev,
- ABS_MT_TOUCH_MAJOR, max(wx, wy));
- input_report_abs(rmi4_data->input_dev,
- ABS_MT_TOUCH_MINOR, min(wx, wy));
-#endif
-#else
+ input_report_key(rmi4_data->input_dev,
+ BTN_TOUCH, 1);
+ input_report_key(rmi4_data->input_dev,
+ BTN_TOOL_FINGER, 1);
input_report_abs(rmi4_data->input_dev,
ABS_MT_POSITION_X, x);
input_report_abs(rmi4_data->input_dev,
ABS_MT_POSITION_Y, y);
+
#ifdef REPORT_2D_W
input_report_abs(rmi4_data->input_dev,
ABS_MT_TOUCH_MAJOR, max(wx, wy));
input_report_abs(rmi4_data->input_dev,
ABS_MT_TOUCH_MINOR, min(wx, wy));
#endif
+#ifndef TYPE_B_PROTOCOL
input_mt_sync(rmi4_data->input_dev);
#endif
touch_count++;
@@ -853,12 +851,14 @@ static int synaptics_rmi4_sensor_report(struct synaptics_rmi4_data *rmi4_data)
* Traverse the function handler list and service the source(s)
* of the interrupt accordingly.
*/
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->num_of_data_sources) {
- if (fhandler->intr_mask &
- intr[fhandler->intr_reg_num]) {
- synaptics_rmi4_report_touch(rmi4_data,
- fhandler, &touch_count);
+ if (!list_empty(&rmi->support_fn_list)) {
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->num_of_data_sources) {
+ if (fhandler->intr_mask &
+ intr[fhandler->intr_reg_num]) {
+ synaptics_rmi4_report_touch(rmi4_data,
+ fhandler, &touch_count);
+ }
}
}
}
@@ -1088,8 +1088,8 @@ static int synaptics_rmi4_capacitance_button_map(
if (!pdata->capacitance_button_map) {
dev_err(&rmi4_data->i2c_client->dev,
- "%s: capacitance_button_map is \
- NULL in board file\n",
+ "%s: capacitance_button_map is" \
+ "NULL in board file\n",
__func__);
return -ENODEV;
} else if (!pdata->capacitance_button_map->map) {
@@ -1194,6 +1194,63 @@ static int synaptics_rmi4_alloc_fh(struct synaptics_rmi4_fn **fhandler,
return 0;
}
+
+ /**
+ * synaptics_rmi4_query_device_info()
+ *
+ * Called by synaptics_rmi4_query_device().
+ *
+ */
+static int synaptics_rmi4_query_device_info(
+ struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval;
+ unsigned char f01_query[F01_STD_QUERY_LEN];
+ struct synaptics_rmi4_device_info *rmi = &(rmi4_data->rmi4_mod_info);
+
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ rmi4_data->f01_query_base_addr,
+ f01_query,
+ sizeof(f01_query));
+ if (retval < 0)
+ return retval;
+
+ /* RMI Version 4.0 currently supported */
+ rmi->version_major = 4;
+ rmi->version_minor = 0;
+
+ rmi->manufacturer_id = f01_query[0];
+ rmi->product_props = f01_query[1];
+ rmi->product_info[0] = f01_query[2] & MASK_7BIT;
+ rmi->product_info[1] = f01_query[3] & MASK_7BIT;
+ rmi->date_code[0] = f01_query[4] & MASK_5BIT;
+ rmi->date_code[1] = f01_query[5] & MASK_4BIT;
+ rmi->date_code[2] = f01_query[6] & MASK_5BIT;
+ rmi->tester_id = ((f01_query[7] & MASK_7BIT) << 8) |
+ (f01_query[8] & MASK_7BIT);
+ rmi->serial_number = ((f01_query[9] & MASK_7BIT) << 8) |
+ (f01_query[10] & MASK_7BIT);
+ memcpy(rmi->product_id_string, &f01_query[11], 10);
+
+ if (rmi->manufacturer_id != 1) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Non-Synaptics device found, manufacturer ID = %d\n",
+ __func__, rmi->manufacturer_id);
+ }
+
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ rmi4_data->f01_query_base_addr + F01_BUID_ID_OFFSET,
+ rmi->build_id,
+ sizeof(rmi->build_id));
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to read firmware build id (code %d)\n",
+ __func__, retval);
+ return retval;
+ }
+ return retval;
+}
+
/**
* synaptics_rmi4_query_device()
*
@@ -1214,7 +1271,6 @@ static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data)
unsigned char page_number;
unsigned char intr_count = 0;
unsigned char data_sources = 0;
- unsigned char f01_query[F01_STD_QUERY_LEN];
unsigned short pdt_entry_addr;
unsigned short intr_addr;
struct synaptics_rmi4_f01_device_status status;
@@ -1264,6 +1320,11 @@ static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data)
rmi4_data->f01_cmd_base_addr =
rmi_fd.cmd_base_addr;
+ retval =
+ synaptics_rmi4_query_device_info(rmi4_data);
+ if (retval < 0)
+ return retval;
+
retval = synaptics_rmi4_i2c_read(rmi4_data,
rmi4_data->f01_data_base_addr,
status.data,
@@ -1277,7 +1338,17 @@ static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data)
status.status_code);
goto flash_prog_mode;
}
- break;
+ break;
+
+ case SYNAPTICS_RMI4_F34:
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ rmi_fd.ctrl_base_addr,
+ rmi->config_id,
+ sizeof(rmi->config_id));
+ if (retval < 0)
+ return retval;
+ break;
+
case SYNAPTICS_RMI4_F11:
if (rmi_fd.intr_src_count == 0)
break;
@@ -1335,56 +1406,24 @@ flash_prog_mode:
"%s: Number of interrupt registers = %d\n",
__func__, rmi4_data->num_of_intr_regs);
- retval = synaptics_rmi4_i2c_read(rmi4_data,
- rmi4_data->f01_query_base_addr,
- f01_query,
- sizeof(f01_query));
- if (retval < 0)
- return retval;
-
- /* RMI Version 4.0 currently supported */
- rmi->version_major = 4;
- rmi->version_minor = 0;
-
- rmi->manufacturer_id = f01_query[0];
- rmi->product_props = f01_query[1];
- rmi->product_info[0] = f01_query[2] & MASK_7BIT;
- rmi->product_info[1] = f01_query[3] & MASK_7BIT;
- rmi->date_code[0] = f01_query[4] & MASK_5BIT;
- rmi->date_code[1] = f01_query[5] & MASK_4BIT;
- rmi->date_code[2] = f01_query[6] & MASK_5BIT;
- rmi->tester_id = ((f01_query[7] & MASK_7BIT) << 8) |
- (f01_query[8] & MASK_7BIT);
- rmi->serial_number = ((f01_query[9] & MASK_7BIT) << 8) |
- (f01_query[10] & MASK_7BIT);
- memcpy(rmi->product_id_string, &f01_query[11], 10);
-
- if (rmi->manufacturer_id != 1) {
- dev_err(&rmi4_data->i2c_client->dev,
- "%s: Non-Synaptics device found, manufacturer ID = %d\n",
- __func__, rmi->manufacturer_id);
- }
-
- retval = synaptics_rmi4_i2c_read(rmi4_data,
- rmi4_data->f01_query_base_addr + F01_BUID_ID_OFFSET,
- rmi->build_id,
- sizeof(rmi->build_id));
- if (retval < 0)
- return retval;
-
memset(rmi4_data->intr_mask, 0x00, sizeof(rmi4_data->intr_mask));
/*
* Map out the interrupt bit masks for the interrupt sources
* from the registered function handlers.
*/
- list_for_each_entry(fhandler, &rmi->support_fn_list, link)
- data_sources += fhandler->num_of_data_sources;
+ if (!list_empty(&rmi->support_fn_list)) {
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link)
+ data_sources += fhandler->num_of_data_sources;
+ }
if (data_sources) {
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->num_of_data_sources) {
- rmi4_data->intr_mask[fhandler->intr_reg_num] |=
- fhandler->intr_mask;
+ if (!list_empty(&rmi->support_fn_list)) {
+ list_for_each_entry(fhandler,
+ &rmi->support_fn_list, link) {
+ if (fhandler->num_of_data_sources) {
+ rmi4_data->intr_mask[fhandler->intr_reg_num] |=
+ fhandler->intr_mask;
+ }
}
}
}
@@ -1430,12 +1469,14 @@ static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data)
msleep(100);
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
- synaptics_rmi4_f1a_kfree(fhandler);
- else
- kfree(fhandler->data);
- kfree(fhandler);
+ if (!list_empty(&rmi->support_fn_list)) {
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
+ synaptics_rmi4_f1a_kfree(fhandler);
+ else
+ kfree(fhandler->data);
+ kfree(fhandler);
+ }
}
retval = synaptics_rmi4_query_device(rmi4_data);
@@ -1534,12 +1575,14 @@ void synaptics_rmi4_new_function(enum exp_fn fn_type, bool insert,
exp_fhandler->inserted = false;
list_add_tail(&exp_fhandler->link, &exp_fn_list);
} else {
- list_for_each_entry(exp_fhandler, &exp_fn_list, link) {
- if (exp_fhandler->func_init == func_init) {
- exp_fhandler->inserted = false;
- exp_fhandler->func_init = NULL;
- exp_fhandler->func_attn = NULL;
- goto exit;
+ if (!list_empty(&exp_fn_list)) {
+ list_for_each_entry(exp_fhandler, &exp_fn_list, link) {
+ if (exp_fhandler->func_init == func_init) {
+ exp_fhandler->inserted = false;
+ exp_fhandler->func_init = NULL;
+ exp_fhandler->func_attn = NULL;
+ goto exit;
+ }
}
}
}
@@ -1611,7 +1654,7 @@ static int __devinit synaptics_rmi4_probe(struct i2c_client *client,
retval = -ENOMEM;
goto err_input_device;
}
-/*
+
if (platform_data->regulator_en) {
rmi4_data->regulator = regulator_get(&client->dev, "vdd");
if (IS_ERR(rmi4_data->regulator)) {
@@ -1623,7 +1666,7 @@ static int __devinit synaptics_rmi4_probe(struct i2c_client *client,
}
regulator_enable(rmi4_data->regulator);
}
-*/
+
rmi4_data->i2c_client = client;
rmi4_data->current_page = MASK_8BIT;
rmi4_data->board = platform_data;
@@ -1652,12 +1695,16 @@ static int __devinit synaptics_rmi4_probe(struct i2c_client *client,
rmi4_data->input_dev->name = DRIVER_NAME;
rmi4_data->input_dev->phys = INPUT_PHYS_NAME;
rmi4_data->input_dev->id.bustype = BUS_I2C;
+ rmi4_data->input_dev->id.product = SYNAPTICS_RMI4_DRIVER_PRODUCT;
+ rmi4_data->input_dev->id.version = SYNAPTICS_RMI4_DRIVER_VERSION;
rmi4_data->input_dev->dev.parent = &client->dev;
input_set_drvdata(rmi4_data->input_dev, rmi4_data);
set_bit(EV_SYN, rmi4_data->input_dev->evbit);
set_bit(EV_KEY, rmi4_data->input_dev->evbit);
set_bit(EV_ABS, rmi4_data->input_dev->evbit);
+ set_bit(BTN_TOUCH, rmi4_data->input_dev->keybit);
+ set_bit(BTN_TOOL_FINGER, rmi4_data->input_dev->keybit);
#ifdef INPUT_PROP_DIRECT
set_bit(INPUT_PROP_DIRECT, rmi4_data->input_dev->propbit);
@@ -1681,9 +1728,11 @@ static int __devinit synaptics_rmi4_probe(struct i2c_client *client,
#endif
f1a = NULL;
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
- f1a = fhandler->data;
+ if (!list_empty(&rmi->support_fn_list)) {
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
+ f1a = fhandler->data;
+ }
}
if (f1a) {
@@ -1775,16 +1824,17 @@ err_query_device:
regulator_put(rmi4_data->regulator);
}
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
- synaptics_rmi4_f1a_kfree(fhandler);
- else
- kfree(fhandler->data);
- kfree(fhandler);
+ if (!list_empty(&rmi->support_fn_list)) {
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
+ synaptics_rmi4_f1a_kfree(fhandler);
+ else
+ kfree(fhandler->data);
+ kfree(fhandler);
+ }
}
-/*
+
err_regulator:
-*/
input_free_device(rmi4_data->input_dev);
rmi4_data->input_dev = NULL;
@@ -1836,12 +1886,14 @@ static int __devexit synaptics_rmi4_remove(struct i2c_client *client)
regulator_put(rmi4_data->regulator);
}
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
- synaptics_rmi4_f1a_kfree(fhandler);
- else
- kfree(fhandler->data);
- kfree(fhandler);
+ if (!list_empty(&rmi->support_fn_list)) {
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
+ synaptics_rmi4_f1a_kfree(fhandler);
+ else
+ kfree(fhandler->data);
+ kfree(fhandler);
+ }
}
input_free_device(rmi4_data->input_dev);
diff --git a/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.h b/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.h
index 7ee0a925959a..ecb9b9415e8a 100644
--- a/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.h
+++ b/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.h
@@ -20,7 +20,10 @@
#ifndef _SYNAPTICS_DSX_RMI4_H_
#define _SYNAPTICS_DSX_RMI4_H_
-#define SYNAPTICS_RMI4_DRIVER_VERSION "DSX 1.0"
+#define SYNAPTICS_RMI4_DS4 0x0001
+#define SYNAPTICS_RMI4_DS5 0x0002
+#define SYNAPTICS_RMI4_DRIVER_PRODUCT SYNAPTICS_RMI4_DS4
+#define SYNAPTICS_RMI4_DRIVER_VERSION 0x1001
#include <linux/version.h>
#ifdef CONFIG_HAS_EARLYSUSPEND
@@ -158,6 +161,7 @@ struct synaptics_rmi4_device_info {
unsigned short serial_number;
unsigned char product_id_string[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1];
unsigned char build_id[SYNAPTICS_RMI4_BUILD_ID_SIZE];
+ unsigned char config_id[3];
struct list_head support_fn_list;
};
diff --git a/net/activity_stats.c b/net/activity_stats.c
index 3bf92d80b8b9..8a3e93470069 100644
--- a/net/activity_stats.c
+++ b/net/activity_stats.c
@@ -15,7 +15,6 @@
*/
#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
#include <linux/suspend.h>
#include <net/net_namespace.h>
@@ -60,19 +59,29 @@ void activity_stats_update(void)
spin_unlock_irqrestore(&activity_lock, flags);
}
-static int activity_stats_show(struct seq_file *m, void *v)
+static int activity_stats_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
{
int i;
+ int len;
+ char *p = page;
- seq_printf(m, "Min Bucket(sec) Count\n");
+ /* Only print if offset is 0, or we have enough buffer space */
+ if (off || count < (30 * BUCKET_MAX + 22))
+ return -ENOMEM;
+
+ len = snprintf(p, count, "Min Bucket(sec) Count\n");
+ count -= len;
+ p += len;
for (i = 0; i < BUCKET_MAX; i++) {
- seq_printf(m, "%15d %lu\n", 1 << i, activity_stats[i]);
- if (seq_has_overflowed(m))
- return -ENOSPC;
+ len = snprintf(p, count, "%15d %lu\n", 1 << i, activity_stats[i]);
+ count -= len;
+ p += len;
}
+ *eof = 1;
- return 0;
+ return p - page;
}
static int activity_stats_notifier(struct notifier_block *nb,
@@ -91,26 +100,14 @@ static int activity_stats_notifier(struct notifier_block *nb,
return 0;
}
-static int activity_stats_open(struct inode *inode, struct file *file)
-{
- return single_open(file, activity_stats_show, PDE_DATA(inode));
-}
-
-static const struct file_operations activity_stats_fops = {
- .open = activity_stats_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release,
-};
-
static struct notifier_block activity_stats_notifier_block = {
.notifier_call = activity_stats_notifier,
};
static int __init activity_stats_init(void)
{
- proc_create("activity", S_IRUGO,
- init_net.proc_net_stat, &activity_stats_fops);
+ create_proc_read_entry("activity", S_IRUGO,
+ init_net.proc_net_stat, activity_stats_read_proc, NULL);
return register_pm_notifier(&activity_stats_notifier_block);
}
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 7426ab13cede..6d3402434a63 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -730,6 +730,36 @@ int wiphy_register(struct wiphy *wiphy)
nl80211_send_reg_change_event(&request);
}
+ /* Check that nobody globally advertises any capabilities they do not
+ * advertise on all possible interface types.
+ */
+ if (wiphy->extended_capabilities_len &&
+ wiphy->num_iftype_ext_capab &&
+ wiphy->iftype_ext_capab) {
+ u8 supported_on_all, j;
+ const struct wiphy_iftype_ext_capab *capab;
+
+ capab = wiphy->iftype_ext_capab;
+ for (j = 0; j < wiphy->extended_capabilities_len; j++) {
+ if (capab[0].extended_capabilities_len > j)
+ supported_on_all =
+ capab[0].extended_capabilities[j];
+ else
+ supported_on_all = 0x00;
+ for (i = 1; i < wiphy->num_iftype_ext_capab; i++) {
+ if (j >= capab[i].extended_capabilities_len) {
+ supported_on_all = 0x00;
+ break;
+ }
+ supported_on_all &=
+ capab[i].extended_capabilities[j];
+ }
+ if (WARN_ON(wiphy->extended_capabilities[j] &
+ ~supported_on_all))
+ break;
+ }
+ }
+
rdev->wiphy.registered = true;
rtnl_unlock();
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index ad4b729262fd..30f54d1fc841 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1253,7 +1253,7 @@ nl80211_send_mgmt_stypes(struct sk_buff *msg,
struct nl80211_dump_wiphy_state {
s64 filter_wiphy;
long start;
- long split_start, band_start, chan_start;
+ long split_start, band_start, chan_start, capa_start;
bool split;
};
@@ -1731,6 +1731,47 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
rdev->wiphy.ext_features))
goto nla_put_failure;
+ state->split_start++;
+ break;
+ case 13:
+ if (rdev->wiphy.num_iftype_ext_capab &&
+ rdev->wiphy.iftype_ext_capab) {
+ struct nlattr *nested_ext_capab, *nested;
+
+ nested = nla_nest_start(msg,
+ NL80211_ATTR_IFTYPE_EXT_CAPA);
+ if (!nested)
+ goto nla_put_failure;
+
+ for (i = state->capa_start;
+ i < rdev->wiphy.num_iftype_ext_capab; i++) {
+ const struct wiphy_iftype_ext_capab *capab;
+
+ capab = &rdev->wiphy.iftype_ext_capab[i];
+
+ nested_ext_capab = nla_nest_start(msg, i);
+ if (!nested_ext_capab ||
+ nla_put_u32(msg, NL80211_ATTR_IFTYPE,
+ capab->iftype) ||
+ nla_put(msg, NL80211_ATTR_EXT_CAPA,
+ capab->extended_capabilities_len,
+ capab->extended_capabilities) ||
+ nla_put(msg, NL80211_ATTR_EXT_CAPA_MASK,
+ capab->extended_capabilities_len,
+ capab->extended_capabilities_mask))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, nested_ext_capab);
+ if (state->split)
+ break;
+ }
+ nla_nest_end(msg, nested);
+ if (i < rdev->wiphy.num_iftype_ext_capab) {
+ state->capa_start = i + 1;
+ break;
+ }
+ }
+
/* done */
state->split_start = 0;
break;
diff --git a/sound/core/control.c b/sound/core/control.c
index a85d45595d02..b4fe9b002512 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -160,6 +160,8 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask,
if (snd_BUG_ON(!card || !id))
return;
+ if (card->shutdown)
+ return;
read_lock(&card->ctl_files_rwlock);
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
card->mixer_oss_change_count++;
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 019751a83e25..9e4743e833be 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -849,6 +849,22 @@ int snd_pcm_new_internal(struct snd_card *card, const char *id, int device,
}
EXPORT_SYMBOL(snd_pcm_new_internal);
+static void free_pcm_kctl(struct snd_pcm_str *pstr)
+{
+ if (pstr->chmap_kctl) {
+ snd_ctl_remove(pstr->pcm->card, pstr->chmap_kctl);
+ pstr->chmap_kctl = NULL;
+ }
+ if (pstr->vol_kctl) {
+ snd_ctl_remove(pstr->pcm->card, pstr->vol_kctl);
+ pstr->vol_kctl = NULL;
+ }
+ if (pstr->usr_kctl) {
+ snd_ctl_remove(pstr->pcm->card, pstr->usr_kctl);
+ pstr->usr_kctl = NULL;
+ }
+}
+
static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
{
struct snd_pcm_substream *substream, *substream_next;
@@ -871,6 +887,7 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
kfree(setup);
}
#endif
+ free_pcm_kctl(pstr);
if (pstr->substream_count)
put_device(&pstr->dev);
}
@@ -1135,18 +1152,7 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
for (cidx = 0; cidx < 2; cidx++) {
if (!pcm->internal)
snd_unregister_device(&pcm->streams[cidx].dev);
- if (pcm->streams[cidx].chmap_kctl) {
- snd_ctl_remove(pcm->card, pcm->streams[cidx].chmap_kctl);
- pcm->streams[cidx].chmap_kctl = NULL;
- }
- if (pcm->streams[cidx].vol_kctl) {
- snd_ctl_remove(pcm->card, pcm->streams[cidx].vol_kctl);
- pcm->streams[cidx].vol_kctl = NULL;
- }
- if (pcm->streams[cidx].usr_kctl) {
- snd_ctl_remove(pcm->card, pcm->streams[cidx].usr_kctl);
- pcm->streams[cidx].usr_kctl = NULL;
- }
+ free_pcm_kctl(&pcm->streams[cidx]);
}
mutex_unlock(&pcm->open_mutex);
mutex_unlock(&register_mutex);
diff --git a/sound/soc/codecs/audio-ext-clk.c b/sound/soc/codecs/audio-ext-clk.c
index 7faabcfb1db1..c422267dbf2c 100755
--- a/sound/soc/codecs/audio-ext-clk.c
+++ b/sound/soc/codecs/audio-ext-clk.c
@@ -91,6 +91,10 @@ static int audio_ext_clk2_prepare(struct clk *clk)
struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info;
int ret;
+
+ if (!pnctrl_info->pinctrl || !pnctrl_info->active)
+ return 0;
+
ret = pinctrl_select_state(pnctrl_info->pinctrl,
pnctrl_info->active);
if (ret) {
@@ -115,6 +119,9 @@ static void audio_ext_clk2_unprepare(struct clk *clk)
struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info;
int ret;
+ if (!pnctrl_info->pinctrl || !pnctrl_info->sleep)
+ return;
+
ret = pinctrl_select_state(pnctrl_info->pinctrl,
pnctrl_info->sleep);
if (ret)
diff --git a/sound/soc/codecs/wcd_cpe_core.c b/sound/soc/codecs/wcd_cpe_core.c
index b1e105b3153a..0b4bd3c15127 100644
--- a/sound/soc/codecs/wcd_cpe_core.c
+++ b/sound/soc/codecs/wcd_cpe_core.c
@@ -35,9 +35,10 @@
#include "wcd_cmi_api.h"
#define CMI_CMD_TIMEOUT (10 * HZ)
-#define WCD_CPE_LSM_MAX_SESSIONS 1
+#define WCD_CPE_LSM_MAX_SESSIONS 2
#define WCD_CPE_AFE_MAX_PORTS 4
#define AFE_SVC_EXPLICIT_PORT_START 1
+#define WCD_CPE_EC_PP_BUF_SIZE 480 /* 5 msec buffer */
#define ELF_FLAG_EXECUTE (1 << 0)
#define ELF_FLAG_WRITE (1 << 1)
@@ -1638,7 +1639,8 @@ static int wcd_cpe_vote(struct wcd_cpe_core *core,
core->cpe_users);
if (enable) {
- if (core->cpe_users == 0) {
+ core->cpe_users++;
+ if (core->cpe_users == 1) {
ret = wcd_cpe_enable(core, enable);
if (ret) {
dev_err(core->dev,
@@ -1646,7 +1648,6 @@ static int wcd_cpe_vote(struct wcd_cpe_core *core,
__func__, ret);
goto done;
}
- core->cpe_users++;
} else {
dev_dbg(core->dev,
"%s: cpe already enabled, users = %u\n",
@@ -1654,7 +1655,8 @@ static int wcd_cpe_vote(struct wcd_cpe_core *core,
goto done;
}
} else {
- if (core->cpe_users == 1) {
+ core->cpe_users--;
+ if (core->cpe_users == 0) {
ret = wcd_cpe_enable(core, enable);
if (ret) {
dev_err(core->dev,
@@ -1662,7 +1664,6 @@ static int wcd_cpe_vote(struct wcd_cpe_core *core,
__func__, ret);
goto done;
}
- core->cpe_users--;
} else {
dev_dbg(core->dev,
"%s: %u valid users on cpe\n",
@@ -3335,7 +3336,6 @@ static int wcd_cpe_cmd_lsm_start(void *core_handle,
{
struct cmi_hdr cmd_lsm_start;
struct wcd_cpe_core *core = core_handle;
- struct cpe_lsm_ids ids;
int ret = 0;
ret = wcd_cpe_is_valid_lsm_session(core, session,
@@ -3343,30 +3343,6 @@ static int wcd_cpe_cmd_lsm_start(void *core_handle,
if (ret)
return ret;
- /* Send connect to port (input) */
- ids.module_id = CPE_LSM_MODULE_ID_VOICE_WAKEUP;
- ids.param_id = CPE_LSM_PARAM_ID_CONNECT_TO_PORT;
- ret = wcd_cpe_send_param_connectport(core, session,
- NULL, &ids, CPE_AFE_PORT_1_TX);
- if (ret) {
- dev_err(core->dev,
- "%s: Failed to set connectPort, err=%d\n",
- __func__, ret);
- return ret;
- }
-
- /* Send connect to port (output) */
- ids.module_id = CPE_LSM_MODULE_FRAMEWORK;
- ids.param_id = CPE_LSM_PARAM_ID_CONNECT_TO_PORT;
- ret = wcd_cpe_send_param_connectport(core, session,
- NULL, &ids, session->afe_out_port_id);
- if (ret) {
- dev_err(core->dev,
- "%s: Failed to set connectPort, err=%d\n",
- __func__, ret);
- return ret;
- }
-
WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
memset(&cmd_lsm_start, 0, sizeof(struct cmi_hdr));
@@ -3449,9 +3425,12 @@ static struct cpe_lsm_session *wcd_cpe_alloc_lsm_session(
if (!wcd_cpe_lsm_session_active())
afe_register_service = true;
- for (i = 1; i <= WCD_CPE_LSM_MAX_SESSIONS; i++)
- if (!lsm_sessions[i])
+ for (i = 1; i <= WCD_CPE_LSM_MAX_SESSIONS; i++) {
+ if (!lsm_sessions[i]) {
session_id = i;
+ break;
+ }
+ }
if (session_id < 0) {
dev_err(core->dev,
@@ -3875,6 +3854,83 @@ static void wcd_cpe_snd_model_offset(void *core_handle,
*offset = sizeof(struct cpe_param_data);
}
+static int wcd_cpe_lsm_set_media_fmt_params(void *core_handle,
+ struct cpe_lsm_session *session,
+ struct lsm_hw_params *param)
+{
+ struct cpe_lsm_media_fmt_param media_fmt;
+ struct cmi_hdr *msg_hdr = &media_fmt.hdr;
+ struct wcd_cpe_core *core = core_handle;
+ struct cpe_param_data *param_d = &media_fmt.param;
+ struct cpe_lsm_ids ids;
+ int ret;
+
+ memset(&media_fmt, 0, sizeof(media_fmt));
+ if (fill_lsm_cmd_header_v0_inband(msg_hdr,
+ session->id,
+ CPE_MEDIA_FMT_PLD_SIZE,
+ CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ memset(&ids, 0, sizeof(ids));
+ ids.module_id = CPE_LSM_MODULE_FRAMEWORK;
+ ids.param_id = CPE_LSM_PARAM_ID_MEDIA_FMT;
+
+ wcd_cpe_set_param_data(param_d, &ids, CPE_MEDIA_FMT_PARAM_SIZE,
+ CPE_LSM_SESSION_CMD_SET_PARAMS_V2);
+
+ media_fmt.minor_version = 1;
+ media_fmt.sample_rate = param->sample_rate;
+ media_fmt.num_channels = param->num_chs;
+ media_fmt.bit_width = param->bit_width;
+
+ WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+ ret = wcd_cpe_cmi_send_lsm_msg(core, session, &media_fmt);
+ if (ret)
+ dev_err(core->dev,
+ "%s: Set_param(media_format) failed, err=%d\n",
+ __func__, ret);
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+done:
+ return ret;
+}
+
+static int wcd_cpe_lsm_set_port(void *core_handle,
+ struct cpe_lsm_session *session, void *data)
+{
+ u32 port_id;
+ int ret;
+ struct cpe_lsm_ids ids;
+ struct wcd_cpe_core *core = core_handle;
+
+ ret = wcd_cpe_is_valid_lsm_session(core, session, __func__);
+ if (ret)
+ goto done;
+
+ if (!data) {
+ dev_err(core->dev, "%s: data is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ port_id = *(u32 *)data;
+ dev_dbg(core->dev, "%s: port_id: %d\n", __func__, port_id);
+
+ memset(&ids, 0, sizeof(ids));
+ ids.module_id = LSM_MODULE_ID_FRAMEWORK;
+ ids.param_id = LSM_PARAM_ID_CONNECT_TO_PORT;
+
+ ret = wcd_cpe_send_param_connectport(core, session, NULL,
+ &ids, port_id);
+ if (ret)
+ dev_err(core->dev,
+ "%s: send_param_connectport failed, err %d\n",
+ __func__, ret);
+done:
+ return ret;
+}
+
/*
* wcd_cpe_get_lsm_ops: register lsm driver to codec
* @lsm_ops: structure with lsm callbacks
@@ -3899,6 +3955,9 @@ int wcd_cpe_get_lsm_ops(struct wcd_cpe_lsm_ops *lsm_ops)
lsm_ops->lsm_set_fmt_cfg = wcd_cpe_lsm_set_fmt_cfg;
lsm_ops->lsm_set_one_param = wcd_cpe_set_one_param;
lsm_ops->lsm_get_snd_model_offset = wcd_cpe_snd_model_offset;
+ lsm_ops->lsm_set_media_fmt_params = wcd_cpe_lsm_set_media_fmt_params;
+ lsm_ops->lsm_set_port = wcd_cpe_lsm_set_port;
+
return 0;
}
EXPORT_SYMBOL(wcd_cpe_get_lsm_ops);
@@ -4129,10 +4188,10 @@ static int wcd_cpe_send_afe_cal(void *core_handle,
goto rel_cal_mutex;
}
- rc = fill_cmi_header(hdr, port_d->port_id,
- CMI_CPE_AFE_SERVICE_ID,
- 0, 20, CPE_AFE_CMD_SET_PARAM,
- true);
+ rc = fill_afe_cmd_header(hdr, port_d->port_id,
+ CPE_AFE_CMD_SET_PARAM,
+ CPE_AFE_PARAM_PAYLOAD_SIZE,
+ true);
if (rc) {
dev_err(core->dev,
"%s: invalid params for header, err = %d\n",
@@ -4163,10 +4222,10 @@ static int wcd_cpe_send_afe_cal(void *core_handle,
hdr = (struct cmi_hdr *) inb_msg;
- rc = fill_cmi_header(hdr, port_d->port_id,
- CMI_CPE_AFE_SERVICE_ID,
- 0, afe_cal->cal_data.size,
- CPE_AFE_CMD_SET_PARAM, false);
+ rc = fill_afe_cmd_header(hdr, port_d->port_id,
+ CPE_AFE_CMD_SET_PARAM,
+ CPE_AFE_PARAM_PAYLOAD_SIZE,
+ false);
if (rc) {
dev_err(core->dev,
"%s: invalid params for header, err = %d\n",
@@ -4299,8 +4358,12 @@ static int wcd_cpe_afe_cmd_port_cfg(void *core_handle,
port_cfg_cmd.bit_width = afe_cfg->bit_width;
port_cfg_cmd.num_channels = afe_cfg->num_channels;
port_cfg_cmd.sample_rate = afe_cfg->sample_rate;
- port_cfg_cmd.buffer_size = AFE_OUT_BUF_SIZE(afe_cfg->bit_width,
- afe_cfg->sample_rate);
+
+ if (afe_port_d->port_id == CPE_AFE_PORT_3_TX)
+ port_cfg_cmd.buffer_size = WCD_CPE_EC_PP_BUF_SIZE;
+ else
+ port_cfg_cmd.buffer_size = AFE_OUT_BUF_SIZE(afe_cfg->bit_width,
+ afe_cfg->sample_rate);
ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &port_cfg_cmd);
if (ret)
diff --git a/sound/soc/msm/qdsp6v2/audio_calibration.c b/sound/soc/msm/qdsp6v2/audio_calibration.c
index c4ea4ed857ca..60d09dfaeb7f 100644
--- a/sound/soc/msm/qdsp6v2/audio_calibration.c
+++ b/sound/soc/msm/qdsp6v2/audio_calibration.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014, 2016 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
@@ -490,7 +490,13 @@ static long audio_cal_shared_ioctl(struct file *file, unsigned int cmd,
goto unlock;
if (data == NULL)
goto unlock;
- if (copy_to_user((void *)arg, data,
+ if ((sizeof(data->hdr) + data->hdr.cal_type_size) > size) {
+ pr_err("%s: header size %zd plus cal type size %d are greater than data buffer size %d\n",
+ __func__, sizeof(data->hdr),
+ data->hdr.cal_type_size, size);
+ ret = -EFAULT;
+ goto unlock;
+ } else if (copy_to_user((void *)arg, data,
sizeof(data->hdr) + data->hdr.cal_type_size)) {
pr_err("%s: Could not copy cal type to user\n",
__func__);
diff --git a/sound/usb/card.c b/sound/usb/card.c
index e94f4d2f2620..524688e4c144 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -643,6 +643,7 @@ static int usb_audio_probe(struct usb_interface *intf,
usb_chip[chip->index] = chip;
chip->num_interfaces++;
usb_set_intfdata(intf, chip);
+ usb_enable_autosuspend(chip->dev);
atomic_dec(&chip->active);
mutex_unlock(&register_mutex);
return 0;