summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/arm/coresight.txt3
-rw-r--r--Documentation/devicetree/bindings/fb/mdss-dp.txt119
-rw-r--r--Documentation/devicetree/bindings/fb/mdss-edp.txt52
-rw-r--r--Documentation/devicetree/bindings/gpu/adreno.txt5
-rw-r--r--Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt179
-rw-r--r--Documentation/devicetree/bindings/usb/msm-phy.txt27
-rw-r--r--Documentation/devicetree/bindings/usb/msm-ssusb.txt21
-rw-r--r--arch/arm/boot/dts/qcom/msm-gdsc-cobalt.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi35
-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.dtsi13
-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-pinctrl.dtsi78
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt.dtsi13
-rw-r--r--arch/arm/boot/dts/qcom/msmfalcon.dtsi126
-rw-r--r--arch/arm64/configs/msm-perf_defconfig1
-rw-r--r--arch/arm64/configs/msmcortex-perf_defconfig20
-rw-r--r--arch/arm64/configs/msmcortex_defconfig2
-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/input/touchscreen/it7258_ts_i2c.c343
-rw-r--r--drivers/iommu/iommu-debug.c63
-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/media/platform/msm/vidc/hfi_packetization.c8
-rw-r--r--drivers/media/platform/msm/vidc/msm_venc.c29
-rw-r--r--drivers/media/platform/msm/vidc/vidc_hfi_api.h1
-rw-r--r--drivers/media/platform/msm/vidc/vidc_hfi_helper.h2
-rw-r--r--drivers/misc/uid_stat.c105
-rw-r--r--drivers/platform/msm/ipa/ipa_clients/ipa_usb.c95
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa.c26
-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/Kconfig10
-rw-r--r--drivers/power/qcom-charger/Makefile1
-rw-r--r--drivers/power/qcom-charger/fg-core.h247
-rw-r--r--drivers/power/qcom-charger/fg-memif.c583
-rw-r--r--drivers/power/qcom-charger/fg-reg.h299
-rw-r--r--drivers/power/qcom-charger/fg-util.c644
-rw-r--r--drivers/power/qcom-charger/qpnp-fg-gen3.c1495
-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/icnss.c14
-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.c49
-rw-r--r--drivers/usb/dwc3/gadget.c2
-rw-r--r--drivers/usb/gadget/function/f_gsi.c22
-rw-r--r--drivers/usb/pd/policy_engine.c3
-rw-r--r--drivers/usb/phy/phy-msm-qusb-v2.c92
-rw-r--r--drivers/usb/phy/phy-msm-ssusb-qmp.c17
-rw-r--r--drivers/video/fbdev/msm/Kconfig8
-rw-r--r--drivers/video/fbdev/msm/Makefile4
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.c1148
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.h (renamed from drivers/video/fbdev/msm/mdss_edp.h)163
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_aux.c (renamed from drivers/video/fbdev/msm/mdss_edp_aux.c)433
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.c284
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.h111
-rw-r--r--drivers/video/fbdev/msm/mdss_edp.c1273
-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--drivers/video/fbdev/msm/msm_mdss_io_8974.c313
-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/ipa_usb.h13
-rw-r--r--include/linux/qpnp/qpnp-adc.h3
-rw-r--r--include/linux/usb/msm_hsusb.h2
-rw-r--r--include/net/xfrm.h5
-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/v4l2-controls.h7
-rw-r--r--include/uapi/media/msmb_isp.h1
-rw-r--r--net/activity_stats.c39
-rw-r--r--net/ipv4/xfrm4_policy.c11
-rw-r--r--net/ipv6/ip6_output.c3
-rw-r--r--net/ipv6/route.c3
-rw-r--r--net/ipv6/xfrm6_policy.c8
-rw-r--r--net/xfrm/xfrm_policy.c23
-rw-r--r--sound/core/control.c2
-rw-r--r--sound/core/pcm.c30
-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
131 files changed, 14556 insertions, 2952 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
new file mode 100644
index 000000000000..0b65b776645b
--- /dev/null
+++ b/Documentation/devicetree/bindings/fb/mdss-dp.txt
@@ -0,0 +1,119 @@
+QTI MDSS DP
+
+MDSS DP is a display-port driver which supports panels that are compatible with
+VESA DP and EDP display interface specification.
+
+When configuring the optional properties for external backlight, one should also
+configure the gpio that drives the pwm to it.
+
+Required properties
+- compatible : Must be "qcom,mdss-edp".
+- reg : Offset and length of the register set for the
+ device.
+- reg-names : Names to refer to register sets related to this
+ device
+- gdsc-supply : Phandle for gdsc regulator device node.
+- vdda-1p2-supply : Phandle for 1.2V vdda regulator device node.
+- vdda-0p9-supply : Phandle for 0.9V vdda regulator device node.
+- status : A string that has to be set to "okay/ok" to enable
+ the driver. By default this property will be set to
+ "disable". Will be set to "ok/okay" status for
+ specific platforms.
+- qcom,mdss-fb-map: pHandle that specifies the framebuffer to which the
+ interface is mapped.
+- clocks: List of Phandles for clock device nodes
+ needed by the device.
+- clock-names: List of clock names needed by the device.
+
+Optional properties:
+- qcom,<type>-supply-entries: A node that lists the elements of the supply used by the
+ a particular "type" of DSI modulee. The module "types"
+ can be "core", "ctrl", and "phy". Within the same type,
+ there can be more than one instance of this binding,
+ in which case the entry would be appended with the
+ supply entry index.
+ e.g. qcom,ctrl-supply-entry@0
+ -- qcom,supply-name: name of the supply (vdd/vdda/vddio)
+ -- qcom,supply-min-voltage: minimum voltage level (uV)
+ -- qcom,supply-max-voltage: maximum voltage level (uV)
+ -- qcom,supply-enable-load: load drawn (uA) from enabled supply
+ -- qcom,supply-disable-load: load drawn (uA) from disabled supply
+ -- qcom,supply-pre-on-sleep: time to sleep (ms) before turning on
+ -- 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
+
+Example:
+ mdss_dp_ctrl: qcom,dp_ctrl@c990000 {
+ cell-index = <0>;
+ compatible = "qcom,mdss-dp";
+ qcom,mdss-fb-map = <&mdss_fb3>;
+
+ gdsc-supply = <&gdsc_mdss>;
+ vdda-1p2-supply = <&pmcobalt_l2>;
+ vdda-0p9-supply = <&pmcobalt_l1>;
+
+ reg = <0xc990000 0xa84>,
+ <0xc011000 0x910>,
+ <0x1fcb200 0x050>;
+ reg-names = "dp_ctrl", "dp_phy", "tcsr_regs";
+
+ clocks = <&clock_mmss clk_mmss_mnoc_ahb_clk>,
+ <&clock_mmss clk_mmss_mdss_ahb_clk>,
+ <&clock_mmss clk_mmss_mdss_axi_clk>,
+ <&clock_mmss clk_mmss_mdss_mdp_clk>,
+ <&clock_mmss clk_mmss_mdss_hdmi_dp_ahb_clk>,
+ <&clock_mmss clk_mmss_mdss_dp_aux_clk>,
+ <&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>,
+ <&clock_mmss clk_mmss_mdss_dp_link_clk>,
+ <&clock_mmss clk_mmss_mdss_dp_link_intf_clk>,
+ <&clock_mmss clk_mmss_mdss_dp_crypto_clk>,
+ <&clock_mmss clk_mmss_mdss_dp_pixel_clk>;
+ clock-names = "core_mnoc_clk", "core_iface_clk", "core_bus_clk",
+ "core_mdp_core_clk", "core_alt_iface_clk",
+ "core_aux_clk", "core_cfg_ahb_clk", "ctrl_link_clk",
+ "ctrl_link_iface_clk", "ctrl_crypto_clk", "ctrl_pixel_clk";
+
+ qcom,core-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,core-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "gdsc";
+ qcom,supply-min-voltage = <0>;
+ qcom,supply-max-voltage = <0>;
+ qcom,supply-enable-load = <0>;
+ qcom,supply-disable-load = <0>;
+ };
+ };
+
+ qcom,ctrl-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,ctrl-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "vdda-1p2";
+ qcom,supply-min-voltage = <1200000>;
+ qcom,supply-max-voltage = <1200000>;
+ qcom,supply-enable-load = <12560>;
+ qcom,supply-disable-load = <4>;
+ };
+ };
+
+ qcom,phy-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,phy-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "vdda-0p9";
+ qcom,supply-min-voltage = <880000>;
+ qcom,supply-max-voltage = <880000>;
+ qcom,supply-enable-load = <73400>;
+ qcom,supply-disable-load = <32>;
+ };
+ };
+ };
+
diff --git a/Documentation/devicetree/bindings/fb/mdss-edp.txt b/Documentation/devicetree/bindings/fb/mdss-edp.txt
deleted file mode 100644
index 882047a43fc9..000000000000
--- a/Documentation/devicetree/bindings/fb/mdss-edp.txt
+++ /dev/null
@@ -1,52 +0,0 @@
-Qualcomm MDSS EDP
-
-MDSS EDP is a edp driver which supports panels that are compatable with
-VESA EDP display interface specification.
-
-When configuring the optional properties for external backlight, one should also
-configure the gpio that drives the pwm to it.
-
-Required properties
-- compatible : Must be "qcom,mdss-edp".
-- reg : Offset and length of the register set for the
- device.
-- reg-names : Names to refer to register sets related to this
- device
-- vdda-supply : Phandle for vdd regulator device node.
-- gpio-panel-en : GPIO for supplying power to panel and backlight
- driver.
-- gpio-lvl-en : GPIO to enable HPD be received by host.
-- status : A string that has to be set to "okay/ok" to enable
- the driver. By default this property will be set to
- "disable". Will be set to "ok/okay" status for
- specific platforms.
-- qcom,mdss-fb-map: pHandle that specifies the framebuffer to which the
- interface is mapped.
-- gpio-panel-hpd : gpio pin use for edp hpd
-
-Optional properties
-- qcom,panel-lpg-channel : LPG channel for backlight.
-- qcom,panel-pwm-period : PWM period in microseconds.
-
-
-Optional properties:
-- qcom,mdss-brightness-max-level: Specifies the max brightness level supported.
- 255 = default value.
-
-Example:
- mdss_edp: qcom,mdss_edp@fd923400 {
- compatible = "qcom,mdss-edp";
- reg = <0xfd923400 0x700>,
- <0xfd8c2000 0x1000>;
- reg-names = "edp_base", "mmss_cc_base";
- vdda-supply = <&pm8941_l12>;
- gpio-panel-en = <&msmgpio 58 0>;
- gpio-lvl-en = <&msmgpio 91 0>;
- qcom,panel-lpg-channel = <7>; /* LPG Channel 8 */
- qcom,panel-pwm-period = <53>;
- status = "disable";
- qcom,mdss-fb-map = <&mdss_fb0>;
- gpio-panel-hpd = <&msmgpio 102 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/power/qcom-charger/qpnp-fg-gen3.txt b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt
new file mode 100644
index 000000000000..9bf6d5b2bf8e
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt
@@ -0,0 +1,179 @@
+Qualcomm Techonologies, Inc. QPNP PMIC Fuel Gauge Gen3 Device
+
+QPNP PMIC FG Gen3 device provides interface to the clients to read properties
+related to the battery. Its main function is to retrieve the State of Charge
+(SOC), in percentage scale representing the amount of charge left in the
+battery.
+
+=======================
+Required Node Structure
+=======================
+
+FG Gen3 device must be described in two levels of device nodes. The first
+level describes the FG Gen3 device. The second level describes one or more
+peripherals managed by FG Gen3 driver. All the peripheral specific parameters
+such as base address, interrupts etc., should be under second level node.
+
+====================================
+First Level Node - FG Gen3 device
+====================================
+
+- compatible
+ Usage: required
+ Value type: <string>
+ Definition: Should be "qcom,fg-gen3".
+
+- qcom,pmic-revid
+ Usage: required
+ Value type: <phandle>
+ Definition: Should specify the phandle of PMIC revid module. This is
+ used to identify the PMIC subtype.
+
+- io-channels
+- io-channel-names
+ Usage: required
+ Value type: <phandle>
+ Definition: For details about IIO bindings see:
+ Documentation/devicetree/bindings/iio/iio-bindings.txt
+
+- qcom,fg-cutoff-voltage
+ Usage: optional
+ Value type: <u32>
+ Definition: The voltage (in mV) where the fuel gauge will steer the SOC
+ to be zero. For example, if the cutoff voltage is set to
+ 3400mv, the fuel gauge will try to count SoC so that the
+ battery SOC will be 0 when it is 3400mV. If this property
+ is not specified, then the default value used will be
+ 3200mV.
+
+- qcom,fg-empty-voltage
+ Usage: optional
+ Value type: <u32>
+ Definition: The voltage threshold (in mV) based on which the empty soc
+ interrupt will be triggered. When the empty soc interrupt
+ fires, battery soc will be set to 0 and the userspace will
+ be notified via the power supply framework. The userspace
+ will read 0% soc and immediately shutdown. If this property
+ is not specified, then the default value used will be
+ 3100mV.
+
+- qcom,fg-vbatt-low-thr
+ Usage: optional
+ Value type: <u32>
+ Definition: The voltage threshold (in mV) which upon set will be used
+ for configuring the low battery voltage threshold.
+
+- qcom,fg-chg-term-current
+ Usage: optional
+ Value type: <u32>
+ Definition: Battery current (in mA) at which the fuel gauge will issue
+ an end of charge if the charger is configured to use the
+ fuel gauge ADC for end of charge detection. If this
+ property is not specified, then the default value used
+ will be 100mA.
+
+- qcom,fg-sys-term-current
+ Usage: optional
+ Value type: <u32>
+ Definition: Battery current (in mA) at which the fuel gauge will try to
+ scale towards 100%. When the charge current goes above this
+ the SOC should be at 100%. If this property is not
+ specified, then the default value used will be 125mA.
+
+- qcom,fg-delta-soc-thr
+ Usage: optional
+ Value type: <u32>
+ Definition: Percentage of monotonic SOC increase upon which the delta
+ SOC interrupt will be triggered. If this property is not
+ specified, then the default value will be 1.
+
+- qcom,fg-recharge-soc-thr
+ Usage: optional
+ Value type: <u32>
+ Definition: Percentage of monotonic SOC upon which the charging will
+ will be resumed once the charging is complete. If this
+ property is not specified, then the default value will be
+ 95.
+
+- qcom,fg-rsense-sel
+ Usage: optional
+ Value type: <u32>
+ Definition: Specifies the source of sense resistor.
+ Allowed values are:
+ 0 - Rsense is from Battery FET
+ 1 - Rsense is external
+ 2 - Rsense is Battery FET and SMB
+ Option 2 can be used only when a parallel charger is
+ present. If this property is not specified, then the
+ default value will be 2.
+
+- qcom,fg-jeita-thresholds
+ Usage: optional
+ Value type: <prop-encoded-array>
+ Definition: A list of integers which holds the jeita thresholds (degC)
+ in the following order. Allowed size is 4.
+ Element 0 - JEITA cold threshold
+ Element 1 - JEITA cool threshold
+ Element 2 - JEITA warm threshold
+ Element 3 - JEITA hot threshold
+ If these parameters are not specified, then the default
+ values used will be 0, 5, 45, 50.
+
+==========================================================
+Second Level Nodes - Peripherals managed by FG Gen3 driver
+==========================================================
+- reg
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Addresses and sizes for the specified peripheral
+
+- interrupts
+ Usage: optional
+ Value type: <prop-encoded-array>
+ Definition: Interrupt mapping as per the interrupt encoding
+
+- interrupt-names
+ Usage: optional
+ Value type: <stringlist>
+ Definition: Interrupt names. This list must match up 1-to-1 with the
+ interrupts specified in the 'interrupts' property.
+
+========
+Example
+========
+
+pmicobalt_fg: qpnp,fg {
+ compatible = "qcom,fg-gen3";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ qcom,pmic-revid = <&pmicobalt_revid>;
+ io-channels = <&pmicobalt_rradc 3>;
+ io-channel-names = "rradc_batt_id";
+ status = "okay";
+
+ qcom,fg-batt-soc@4000 {
+ status = "okay";
+ reg = <0x4000 0x100>;
+ interrupts = <0x2 0x40 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x40 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x40 0x2 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x40 0x3 IRQ_TYPE_EDGE_BOTH>;
+ interrupt-names = "soc-update",
+ "soc-ready",
+ "bsoc-delta",
+ "msoc-delta";
+
+ };
+
+ qcom,fg-batt-info@4100 {
+ status = "okay";
+ reg = <0x4100 0x100>;
+ interrupts = <0x2 0x41 0x3 IRQ_TYPE_EDGE_BOTH>;
+ interrupt-names = "batt-missing";
+ };
+
+ qcom,fg-memif@4400 {
+ status = "okay";
+ reg = <0x4400 0x100>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/usb/msm-phy.txt b/Documentation/devicetree/bindings/usb/msm-phy.txt
index 35bb94b2ef70..2f82fbfda14f 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,7 +176,7 @@ 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:
@@ -171,7 +190,13 @@ Optional properties:
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.
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/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/msm-pmicobalt.dtsi b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi
index fad834199be5..47ba50ab8270 100644
--- a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi
@@ -339,6 +339,41 @@
channel = <0xc>;
};
};
+
+ pmicobalt_fg: qpnp,fg {
+ compatible = "qcom,fg-gen3";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ qcom,pmic-revid = <&pmicobalt_revid>;
+ io-channels = <&pmicobalt_rradc 0>;
+ io-channel-names = "rradc_batt_id";
+ status = "okay";
+
+ qcom,fg-batt-soc@4000 {
+ status = "okay";
+ reg = <0x4000 0x100>;
+ interrupts = <0x2 0x40 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x40 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x40 0x2 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x40 0x3 IRQ_TYPE_EDGE_BOTH>;
+ interrupt-names = "soc-update",
+ "soc-ready",
+ "bsoc-delta",
+ "msoc-delta";
+ };
+
+ qcom,fg-batt-info@4100 {
+ status = "okay";
+ reg = <0x4100 0x100>;
+ interrupts = <0x2 0x41 0x3 IRQ_TYPE_EDGE_BOTH>;
+ interrupt-names = "batt-missing";
+ };
+
+ qcom,fg-memif@4400 {
+ status = "okay";
+ reg = <0x4400 0x100>;
+ };
+ };
};
qcom,pmicobalt@3 {
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..7a26b9f0b227 100644
--- a/arch/arm/boot/dts/qcom/msm8996.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996.dtsi
@@ -3004,13 +3004,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 +3123,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-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi
index 1094d96bd100..6db4ed1f45e1 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi
@@ -1425,6 +1425,84 @@
};
};
+ mdss_dp_aux_active: mdss_dp_aux_active {
+ mux {
+ pins = "gpio77", "gpio78";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio77", "gpio78";
+ bias-disable = <0>; /* no pull */
+ drive-strength = <8>;
+ };
+ };
+
+ mdss_dp_aux_suspend: mdss_dp_aux_suspend {
+ mux {
+ pins = "gpio77", "gpio78";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio77", "gpio78";
+ bias-pull-down;
+ drive-strength = <2>;
+ };
+ };
+
+ mdss_dp_usbplug_cc_active: mdss_dp_usbplug_cc_active {
+ mux {
+ pins = "gpio38";
+ function = "usb_phy";
+ };
+
+ config {
+ pins = "gpio38";
+ bias-disable;
+ drive-strength = <16>;
+ };
+ };
+
+ mdss_dp_usbplug_cc_suspend: mdss_dp_usbplug_cc_suspend {
+ mux {
+ pins = "gpio38";
+ function = "usb_phy";
+ };
+
+ config {
+ pins = "gpio38";
+ bias-pull-down;
+ drive-strength = <2>;
+ };
+ };
+
+ mdss_dp_hpd_active: mdss_dp_hpd_active {
+ mux {
+ pins = "gpio34";
+ function = "edp_hot";
+ };
+
+ config {
+ pins = "gpio34";
+ bias-pull-down;
+ drive-strength = <16>;
+ };
+ };
+
+ mdss_dp_hpd_suspend: mdss_dp_hpd_suspend {
+ mux {
+ pins = "gpio34";
+ function = "edp_hot";
+ };
+
+ config {
+ pins = "gpio34";
+ bias-pull-down;
+ drive-strength = <2>;
+ };
+ };
+
blsp2_uart3_active: blsp2_uart3_active {
mux {
pins = "gpio49", "gpio50", "gpio51", "gpio52";
diff --git a/arch/arm/boot/dts/qcom/msmcobalt.dtsi b/arch/arm/boot/dts/qcom/msmcobalt.dtsi
index 5dc530ea8494..f02abd2173ef 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,7 +1778,7 @@
};
qusb_phy0: qusb@c012000 {
- compatible = "qcom,qusb2phy";
+ compatible = "qcom,qusb2phy-v2";
reg = <0x0c012000 0x2a8>,
<0x0a8f8800 0x400>;
reg-names = "qusb_phy_base",
@@ -1798,11 +1798,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 +1948,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/arm/boot/dts/qcom/msmfalcon.dtsi b/arch/arm/boot/dts/qcom/msmfalcon.dtsi
index ea60ed90cf4f..9ccf55322de1 100644
--- a/arch/arm/boot/dts/qcom/msmfalcon.dtsi
+++ b/arch/arm/boot/dts/qcom/msmfalcon.dtsi
@@ -331,6 +331,58 @@
qcom,mpu-enabled;
};
+ qcom,glink-smem-native-xprt-modem@86000000 {
+ compatible = "qcom,glink-smem-native-xprt";
+ reg = <0x86000000 0x200000>,
+ <0x17911008 0x4>;
+ reg-names = "smem", "irq-reg-base";
+ qcom,irq-mask = <0x8000>;
+ interrupts = <0 452 1>;
+ label = "mpss";
+ };
+
+ qcom,glink-smem-native-xprt-adsp@86000000 {
+ compatible = "qcom,glink-smem-native-xprt";
+ reg = <0x86000000 0x200000>,
+ <0x17911008 0x4>;
+ reg-names = "smem", "irq-reg-base";
+ qcom,irq-mask = <0x200>;
+ interrupts = <0 157 1>;
+ label = "lpass";
+ qcom,qos-config = <&glink_qos_adsp>;
+ qcom,ramp-time = <0xaf>;
+ };
+
+ glink_qos_adsp: qcom,glink-qos-config-adsp {
+ compatible = "qcom,glink-qos-config";
+ qcom,flow-info = <0x3c 0x0>,
+ <0x3c 0x0>,
+ <0x3c 0x0>,
+ <0x3c 0x0>;
+ qcom,mtu-size = <0x800>;
+ qcom,tput-stats-cycle = <0xa>;
+ };
+
+ qcom,glink-smem-native-xprt-cdsp@86000000 {
+ compatible = "qcom,glink-smem-native-xprt";
+ reg = <0x86000000 0x200000>,
+ <0x17911008 0x4>;
+ reg-names = "smem", "irq-reg-base";
+ qcom,irq-mask = <0x20000000>;
+ interrupts = <0 513 1>;
+ label = "cdsp";
+ };
+
+ qcom,glink-smem-native-xprt-rpm@68000 {
+ compatible = "qcom,glink-rpm-native-xprt";
+ reg = <0x778000 0x7000>,
+ <0x17911008 0x4>;
+ reg-names = "msgram", "irq-reg-base";
+ qcom,irq-mask = <0x1>;
+ interrupts = <0 168 1>;
+ label = "rpm";
+ };
+
glink_mpss: qcom,glink-ssr-modem {
compatible = "qcom,glink_ssr";
label = "modem";
@@ -366,6 +418,80 @@
<&glink_rpm>;
qcom,xprt = "smem";
};
+
+ qcom,glink_pkt {
+ compatible = "qcom,glinkpkt";
+
+ qcom,glinkpkt-at-mdm0 {
+ qcom,glinkpkt-transport = "smem";
+ qcom,glinkpkt-edge = "mpss";
+ qcom,glinkpkt-ch-name = "DS";
+ qcom,glinkpkt-dev-name = "at_mdm0";
+ };
+
+ qcom,glinkpkt-loopback_cntl {
+ qcom,glinkpkt-transport = "lloop";
+ qcom,glinkpkt-edge = "local";
+ qcom,glinkpkt-ch-name = "LOCAL_LOOPBACK_CLNT";
+ qcom,glinkpkt-dev-name = "glink_pkt_loopback_ctrl";
+ };
+
+ qcom,glinkpkt-loopback_data {
+ qcom,glinkpkt-transport = "lloop";
+ qcom,glinkpkt-edge = "local";
+ qcom,glinkpkt-ch-name = "glink_pkt_lloop_CLNT";
+ qcom,glinkpkt-dev-name = "glink_pkt_loopback";
+ };
+
+ qcom,glinkpkt-apr-apps2 {
+ qcom,glinkpkt-transport = "smem";
+ qcom,glinkpkt-edge = "adsp";
+ qcom,glinkpkt-ch-name = "apr_apps2";
+ qcom,glinkpkt-dev-name = "apr_apps2";
+ };
+
+ qcom,glinkpkt-data40-cntl {
+ qcom,glinkpkt-transport = "smem";
+ qcom,glinkpkt-edge = "mpss";
+ qcom,glinkpkt-ch-name = "DATA40_CNTL";
+ qcom,glinkpkt-dev-name = "smdcntl8";
+ };
+ };
+
+ qcom,ipc_router {
+ compatible = "qcom,ipc_router";
+ qcom,node-id = <1>;
+ };
+
+ qcom,ipc_router_modem_xprt {
+ compatible = "qcom,ipc_router_glink_xprt";
+ qcom,ch-name = "IPCRTR";
+ qcom,xprt-remote = "mpss";
+ qcom,glink-xprt = "smem";
+ qcom,xprt-linkid = <1>;
+ qcom,xprt-version = <1>;
+ qcom,fragmented-data;
+ };
+
+ qcom,ipc_router_q6_xprt {
+ compatible = "qcom,ipc_router_glink_xprt";
+ qcom,ch-name = "IPCRTR";
+ qcom,xprt-remote = "lpass";
+ qcom,glink-xprt = "smem";
+ qcom,xprt-linkid = <1>;
+ qcom,xprt-version = <1>;
+ qcom,fragmented-data;
+ };
+
+ qcom,ipc_router_cdsp_xprt {
+ compatible = "qcom,ipc_router_glink_xprt";
+ qcom,ch-name = "IPCRTR";
+ qcom,xprt-remote = "cdsp";
+ qcom,glink-xprt = "smem";
+ qcom,xprt-linkid = <1>;
+ qcom,xprt-version = <1>;
+ qcom,fragmented-data;
+ };
};
#include "msmfalcon-ion.dtsi"
diff --git a/arch/arm64/configs/msm-perf_defconfig b/arch/arm64/configs/msm-perf_defconfig
index f396b0b7f4cc..fa6cdb3d055f 100644
--- a/arch/arm64/configs/msm-perf_defconfig
+++ b/arch/arm64/configs/msm-perf_defconfig
@@ -85,6 +85,7 @@ CONFIG_IP_MULTIPLE_TABLES=y
CONFIG_IP_ROUTE_VERBOSE=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
+CONFIG_INET_ESP=y
# CONFIG_INET_LRO is not set
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_IPV6_ROUTE_INFO=y
diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig
index be3e4ce1492a..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
@@ -379,6 +382,7 @@ CONFIG_FB_MSM=y
CONFIG_FB_MSM_MDSS=y
CONFIG_FB_MSM_MDSS_WRITEBACK=y
CONFIG_FB_MSM_MDSS_HDMI_PANEL=y
+CONFIG_FB_MSM_MDSS_DP_PANEL=y
CONFIG_LOGO=y
# CONFIG_LOGO_LINUX_MONO is not set
# CONFIG_LOGO_LINUX_VGA16 is not set
@@ -467,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
@@ -546,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 2e9a7908307b..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
@@ -384,6 +385,7 @@ CONFIG_FB_MSM=y
CONFIG_FB_MSM_MDSS=y
CONFIG_FB_MSM_MDSS_WRITEBACK=y
CONFIG_FB_MSM_MDSS_HDMI_PANEL=y
+CONFIG_FB_MSM_MDSS_DP_PANEL=y
CONFIG_LOGO=y
# CONFIG_LOGO_LINUX_MONO is not set
# CONFIG_LOGO_LINUX_VGA16 is not set
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..c9905a4a87df
--- /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;
+ else
+ 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) {
+ dev_err(&client->dev, "Not enough memory\n");
+ 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);
+
+ 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 __devexit 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/input/touchscreen/it7258_ts_i2c.c b/drivers/input/touchscreen/it7258_ts_i2c.c
index 048358e2ef9d..773ece9eb1d4 100644
--- a/drivers/input/touchscreen/it7258_ts_i2c.c
+++ b/drivers/input/touchscreen/it7258_ts_i2c.c
@@ -1,6 +1,7 @@
/* drivers/input/touchscreen/it7258_ts_i2c.c
*
* Copyright (C) 2014 ITE Tech. Inc.
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -28,31 +29,50 @@
#define SCREEN_X_RESOLUTION 320
#define SCREEN_Y_RESOLUTION 320
-#define BUF_COMMAND 0x20 /* all commands writes go to this idx */
+/* all commands writes go to this idx */
+#define BUF_COMMAND 0x20
#define BUF_SYS_COMMAND 0x40
-#define BUF_QUERY 0x80 /* "revice ready?" and "wake up please" and "read touch data" reads go to this idx */
-#define BUF_RESPONSE 0xA0 /* most command responce reads go to this idx */
+/*
+ * "device ready?" and "wake up please" and "read touch data" reads
+ * go to this idx
+ */
+#define BUF_QUERY 0x80
+/* most command response reads go to this idx */
+#define BUF_RESPONSE 0xA0
#define BUF_SYS_RESPONSE 0xC0
-#define BUF_POINT_INFO 0xE0 /* reads of "point" go through here and produce 14 bytes of data */
+/* reads of "point" go through here and produce 14 bytes of data */
+#define BUF_POINT_INFO 0xE0
-/* commands and their subcommands. when no subcommands exist, a zero is send as the second byte */
+/*
+ * commands and their subcommands. when no subcommands exist, a zero
+ * is send as the second byte
+ */
#define CMD_IDENT_CHIP 0x00
-#define CMD_READ_VERSIONS 0x01 /* VERSION_LENGTH bytes of data in response */
-# define VER_FIRMWARE 0x00
-# define VER_CONFIG 0x06
-# define VERSION_LENGTH 10
-#define CMD_PWR_CTL 0x04 /* subcommand is zero, next byte is power mode */
-# define PWR_CTL_LOW_POWER_MODE 0x01 /* idle mode */
-# define PWR_CTL_SLEEP_MODE 0x02 /* sleep mode */
-#define CMD_UNKNOWN_7 0x07 /* command is not documented in the datasheet v1.0.0.7 */
+/* VERSION_LENGTH bytes of data in response */
+#define CMD_READ_VERSIONS 0x01
+#define VER_FIRMWARE 0x00
+#define VER_CONFIG 0x06
+#define VERSION_LENGTH 10
+/* subcommand is zero, next byte is power mode */
+#define CMD_PWR_CTL 0x04
+/* idle mode */
+#define PWR_CTL_LOW_POWER_MODE 0x01
+/* sleep mode */
+#define PWR_CTL_SLEEP_MODE 0x02
+/* command is not documented in the datasheet v1.0.0.7 */
+#define CMD_UNKNOWN_7 0x07
#define CMD_FIRMWARE_REINIT_C 0x0C
-#define CMD_CALIBRATE 0x13 /* needs to be followed by 4 bytes of zeroes */
+/* needs to be followed by 4 bytes of zeroes */
+#define CMD_CALIBRATE 0x13
#define CMD_FIRMWARE_UPGRADE 0x60
-# define FIRMWARE_MODE_ENTER 0x00
-# define FIRMWARE_MODE_EXIT 0x80
-#define CMD_SET_START_OFFSET 0x61 /* address for FW read/write */
-#define CMD_FW_WRITE 0x62 /* subcommand is number of bytes to write */
-#define CMD_FW_READ 0x63 /* subcommand is number of bytes to read */
+#define FIRMWARE_MODE_ENTER 0x00
+#define FIRMWARE_MODE_EXIT 0x80
+/* address for FW read/write */
+#define CMD_SET_START_OFFSET 0x61
+/* subcommand is number of bytes to write */
+#define CMD_FW_WRITE 0x62
+/* subcommand is number of bytes to read */
+#define CMD_FW_READ 0x63
#define CMD_FIRMWARE_REINIT_6F 0x6F
#define FW_WRITE_CHUNK_SIZE 128
@@ -72,7 +92,8 @@
#define PT_INFO_BITS 0xF8
#define BT_INFO_NONE 0x00
#define PT_INFO_YES 0x80
-#define BT_INFO_NONE_BUT_DOWN 0x08 /* no new data but finder(s) still down */
+/* no new data but finder(s) still down */
+#define BT_INFO_NONE_BUT_DOWN 0x08
/* use this to include integers in commands */
#define CMD_UINT16(v) ((uint8_t)(v)) , ((uint8_t)((v) >> 8))
@@ -83,19 +104,21 @@ struct FingerData {
uint8_t hi;
uint8_t yLo;
uint8_t pressure;
-} __attribute__((packed));
+} __packed;
struct PointData {
uint8_t flags;
uint8_t palm;
struct FingerData fd[3];
-} __attribute__((packed));
+} __packed;
#define PD_FLAGS_DATA_TYPE_BITS 0xF0
/* other types (like chip-detected gestures) exist but we do not care */
#define PD_FLAGS_DATA_TYPE_TOUCH 0x00
-#define PD_FLAGS_NOT_PEN 0x08 /* set if pen touched, clear if finger(s) */
-#define PD_FLAGS_HAVE_FINGERS 0x07 /* a bit for each finger data that is valid (from lsb to msb) */
+/* set if pen touched, clear if finger(s) */
+#define PD_FLAGS_NOT_PEN 0x08
+/* a bit for each finger data that is valid (from lsb to msb) */
+#define PD_FLAGS_HAVE_FINGERS 0x07
#define PD_PALM_FLAG_BIT 0x01
#define FD_PRESSURE_BITS 0x0F
#define FD_PRESSURE_NONE 0x00
@@ -110,14 +133,13 @@ struct IT7260_ts_data {
struct input_dev *input_dev;
};
-static int8_t fwUploadResult = SYSFS_RESULT_NOT_DONE;
-static int8_t calibrationWasSuccessful = SYSFS_RESULT_NOT_DONE;
-static bool devicePresent = false;
+static int8_t fwUploadResult;
+static int8_t calibrationWasSuccessful;
+static bool devicePresent;
static DEFINE_MUTEX(sleepModeMutex);
-static bool chipAwake = true;
-static bool hadFingerDown = false;
-static bool isDeviceSleeping = false;
-static bool isDeviceSuspend = false;
+static bool chipAwake;
+static bool hadFingerDown;
+static bool isDeviceSuspend;
static struct input_dev *input_dev;
static struct IT7260_ts_data *gl_ts;
@@ -125,7 +147,8 @@ static struct IT7260_ts_data *gl_ts;
#define LOGI(...) printk(DEVICE_NAME ": " __VA_ARGS__)
/* internal use func - does not make sure chip is ready before read */
-static bool i2cReadNoReadyCheck(uint8_t bufferIndex, uint8_t *dataBuffer, uint16_t dataLength)
+static bool i2cReadNoReadyCheck(uint8_t bufferIndex, uint8_t *dataBuffer,
+ uint16_t dataLength)
{
struct i2c_msg msgs[2] = {
{
@@ -147,7 +170,8 @@ static bool i2cReadNoReadyCheck(uint8_t bufferIndex, uint8_t *dataBuffer, uint16
return i2c_transfer(gl_ts->client->adapter, msgs, 2);
}
-static bool i2cWriteNoReadyCheck(uint8_t bufferIndex, const uint8_t *dataBuffer, uint16_t dataLength)
+static bool i2cWriteNoReadyCheck(uint8_t bufferIndex,
+ const uint8_t *dataBuffer, uint16_t dataLength)
{
uint8_t txbuf[257];
struct i2c_msg msg = {
@@ -167,16 +191,16 @@ static bool i2cWriteNoReadyCheck(uint8_t bufferIndex, const uint8_t *dataBuffer,
}
/*
- * Device is apparently always ready for i2c but not for actual register reads/writes.
- * This function ascertains it is ready for that too. the results of this call often
- * were ignored.
+ * Device is apparently always ready for i2c but not for actual
+ * register reads/writes. This function ascertains it is ready
+ * for that too. the results of this call often were ignored.
*/
static bool waitDeviceReady(bool forever, bool slowly)
{
uint8_t ucQuery;
uint32_t count = DEVICE_READY_MAX_WAIT;
- do{
+ do {
if (!i2cReadNoReadyCheck(BUF_QUERY, &ucQuery, sizeof(ucQuery)))
ucQuery = CMD_STATUS_BUSY;
@@ -185,18 +209,20 @@ static bool waitDeviceReady(bool forever, bool slowly)
if (!forever)
count--;
- }while((ucQuery & CMD_STATUS_BUSY) && count);
+ } while ((ucQuery & CMD_STATUS_BUSY) && count);
return !ucQuery;
}
-static bool i2cRead(uint8_t bufferIndex, uint8_t *dataBuffer, uint16_t dataLength)
+static bool i2cRead(uint8_t bufferIndex, uint8_t *dataBuffer,
+ uint16_t dataLength)
{
waitDeviceReady(false, false);
return i2cReadNoReadyCheck(bufferIndex, dataBuffer, dataLength);
}
-static bool i2cWrite(uint8_t bufferIndex, const uint8_t *dataBuffer, uint16_t dataLength)
+static bool i2cWrite(uint8_t bufferIndex, const uint8_t *dataBuffer,
+ uint16_t dataLength)
{
waitDeviceReady(false, false);
return i2cWriteNoReadyCheck(bufferIndex, dataBuffer, dataLength);
@@ -219,7 +245,8 @@ static bool chipFirmwareReinitialize(uint8_t cmdOfChoice)
static bool chipFirmwareUpgradeModeEnterExit(bool enter)
{
- uint8_t cmd[] = {CMD_FIRMWARE_UPGRADE, 0, 'I', 'T', '7', '2', '6', '0', 0x55, 0xAA};
+ uint8_t cmd[] = {CMD_FIRMWARE_UPGRADE, 0, 'I', 'T', '7', '2',
+ '6', '0', 0x55, 0xAA};
uint8_t resp[2];
cmd[1] = enter ? FIRMWARE_MODE_ENTER : FIRMWARE_MODE_EXIT;
@@ -252,11 +279,13 @@ static bool chipSetStartOffset(uint16_t offset)
/* write fwLength bytes from fwData at chip offset writeStartOffset */
-static bool chipFlashWriteAndVerify(unsigned int fwLength, const uint8_t *fwData, uint16_t writeStartOffset)
+static bool chipFlashWriteAndVerify(unsigned int fwLength,
+ const uint8_t *fwData, uint16_t writeStartOffset)
{
uint32_t curDataOfst;
- for (curDataOfst = 0; curDataOfst < fwLength; curDataOfst += FW_WRITE_CHUNK_SIZE) {
+ for (curDataOfst = 0; curDataOfst < fwLength;
+ curDataOfst += FW_WRITE_CHUNK_SIZE) {
uint8_t cmdWrite[2 + FW_WRITE_CHUNK_SIZE] = {CMD_FW_WRITE};
uint8_t bufRead[FW_WRITE_CHUNK_SIZE];
@@ -277,9 +306,10 @@ static bool chipFlashWriteAndVerify(unsigned int fwLength, const uint8_t *fwData
/* prepare the read command */
cmdRead[1] = curWriteSz;
- for (nRetries = 0; nRetries < FW_WRITE_RETRY_COUNT; nRetries++) {
+ for (nRetries = 0; nRetries < FW_WRITE_RETRY_COUNT;
+ nRetries++) {
- /* set write offset and write the data*/
+ /* set write offset and write the data */
chipSetStartOffset(writeStartOffset + curDataOfst);
i2cWrite(BUF_COMMAND, cmdWrite, 2 + curWriteSz);
@@ -289,10 +319,13 @@ static bool chipFlashWriteAndVerify(unsigned int fwLength, const uint8_t *fwData
i2cRead(BUF_RESPONSE, bufRead, curWriteSz);
/* verify. If success break out of retry loop */
- for (i = 0; i < curWriteSz && bufRead[i] == cmdWrite[i + 2]; i++);
+ i = 0;
+ while (i < curWriteSz && bufRead[i] == cmdWrite[i + 2])
+ i++;
if (i == curWriteSz)
break;
- LOGE("write of data offset %u failed on try %u at byte %u/%u\n", curDataOfst, nRetries, i, curWriteSz);
+ pr_err("write of data offset %u failed on try %u at byte %u/%u\n",
+ curDataOfst, nRetries, i, curWriteSz);
}
/* if we've failed after all the retries, tell the caller */
if (nRetries == FW_WRITE_RETRY_COUNT)
@@ -302,7 +335,8 @@ static bool chipFlashWriteAndVerify(unsigned int fwLength, const uint8_t *fwData
return true;
}
-static bool chipFirmwareUpload(uint32_t fwLen, const uint8_t *fwData, uint32_t cfgLen, const uint8_t *cfgData)
+static bool chipFirmwareUpload(uint32_t fwLen, const uint8_t *fwData,
+ uint32_t cfgLen, const uint8_t *cfgData)
{
bool success = false;
@@ -317,7 +351,8 @@ static bool chipFirmwareUpload(uint32_t fwLen, const uint8_t *fwData, uint32_t c
}
/* flash config data if requested */
- if (fwLen && fwData && !chipFlashWriteAndVerify(cfgLen, cfgData, CHIP_FLASH_SIZE - cfgLen)) {
+ if (fwLen && fwData && !chipFlashWriteAndVerify(cfgLen, cfgData,
+ CHIP_FLASH_SIZE - cfgLen)) {
LOGE("failed to upload touch cfg data\n");
goto out;
}
@@ -325,24 +360,33 @@ static bool chipFirmwareUpload(uint32_t fwLen, const uint8_t *fwData, uint32_t c
success = true;
out:
- return chipFirmwareUpgradeModeEnterExit(false) && chipFirmwareReinitialize(CMD_FIRMWARE_REINIT_6F) && success;
+ return chipFirmwareUpgradeModeEnterExit(false) &&
+ chipFirmwareReinitialize(CMD_FIRMWARE_REINIT_6F) && success;
}
+
/*
* both buffers should be VERSION_LENGTH in size,
* but only a part of them is significant
*/
static bool chipGetVersions(uint8_t *verFw, uint8_t *verCfg, bool logIt)
{
- /* this code to get versions is reproduced as was written, but it does not make sense. Something here *PROBABLY IS* wrong */
+ /*
+ * this code to get versions is reproduced as was written, but it does
+ * not make sense. Something here *PROBABLY IS* wrong
+ */
static const uint8_t cmdReadFwVer[] = {CMD_READ_VERSIONS, VER_FIRMWARE};
static const uint8_t cmdReadCfgVer[] = {CMD_READ_VERSIONS, VER_CONFIG};
bool ret = true;
- /* this structure is so that we definitely do all the calls, but still return a status in case anyone cares */
+ /*
+ * this structure is so that we definitely do all the calls, but still
+ * return a status in case anyone cares
+ */
ret = i2cWrite(BUF_COMMAND, cmdReadFwVer, sizeof(cmdReadFwVer)) && ret;
ret = i2cRead(BUF_RESPONSE, verFw, VERSION_LENGTH) && ret;
- ret = i2cWrite(BUF_COMMAND, cmdReadCfgVer, sizeof(cmdReadCfgVer)) && ret;
+ ret = i2cWrite(BUF_COMMAND, cmdReadCfgVer,
+ sizeof(cmdReadCfgVer)) && ret;
ret = i2cRead(BUF_RESPONSE, verCfg, VERSION_LENGTH) && ret;
if (logIt)
@@ -353,26 +397,28 @@ static bool chipGetVersions(uint8_t *verFw, uint8_t *verCfg, bool logIt)
return ret;
}
-static ssize_t sysfsUpgradeStore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+static ssize_t sysfsUpgradeStore(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
{
const struct firmware *fw, *cfg;
uint8_t verFw[10], verCfg[10];
unsigned fwLen = 0, cfgLen = 0;
bool manualUpgrade, success;
- int mode = 0;
+ int mode = 0, ret;
- if (request_firmware(&fw, "it7260.fw", dev))
+ ret = request_firmware(&fw, "it7260.fw", dev);
+ if (ret)
LOGE("failed to get firmware for it7260\n");
else
fwLen = fw->size;
-
- if (request_firmware(&cfg, "it7260.cfg", dev))
+ ret = request_firmware(&cfg, "it7260.cfg", dev);
+ if (ret)
LOGE("failed to get config data for it7260\n");
else
cfgLen = cfg->size;
- sscanf(buf, "%d", &mode);
+ ret = kstrtoint(buf, 10, &mode);
manualUpgrade = mode == SYSFS_FW_UPLOAD_MODE_MANUAL;
LOGI("firmware found %ub of fw and %ub of config in %s mode\n",
fwLen, cfgLen, manualUpgrade ? "manual" : "normal");
@@ -389,13 +435,14 @@ static ssize_t sysfsUpgradeStore(struct device *dev, struct device_attribute *at
verCfg[4] < cfg->data[cfgLen - 5])){
LOGI("firmware/config will be upgraded\n");
disable_irq(gl_ts->client->irq);
- success = chipFirmwareUpload(fwLen, fw->data, cfgLen, cfg->data);
+ success = chipFirmwareUpload(fwLen, fw->data, cfgLen,
+ cfg->data);
enable_irq(gl_ts->client->irq);
- fwUploadResult = success ? SYSFS_RESULT_SUCCESS : SYSFS_RESULT_FAIL;
+ fwUploadResult = success ?
+ SYSFS_RESULT_SUCCESS : SYSFS_RESULT_FAIL;
LOGI("upload %s\n", success ? "success" : "failed");
- }
- else {
+ } else {
LOGI("firmware/config upgrade not needed\n");
}
}
@@ -409,14 +456,16 @@ static ssize_t sysfsUpgradeStore(struct device *dev, struct device_attribute *at
return count;
}
-static ssize_t sysfsUpgradeShow(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t sysfsUpgradeShow(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- return sprintf(buf, "%d", fwUploadResult);
+ return snprintf(buf, MAX_BUFFER_SIZE, "%d", fwUploadResult);
}
-static ssize_t sysfsCalibrationShow(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t sysfsCalibrationShow(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- return sprintf(buf, "%d", calibrationWasSuccessful);
+ return snprintf(buf, MAX_BUFFER_SIZE, "%d", calibrationWasSuccessful);
}
static bool chipSendCalibrationCmd(bool autoTuneOn)
@@ -425,52 +474,68 @@ static bool chipSendCalibrationCmd(bool autoTuneOn)
return i2cWrite(BUF_COMMAND, cmdCalibrate, sizeof(cmdCalibrate));
}
-static ssize_t sysfsCalibrationStore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+static ssize_t sysfsCalibrationStore(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
{
uint8_t resp;
if (!chipSendCalibrationCmd(false))
LOGE("failed to send calibration command\n");
else {
- calibrationWasSuccessful = i2cRead(BUF_RESPONSE, &resp, sizeof(resp)) ? SYSFS_RESULT_SUCCESS : SYSFS_RESULT_FAIL;
-
- /* previous logic that was here never called chipFirmwareReinitialize() due to checking a guaranteed-not-null value against null. We now call it. Hopefully this is OK */
+ calibrationWasSuccessful =
+ i2cRead(BUF_RESPONSE, &resp, sizeof(resp))
+ ? SYSFS_RESULT_SUCCESS : SYSFS_RESULT_FAIL;
+
+ /*
+ * previous logic that was here never called
+ * chipFirmwareReinitialize() due to checking a
+ * guaranteed-not-null value against null. We now
+ * call it. Hopefully this is OK
+ */
if (!resp)
- LOGI("chipFirmwareReinitialize -> %s\n", chipFirmwareReinitialize(CMD_FIRMWARE_REINIT_6F) ? "success" : "fail");
+ LOGI("chipFirmwareReinitialize -> %s\n",
+ chipFirmwareReinitialize(CMD_FIRMWARE_REINIT_6F)
+ ? "success" : "fail");
}
return count;
}
-static ssize_t sysfsPointShow(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t sysfsPointShow(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
uint8_t pointData[sizeof(struct PointData)];
bool readSuccess;
ssize_t ret;
- readSuccess = i2cReadNoReadyCheck(BUF_POINT_INFO, pointData, sizeof(pointData));
- ret = sprintf(buf, "point_show read ret[%d]--point[%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x]=\n",
- readSuccess, pointData[0],pointData[1],pointData[2],pointData[3],
- pointData[4],pointData[5],pointData[6],pointData[7],pointData[8],
- pointData[9],pointData[10],pointData[11],pointData[12],pointData[13]);
-
+ readSuccess = i2cReadNoReadyCheck(BUF_POINT_INFO, pointData,
+ sizeof(pointData));
+ ret = snprintf(buf, MAX_BUFFER_SIZE,
+ "point_show read ret[%d]--point[%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x]=\n",
+ readSuccess, pointData[0], pointData[1], pointData[2],
+ pointData[3], pointData[4], pointData[5], pointData[6],
+ pointData[7], pointData[8], pointData[9], pointData[10],
+ pointData[11], pointData[12], pointData[13]);
LOGI("%s", buf);
return ret;
}
-static ssize_t sysfsPointStore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+static ssize_t sysfsPointStore(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
{
return count;
}
-static ssize_t sysfsStatusShow(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t sysfsStatusShow(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- return sprintf(buf, "%d\n", devicePresent ? 1 : 0);
+ return snprintf(buf, MAX_BUFFER_SIZE, "%d\n", devicePresent ? 1 : 0);
}
-static ssize_t sysfsStatusStore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+static ssize_t sysfsStatusStore(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
{
uint8_t verFw[10], verCfg[10];
@@ -479,68 +544,82 @@ static ssize_t sysfsStatusStore(struct device *dev, struct device_attribute *att
return count;
}
-static ssize_t sysfsVersionShow(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t sysfsVersionShow(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
uint8_t verFw[10], verCfg[10];
chipGetVersions(verFw, verCfg, false);
- return sprintf(buf, "%x,%x,%x,%x # %x,%x,%x,%x\n",verFw[5], verFw[6], verFw[7], verFw[8], verCfg[1], verCfg[2], verCfg[3], verCfg[4]);
+ return snprintf(buf, MAX_BUFFER_SIZE, "%x,%x,%x,%x # %x,%x,%x,%x\n",
+ verFw[5], verFw[6], verFw[7], verFw[8],
+ verCfg[1], verCfg[2], verCfg[3], verCfg[4]);
}
-static ssize_t sysfsVersionStore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+static ssize_t sysfsVersionStore(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
{
return count;
}
-static ssize_t sysfsSleepShow(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t sysfsSleepShow(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
/*
- * The usefulness of this was questionable at best - we were at least leaking a byte of
- * kernel data (by claiming to return a byte but not writing to buf. To fix this now
- * we actually return the sleep status
+ * The usefulness of this was questionable at best - we were at least
+ * leaking a byte of kernel data (by claiming to return a byte but not
+ * writing to buf. To fix this now we actually return the sleep status
*/
if (!mutex_lock_interruptible(&sleepModeMutex)) {
*buf = chipAwake ? '1' : '0';
mutex_unlock(&sleepModeMutex);
return 1;
- }
- else
+ } else {
return -EINTR;
+ }
}
-static ssize_t sysfsSleepStore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+static ssize_t sysfsSleepStore(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
{
- static const uint8_t cmdGoSleep[] = {CMD_PWR_CTL, 0x00, PWR_CTL_SLEEP_MODE};
- int goToSleepVal;
+ static const uint8_t cmdGoSleep[] = {CMD_PWR_CTL,
+ 0x00, PWR_CTL_SLEEP_MODE};
+ int goToSleepVal, ret;
bool goToWake;
uint8_t dummy;
- sscanf(buf, "%d", &goToSleepVal);
- goToWake = !goToSleepVal; /* convert to bool of proper polarity */
+ ret = kstrtoint(buf, 10, &goToSleepVal);
+ /* convert to bool of proper polarity */
+ goToWake = !goToSleepVal;
if (!mutex_lock_interruptible(&sleepModeMutex)) {
if ((chipAwake && goToWake) || (!chipAwake && !goToWake))
- LOGE("duplicate request to %s chip\n", goToWake ? "wake" : "sleep");
+ LOGE("duplicate request to %s chip\n",
+ goToWake ? "wake" : "sleep");
else if (goToWake) {
i2cReadNoReadyCheck(BUF_QUERY, &dummy, sizeof(dummy));
enable_irq(gl_ts->client->irq);
- LOGI("touch is going to wake!\n\n");
+ LOGI("touch is going to wake!\n");
} else {
disable_irq(gl_ts->client->irq);
- i2cWriteNoReadyCheck(BUF_COMMAND, cmdGoSleep, sizeof(cmdGoSleep));
- LOGI("touch is going to sleep...\n\n");
+ i2cWriteNoReadyCheck(BUF_COMMAND, cmdGoSleep,
+ sizeof(cmdGoSleep));
+ LOGI("touch is going to sleep...\n");
}
chipAwake = goToWake;
mutex_unlock(&sleepModeMutex);
return count;
- } else
+ } else {
return -EINTR;
+ }
}
-static DEVICE_ATTR(status, S_IRUGO|S_IWUSR|S_IWGRP, sysfsStatusShow, sysfsStatusStore);
-static DEVICE_ATTR(version, S_IRUGO|S_IWUSR|S_IWGRP, sysfsVersionShow, sysfsVersionStore);
-static DEVICE_ATTR(sleep, S_IRUGO|S_IWUSR|S_IWGRP, sysfsSleepShow, sysfsSleepStore);
+static DEVICE_ATTR(status, S_IRUGO|S_IWUSR|S_IWGRP,
+ sysfsStatusShow, sysfsStatusStore);
+static DEVICE_ATTR(version, S_IRUGO|S_IWUSR|S_IWGRP,
+ sysfsVersionShow, sysfsVersionStore);
+static DEVICE_ATTR(sleep, S_IRUGO|S_IWUSR|S_IWGRP,
+ sysfsSleepShow, sysfsSleepStore);
static struct attribute *it7260_attrstatus[] = {
&dev_attr_status.attr,
@@ -553,10 +632,12 @@ static const struct attribute_group it7260_attrstatus_group = {
.attrs = it7260_attrstatus,
};
-static DEVICE_ATTR(calibration, S_IRUGO|S_IWUSR|S_IWGRP, sysfsCalibrationShow, sysfsCalibrationStore);
-static DEVICE_ATTR(upgrade, S_IRUGO|S_IWUSR|S_IWGRP, sysfsUpgradeShow, sysfsUpgradeStore);
-static DEVICE_ATTR(point, S_IRUGO|S_IWUSR|S_IWGRP, sysfsPointShow, sysfsPointStore);
-
+static DEVICE_ATTR(calibration, S_IRUGO|S_IWUSR|S_IWGRP,
+ sysfsCalibrationShow, sysfsCalibrationStore);
+static DEVICE_ATTR(upgrade, S_IRUGO|S_IWUSR|S_IWGRP,
+ sysfsUpgradeShow, sysfsUpgradeStore);
+static DEVICE_ATTR(point, S_IRUGO|S_IWUSR|S_IWGRP,
+ sysfsPointShow, sysfsPointStore);
static struct attribute *it7260_attributes[] = {
&dev_attr_calibration.attr,
@@ -573,7 +654,8 @@ static void chipExternalCalibration(bool autoTuneEnabled)
{
uint8_t resp[2];
- LOGI("sent calibration command -> %d\n", chipSendCalibrationCmd(autoTuneEnabled));
+ LOGI("sent calibration command -> %d\n",
+ chipSendCalibrationCmd(autoTuneEnabled));
waitDeviceReady(true, true);
i2cReadNoReadyCheck(BUF_RESPONSE, resp, sizeof(resp));
chipFirmwareReinitialize(CMD_FIRMWARE_REINIT_C);
@@ -585,7 +667,8 @@ void sendCalibrationCmd(void)
}
EXPORT_SYMBOL(sendCalibrationCmd);
-static void readFingerData(uint16_t *xP, uint16_t *yP, uint8_t *pressureP, const struct FingerData *fd)
+static void readFingerData(uint16_t *xP, uint16_t *yP, uint8_t *pressureP,
+ const struct FingerData *fd)
{
uint16_t x = fd->xLo;
uint16_t y = fd->yLo;
@@ -611,15 +694,19 @@ static void readTouchDataPoint(void)
/* verify there is point data to read & it is readable and valid */
i2cReadNoReadyCheck(BUF_QUERY, &devStatus, sizeof(devStatus));
if (!((devStatus & PT_INFO_BITS) & PT_INFO_YES)) {
- LOGE("readTouchDataPoint() called when no data available (0x%02X)\n", devStatus);
+ pr_err("readTouchDataPoint() called when no data available (0x%02X)\n",
+ devStatus);
return;
}
- if (!i2cReadNoReadyCheck(BUF_POINT_INFO, (void*)&pointData, sizeof(pointData))) {
- LOGE("readTouchDataPoint() failed to read point data buffer\n");
+ if (!i2cReadNoReadyCheck(BUF_POINT_INFO, (void *)&pointData,
+ sizeof(pointData))) {
+ pr_err("readTouchDataPoint() failed to read point data buffer\n");
return;
}
- if ((pointData.flags & PD_FLAGS_DATA_TYPE_BITS) != PD_FLAGS_DATA_TYPE_TOUCH) {
- LOGE("readTouchDataPoint() dropping non-point data of type 0x%02X\n", pointData.flags);
+ if ((pointData.flags & PD_FLAGS_DATA_TYPE_BITS) !=
+ PD_FLAGS_DATA_TYPE_TOUCH) {
+ pr_err("readTouchDataPoint() dropping non-point data of type 0x%02X\n",
+ pointData.flags);
return;
}
@@ -649,20 +736,15 @@ static void readTouchDataPoint(void)
static irqreturn_t IT7260_ts_threaded_handler(int irq, void *devid)
{
- smp_rmb();
- if (isDeviceSleeping) {
- smp_wmb();
- } else {
- readTouchDataPoint();
- }
-
+ readTouchDataPoint();
return IRQ_HANDLED;
}
static bool chipIdentifyIT7260(void)
{
static const uint8_t cmdIdent[] = {CMD_IDENT_CHIP};
- static const uint8_t expectedID[] = {0x0A, 'I', 'T', 'E', '7', '2', '6', '0'};
+ static const uint8_t expectedID[] = {0x0A, 'I', 'T', 'E', '7',
+ '2', '6', '0'};
uint8_t chipID[10] = {0,};
waitDeviceReady(true, false);
@@ -678,7 +760,7 @@ static bool chipIdentifyIT7260(void)
LOGE("i2cRead() failed\n");
return false;
}
- pr_info("chipIdentifyIT7260 read id: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
+ LOGI("chipIdentifyIT7260 read id: %02X %c%c%c%c%c%c%ci %c%c\n",
chipID[0], chipID[1], chipID[2], chipID[3], chipID[4],
chipID[5], chipID[6], chipID[7], chipID[8], chipID[9]);
@@ -690,12 +772,14 @@ static bool chipIdentifyIT7260(void)
else if (chipID[8] == '6' && chipID[9] == '6')
LOGI("rev BX4 found\n");
else
- LOGI("unknown revision (0x%02X 0x%02X) found\n", chipID[8], chipID[9]);
+ LOGI("unknown revision (0x%02X 0x%02X) found\n",
+ chipID[8], chipID[9]);
return true;
}
-static int IT7260_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
+static int IT7260_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
static const uint8_t cmdStart[] = {CMD_UNKNOWN_7};
struct IT7260_i2c_platform_data *pdata;
@@ -729,7 +813,7 @@ static int IT7260_ts_probe(struct i2c_client *client, const struct i2c_device_id
}
if (!chipIdentifyIT7260()) {
- LOGI ("chipIdentifyIT7260 FAIL");
+ LOGI("chipIdentifyIT7260 FAIL");
goto err_ident_fail_or_input_alloc;
}
@@ -762,7 +846,8 @@ static int IT7260_ts_probe(struct i2c_client *client, const struct i2c_device_id
goto err_input_register;
}
- if (request_threaded_irq(client->irq, NULL, IT7260_ts_threaded_handler, IRQF_TRIGGER_LOW | IRQF_ONESHOT, client->name, gl_ts)) {
+ if (request_threaded_irq(client->irq, NULL, IT7260_ts_threaded_handler,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT, client->name, gl_ts)) {
dev_err(&client->dev, "request_irq failed\n");
goto err_irq_reg;
}
diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c
index c78178c0e1a1..a0227fd05939 100644
--- a/drivers/iommu/iommu-debug.c
+++ b/drivers/iommu/iommu-debug.c
@@ -29,6 +29,8 @@
#include <asm/cacheflush.h>
#include <asm/dma-iommu.h>
+#if defined(CONFIG_IOMMU_DEBUG_TRACKING) || defined(CONFIG_IOMMU_TESTS)
+
static const char *iommu_debug_attr_to_string(enum iommu_attr attr)
{
switch (attr) {
@@ -74,6 +76,7 @@ static const char *iommu_debug_attr_to_string(enum iommu_attr attr)
return "Unknown attr!";
}
}
+#endif
#ifdef CONFIG_IOMMU_DEBUG_TRACKING
@@ -457,6 +460,20 @@ static inline void iommu_debug_destroy_tracking(void) { }
#ifdef CONFIG_IOMMU_TESTS
+#ifdef CONFIG_64BIT
+
+#define kstrtoux kstrtou64
+#define kstrtox_from_user kstrtoll_from_user
+#define kstrtosize_t kstrtoul
+
+#else
+
+#define kstrtoux kstrtou32
+#define kstrtox_from_user kstrtoint_from_user
+#define kstrtosize_t kstrtouint
+
+#endif
+
static LIST_HEAD(iommu_debug_devices);
static struct dentry *debugfs_tests_dir;
static u32 iters_per_op = 1;
@@ -549,10 +566,10 @@ DEFINE_SIMPLE_ATTRIBUTE(iommu_debug_nr_iters_ops,
static void iommu_debug_device_profiling(struct seq_file *s, struct device *dev,
enum iommu_attr attrs[],
void *attr_values[], int nattrs,
- const unsigned long sizes[])
+ const size_t sizes[])
{
int i;
- const unsigned long *sz;
+ const size_t *sz;
struct iommu_domain *domain;
struct bus_type *bus;
unsigned long iova = 0x10000;
@@ -593,7 +610,7 @@ static void iommu_debug_device_profiling(struct seq_file *s, struct device *dev,
seq_printf(s, "(average over %d iterations)\n", iters_per_op);
seq_printf(s, "%8s %19s %16s\n", "size", "iommu_map", "iommu_unmap");
for (sz = sizes; *sz; ++sz) {
- unsigned long size = *sz;
+ size_t size = *sz;
size_t unmapped;
u64 map_elapsed_ns = 0, unmap_elapsed_ns = 0;
u64 map_elapsed_us = 0, unmap_elapsed_us = 0;
@@ -625,8 +642,10 @@ static void iommu_debug_device_profiling(struct seq_file *s, struct device *dev,
unmap_elapsed_ns += timespec_to_ns(&diff);
}
- map_elapsed_ns /= iters_per_op;
- unmap_elapsed_ns /= iters_per_op;
+ map_elapsed_ns = div_u64_rem(map_elapsed_ns, iters_per_op,
+ &map_elapsed_rem);
+ unmap_elapsed_ns = div_u64_rem(unmap_elapsed_ns, iters_per_op,
+ &unmap_elapsed_rem);
map_elapsed_us = div_u64_rem(map_elapsed_ns, 1000,
&map_elapsed_rem);
@@ -642,7 +661,7 @@ static void iommu_debug_device_profiling(struct seq_file *s, struct device *dev,
seq_putc(s, '\n');
seq_printf(s, "%8s %19s %16s\n", "size", "iommu_map_sg", "iommu_unmap");
for (sz = sizes; *sz; ++sz) {
- unsigned long size = *sz;
+ size_t size = *sz;
size_t unmapped;
u64 map_elapsed_ns = 0, unmap_elapsed_ns = 0;
u64 map_elapsed_us = 0, unmap_elapsed_us = 0;
@@ -683,8 +702,10 @@ static void iommu_debug_device_profiling(struct seq_file *s, struct device *dev,
unmap_elapsed_ns += timespec_to_ns(&diff);
}
- map_elapsed_ns /= iters_per_op;
- unmap_elapsed_ns /= iters_per_op;
+ map_elapsed_ns = div_u64_rem(map_elapsed_ns, iters_per_op,
+ &map_elapsed_rem);
+ unmap_elapsed_ns = div_u64_rem(unmap_elapsed_ns, iters_per_op,
+ &unmap_elapsed_rem);
map_elapsed_us = div_u64_rem(map_elapsed_ns, 1000,
&map_elapsed_rem);
@@ -709,7 +730,7 @@ out_domain_free:
static int iommu_debug_profiling_show(struct seq_file *s, void *ignored)
{
struct iommu_debug_device *ddev = s->private;
- const unsigned long sizes[] = { SZ_4K, SZ_64K, SZ_2M, SZ_1M * 12,
+ const size_t sizes[] = { SZ_4K, SZ_64K, SZ_2M, SZ_1M * 12,
SZ_1M * 20, 0 };
enum iommu_attr attrs[] = {
DOMAIN_ATTR_COHERENT_HTW_DISABLE,
@@ -739,7 +760,7 @@ static const struct file_operations iommu_debug_profiling_fops = {
static int iommu_debug_secure_profiling_show(struct seq_file *s, void *ignored)
{
struct iommu_debug_device *ddev = s->private;
- const unsigned long sizes[] = { SZ_4K, SZ_64K, SZ_2M, SZ_1M * 12,
+ const size_t sizes[] = { SZ_4K, SZ_64K, SZ_2M, SZ_1M * 12,
SZ_1M * 20, 0 };
enum iommu_attr attrs[] = {
@@ -830,7 +851,7 @@ static int iommu_debug_profiling_fast_dma_api_show(struct seq_file *s,
if (!virt)
goto out;
- mapping = arm_iommu_create_mapping(&platform_bus_type, 0, SZ_1G * 4ULL);
+ mapping = arm_iommu_create_mapping(&platform_bus_type, 0, SZ_1G * 4UL);
if (!mapping) {
seq_puts(s, "fast_smmu_create_mapping failed\n");
goto out_kfree;
@@ -851,7 +872,7 @@ static int iommu_debug_profiling_fast_dma_api_show(struct seq_file *s,
goto out_detach;
}
for (experiment = 0; experiment < 2; ++experiment) {
- u64 map_avg = 0, unmap_avg = 0;
+ size_t map_avg = 0, unmap_avg = 0;
for (i = 0; i < 10; ++i) {
struct timespec tbefore, tafter, diff;
@@ -888,7 +909,7 @@ static int iommu_debug_profiling_fast_dma_api_show(struct seq_file *s,
i < 9 ? ", " : "");
}
map_avg /= 10;
- seq_printf(s, "] (avg: %llu)\n", map_avg);
+ seq_printf(s, "] (avg: %zu)\n", map_avg);
seq_printf(s, "%13s %24s (ns): [", extra_labels[experiment],
"dma_unmap_single_attrs");
@@ -898,7 +919,7 @@ static int iommu_debug_profiling_fast_dma_api_show(struct seq_file *s,
i < 9 ? ", " : "");
}
unmap_avg /= 10;
- seq_printf(s, "] (avg: %llu)\n", unmap_avg);
+ seq_printf(s, "] (avg: %zu)\n", unmap_avg);
}
out_disable_config_clocks:
@@ -1382,7 +1403,7 @@ static int __apply_to_new_mapping(struct seq_file *s,
int ret = -EINVAL, fast = 1;
phys_addr_t pt_phys;
- mapping = arm_iommu_create_mapping(&platform_bus_type, 0, SZ_1G * 4ULL);
+ mapping = arm_iommu_create_mapping(&platform_bus_type, 0, SZ_1G * 4UL);
if (!mapping)
goto out;
@@ -1631,7 +1652,7 @@ static ssize_t iommu_debug_atos_write(struct file *file,
struct iommu_debug_device *ddev = file->private_data;
dma_addr_t iova;
- if (kstrtoll_from_user(ubuf, count, 0, &iova)) {
+ if (kstrtox_from_user(ubuf, count, 0, &iova)) {
pr_err("Invalid format for iova\n");
ddev->iova = 0;
return -EINVAL;
@@ -1730,13 +1751,13 @@ static ssize_t iommu_debug_map_write(struct file *file, const char __user *ubuf,
/* split up the words */
*comma1 = *comma2 = *comma3 = '\0';
- if (kstrtou64(buf, 0, &iova))
+ if (kstrtoux(buf, 0, &iova))
goto invalid_format;
- if (kstrtou64(comma1 + 1, 0, &phys))
+ if (kstrtoux(comma1 + 1, 0, &phys))
goto invalid_format;
- if (kstrtoul(comma2 + 1, 0, &size))
+ if (kstrtosize_t(comma2 + 1, 0, &size))
goto invalid_format;
if (kstrtoint(comma3 + 1, 0, &prot))
@@ -1802,10 +1823,10 @@ static ssize_t iommu_debug_unmap_write(struct file *file,
/* split up the words */
*comma1 = '\0';
- if (kstrtou64(buf, 0, &iova))
+ if (kstrtoux(buf, 0, &iova))
goto invalid_format;
- if (kstrtoul(comma1 + 1, 0, &size))
+ if (kstrtosize_t(comma1 + 1, 0, &size))
goto invalid_format;
unmapped = iommu_unmap(ddev->domain, iova, size);
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/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index d58684109395..4a26ab920016 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -2116,6 +2116,14 @@ int create_pkt_cmd_session_set_property(
pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
break;
}
+ case HAL_PARAM_VENC_H264_TRANSFORM_8x8:
+ {
+ create_pkt_enable(pkt->rg_property_data,
+ HFI_PROPERTY_PARAM_VENC_H264_8X8_TRANSFORM,
+ ((struct hal_enable *)pdata)->enable);
+ pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
+ break;
+ }
/* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */
case HAL_CONFIG_BUFFER_REQUIREMENTS:
case HAL_CONFIG_PRIORITY:
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index 0e668b93598f..5c7408740e95 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -869,7 +869,7 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
.name = "Ltr Mode",
.type = V4L2_CTRL_TYPE_INTEGER,
.minimum = V4L2_MPEG_VIDC_VIDEO_LTR_MODE_DISABLE,
- .maximum = V4L2_MPEG_VIDC_VIDEO_LTR_MODE_PERIODIC,
+ .maximum = V4L2_MPEG_VIDC_VIDEO_LTR_MODE_MANUAL,
.default_value = V4L2_MPEG_VIDC_VIDEO_LTR_MODE_DISABLE,
.step = 1,
.qmenu = NULL,
@@ -1232,6 +1232,15 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
.default_value = 0,
.step = 1,
},
+ {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8,
+ .name = "Transform 8x8",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_DISABLE,
+ .maximum = V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_ENABLE,
+ .default_value = V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_ENABLE,
+ .step = 1,
+ },
};
#define NUM_CTRLS ARRAY_SIZE(msm_venc_ctrls)
@@ -3100,6 +3109,24 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
pdata = &enable;
break;
}
+ case V4L2_CID_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8:
+ property_id = HAL_PARAM_VENC_H264_TRANSFORM_8x8;
+ switch (ctrl->val) {
+ case V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_ENABLE:
+ enable.enable = 1;
+ break;
+ case V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_DISABLE:
+ enable.enable = 0;
+ break;
+ default:
+ dprintk(VIDC_ERR,
+ "Invalid H264 8x8 transform control value %d\n",
+ ctrl->val);
+ rc = -ENOTSUPP;
+ break;
+ }
+ pdata = &enable;
+ break;
default:
dprintk(VIDC_ERR, "Unsupported index: %x\n", ctrl->id);
rc = -ENOTSUPP;
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index 624fd53debe8..b2231869c499 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -237,6 +237,7 @@ enum hal_property {
HAL_PARAM_VENC_CONSTRAINED_INTRA_PRED,
HAL_CONFIG_VENC_BLUR_RESOLUTION,
HAL_PARAM_VENC_SESSION_QP_RANGE_PACKED,
+ HAL_PARAM_VENC_H264_TRANSFORM_8x8,
};
enum hal_domain {
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
index a2d95927e400..ff043e9a819b 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
@@ -364,6 +364,8 @@ struct hfi_buffer_info {
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x022)
#define HFI_PROPERTY_PARAM_VENC_PRESERVE_TEXT_QUALITY \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x023)
+#define HFI_PROPERTY_PARAM_VENC_H264_8X8_TRANSFORM \
+ (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x025)
#define HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x026)
#define HFI_PROPERTY_PARAM_VENC_DISABLE_RC_TIMESTAMP \
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 d7a987335dda..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 {
@@ -1061,8 +1068,8 @@ static bool ipa3_usb_check_chan_params(struct ipa_usb_xdci_chan_params *params)
IPA_USB_DBG_LOW("xfer_ring_len = %d\n", params->xfer_ring_len);
IPA_USB_DBG_LOW("xfer_ring_base_addr = %llx\n",
params->xfer_ring_base_addr);
- IPA_USB_DBG_LOW("last_trb_addr = %x\n",
- params->xfer_scratch.last_trb_addr);
+ IPA_USB_DBG_LOW("last_trb_addr_iova = %x\n",
+ params->xfer_scratch.last_trb_addr_iova);
IPA_USB_DBG_LOW("const_buffer_size = %d\n",
params->xfer_scratch.const_buffer_size);
IPA_USB_DBG_LOW("depcmd_low_addr = %x\n",
@@ -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;
@@ -1227,7 +1312,7 @@ static int ipa3_usb_request_xdci_channel(
chan_params.chan_params.err_cb = ipa3_usb_gsi_chan_err_cb;
chan_params.chan_params.chan_user_data = NULL;
chan_params.chan_scratch.xdci.last_trb_addr =
- params->xfer_scratch.last_trb_addr;
+ params->xfer_scratch.last_trb_addr_iova;
/* xferrscidx will be updated later */
chan_params.chan_scratch.xdci.xferrscidx = 0;
chan_params.chan_scratch.xdci.const_buffer_size =
@@ -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_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/Kconfig b/drivers/power/qcom-charger/Kconfig
index b37853b4f70c..7a0b1464ad86 100644
--- a/drivers/power/qcom-charger/Kconfig
+++ b/drivers/power/qcom-charger/Kconfig
@@ -20,6 +20,16 @@ config QPNP_FG
fuel gauge. The state of charge is reported through a BMS power
supply property and also sends uevents when the capacity is updated.
+config QPNP_FG_GEN3
+ tristate "QPNP GEN3 fuel gauge driver"
+ depends on SPMI
+ select REGMAP_SPMI
+ help
+ Say Y here to enable the GEN3 Fuel Gauge driver. This adds support
+ for battery fuel gauging and state of charge of battery connected to
+ the fuel gauge. The state of charge is reported through a BMS power
+ supply property and also sends uevents when the capacity is updated.
+
config SMB135X_CHARGER
tristate "SMB135X Battery Charger"
depends on I2C
diff --git a/drivers/power/qcom-charger/Makefile b/drivers/power/qcom-charger/Makefile
index df7b78d4fc52..aae6084c3c10 100644
--- a/drivers/power/qcom-charger/Makefile
+++ b/drivers/power/qcom-charger/Makefile
@@ -1,5 +1,6 @@
obj-$(CONFIG_QPNP_SMBCHARGER) += qpnp-smbcharger.o batterydata-lib.o pmic-voter.o
obj-$(CONFIG_QPNP_FG) += qpnp-fg.o
+obj-$(CONFIG_QPNP_FG_GEN3) += qpnp-fg-gen3.o fg-memif.o fg-util.o
obj-$(CONFIG_SMB135X_CHARGER) += smb135x-charger.o pmic-voter.o
obj-$(CONFIG_SMB1351_USB_CHARGER) += smb1351-charger.o pmic-voter.o
obj-$(CONFIG_MSM_BCL_CTL) += msm_bcl.o
diff --git a/drivers/power/qcom-charger/fg-core.h b/drivers/power/qcom-charger/fg-core.h
new file mode 100644
index 000000000000..cf7869ea1515
--- /dev/null
+++ b/drivers/power/qcom-charger/fg-core.h
@@ -0,0 +1,247 @@
+/* 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.
+ */
+
+#ifndef __FG_CORE_H__
+#define __FG_CORE_H__
+
+#include <linux/atomic.h>
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/string_helpers.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include "pmic-voter.h"
+
+#define fg_dbg(chip, reason, fmt, ...) \
+ do { \
+ if (*chip->debug_mask & (reason)) \
+ pr_info(fmt, ##__VA_ARGS__); \
+ else \
+ pr_debug(fmt, ##__VA_ARGS__); \
+ } while (0)
+
+#define SRAM_READ "fg_sram_read"
+#define SRAM_WRITE "fg_sram_write"
+#define SRAM_UPDATE "fg_sram_update"
+#define PROFILE_LOAD "fg_profile_load"
+#define DELTA_SOC "fg_delta_soc"
+
+#define DEBUG_PRINT_BUFFER_SIZE 64
+/* 3 byte address + 1 space character */
+#define ADDR_LEN 4
+/* Format is 'XX ' */
+#define CHARS_PER_ITEM 3
+/* 4 data items per line */
+#define ITEMS_PER_LINE 4
+#define MAX_LINE_LENGTH (ADDR_LEN + (ITEMS_PER_LINE * \
+ CHARS_PER_ITEM) + 1) \
+
+#define FG_SRAM_ADDRESS_MAX 255
+
+/* Debug flag definitions */
+enum fg_debug_flag {
+ FG_IRQ = BIT(0), /* Show interrupts */
+ FG_STATUS = BIT(1), /* Show FG status changes */
+ FG_POWER_SUPPLY = BIT(2), /* Show POWER_SUPPLY */
+ FG_SRAM_WRITE = BIT(3), /* Show SRAM writes */
+ FG_SRAM_READ = BIT(4), /* Show SRAM reads */
+ FG_BUS_WRITE = BIT(5), /* Show REGMAP writes */
+ FG_BUS_READ = BIT(6), /* Show REGMAP reads */
+};
+
+/* SRAM access */
+enum sram_access_flags {
+ FG_IMA_DEFAULT = 0,
+ FG_IMA_ATOMIC,
+};
+
+/* JEITA */
+enum {
+ JEITA_COLD = 0,
+ JEITA_COOL,
+ JEITA_WARM,
+ JEITA_HOT,
+ NUM_JEITA_LEVELS,
+};
+
+/* FG irqs */
+enum fg_irq_index {
+ MSOC_FULL_IRQ = 0,
+ MSOC_HIGH_IRQ,
+ MSOC_EMPTY_IRQ,
+ MSOC_LOW_IRQ,
+ MSOC_DELTA_IRQ,
+ BSOC_DELTA_IRQ,
+ SOC_READY_IRQ,
+ SOC_UPDATE_IRQ,
+ BATT_TEMP_DELTA_IRQ,
+ BATT_MISSING_IRQ,
+ ESR_DELTA_IRQ,
+ VBATT_LOW_IRQ,
+ VBATT_PRED_DELTA_IRQ,
+ DMA_GRANT_IRQ,
+ MEM_XCP_IRQ,
+ IMA_RDY_IRQ,
+ FG_IRQ_MAX,
+};
+
+/* WA flags */
+enum {
+ DELTA_SOC_IRQ_WA = BIT(0),
+};
+
+/* SRAM parameters */
+enum fg_sram_param_id {
+ FG_SRAM_BATT_SOC = 0,
+ FG_SRAM_VOLTAGE_PRED,
+ FG_SRAM_OCV,
+ FG_SRAM_RSLOW,
+ /* Entries below here are configurable during initialization */
+ FG_SRAM_CUTOFF_VOLT,
+ FG_SRAM_EMPTY_VOLT,
+ FG_SRAM_VBATT_LOW,
+ FG_SRAM_SYS_TERM_CURR,
+ FG_SRAM_CHG_TERM_CURR,
+ FG_SRAM_DELTA_SOC_THR,
+ FG_SRAM_RECHARGE_SOC_THR,
+ FG_SRAM_MAX,
+};
+
+struct fg_sram_param {
+ u16 address;
+ int offset;
+ u8 len;
+ int value;
+ int numrtr;
+ int denmtr;
+ void (*encode)(struct fg_sram_param *sp, enum fg_sram_param_id id,
+ int val, u8 *buf);
+ int (*decode)(struct fg_sram_param *sp, enum fg_sram_param_id id,
+ int val);
+};
+
+/* DT parameters for FG device */
+struct fg_dt_props {
+ int cutoff_volt_mv;
+ int empty_volt_mv;
+ int vbatt_low_thr_mv;
+ int chg_term_curr_ma;
+ int sys_term_curr_ma;
+ int delta_soc_thr;
+ int recharge_soc_thr;
+ int rsense_sel;
+ int jeita_thresholds[NUM_JEITA_LEVELS];
+};
+
+/* parameters from battery profile */
+struct fg_batt_props {
+ const char *batt_type_str;
+ char *batt_profile;
+ int float_volt_uv;
+ int fastchg_curr_ma;
+ int batt_id_kohm;
+};
+
+struct fg_irq_info {
+ const char *name;
+ const irq_handler_t handler;
+ int irq;
+ bool wakeable;
+};
+
+struct fg_chip {
+ struct device *dev;
+ struct pmic_revid_data *pmic_rev_id;
+ struct regmap *regmap;
+ struct dentry *dentry;
+ struct power_supply *fg_psy;
+ struct power_supply *batt_psy;
+ struct iio_channel *batt_id_chan;
+ struct fg_memif *sram;
+ struct fg_irq_info *irqs;
+ struct votable *awake_votable;
+ struct fg_sram_param *sp;
+ int *debug_mask;
+ char *batt_profile;
+ struct fg_dt_props dt;
+ struct fg_batt_props bp;
+ struct notifier_block nb;
+ struct mutex bus_lock;
+ struct mutex sram_rw_lock;
+ u32 batt_soc_base;
+ u32 batt_info_base;
+ u32 mem_if_base;
+ int nom_cap_uah;
+ bool batt_id_avail;
+ bool profile_loaded;
+ bool battery_missing;
+ struct completion soc_update;
+ struct completion soc_ready;
+ struct delayed_work profile_load_work;
+ struct work_struct status_change_work;
+};
+
+/* Debugfs data structures are below */
+
+/* Log buffer */
+struct fg_log_buffer {
+ size_t rpos;
+ size_t wpos;
+ size_t len;
+ char data[0];
+};
+
+/* transaction parameters */
+struct fg_trans {
+ struct fg_chip *chip;
+ struct fg_log_buffer *log;
+ u32 cnt;
+ u16 addr;
+ u32 offset;
+ u8 *data;
+};
+
+struct fg_dbgfs {
+ struct debugfs_blob_wrapper help_msg;
+ struct fg_chip *chip;
+ struct dentry *root;
+ u32 cnt;
+ u32 addr;
+};
+
+extern int fg_sram_write(struct fg_chip *chip, u16 address, u8 offset,
+ u8 *val, int len, int flags);
+extern int fg_sram_read(struct fg_chip *chip, u16 address, u8 offset,
+ u8 *val, int len, int flags);
+extern int fg_sram_masked_write(struct fg_chip *chip, u16 address, u8 offset,
+ u8 mask, u8 val, int flags);
+extern int fg_interleaved_mem_read(struct fg_chip *chip, u16 address,
+ u8 offset, u8 *val, int len);
+extern int fg_interleaved_mem_write(struct fg_chip *chip, u16 address,
+ u8 offset, u8 *val, int len, bool atomic_access);
+extern int fg_read(struct fg_chip *chip, int addr, u8 *val, int len);
+extern int fg_write(struct fg_chip *chip, int addr, u8 *val, int len);
+extern int fg_masked_write(struct fg_chip *chip, int addr, u8 mask, u8 val);
+extern int fg_ima_init(struct fg_chip *chip);
+extern int fg_sram_debugfs_create(struct fg_chip *chip);
+extern void fill_string(char *str, size_t str_len, u8 *buf, int buf_len);
+extern int64_t twos_compliment_extend(int64_t val, int s_bit_pos);
+#endif
diff --git a/drivers/power/qcom-charger/fg-memif.c b/drivers/power/qcom-charger/fg-memif.c
new file mode 100644
index 000000000000..087223d708da
--- /dev/null
+++ b/drivers/power/qcom-charger/fg-memif.c
@@ -0,0 +1,583 @@
+/* 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.
+ */
+
+#define pr_fmt(fmt) "FG: %s: " fmt, __func__
+
+#include "fg-core.h"
+#include "fg-reg.h"
+
+/* Generic definitions */
+#define RETRY_COUNT 3
+#define BYTES_PER_SRAM_WORD 4
+
+enum {
+ FG_READ = 0,
+ FG_WRITE,
+};
+
+static int fg_set_address(struct fg_chip *chip, u16 address)
+{
+ u8 buffer[2];
+ int rc;
+
+ buffer[0] = address & 0xFF;
+ /* MSB has to be written zero */
+ buffer[1] = 0;
+
+ rc = fg_write(chip, MEM_IF_ADDR_LSB(chip), buffer, 2);
+ if (rc < 0) {
+ pr_err("failed to write to 0x%04X, rc=%d\n",
+ MEM_IF_ADDR_LSB(chip), rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int fg_config_access_mode(struct fg_chip *chip, bool access, bool burst)
+{
+ int rc;
+ u8 intf_ctl = 0;
+
+ intf_ctl = ((access == FG_WRITE) ? IMA_WR_EN_BIT : 0) |
+ (burst ? MEM_ACS_BURST_BIT : 0);
+
+ rc = fg_masked_write(chip, MEM_IF_IMA_CTL(chip), IMA_CTL_MASK,
+ intf_ctl);
+ if (rc < 0) {
+ pr_err("failed to write to 0x%04x, rc=%d\n",
+ MEM_IF_IMA_CTL(chip), rc);
+ return -EIO;
+ }
+
+ return rc;
+}
+
+static int fg_run_iacs_clear_sequence(struct fg_chip *chip)
+{
+ u8 tmp;
+ int rc;
+
+ /*
+ * Values to write for running IACS clear sequence comes from
+ * hardware documentation.
+ */
+ rc = fg_masked_write(chip, MEM_IF_IMA_CFG(chip), IACS_CLR_BIT,
+ IACS_CLR_BIT);
+ if (rc < 0) {
+ pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_IMA_CFG(chip),
+ rc);
+ return rc;
+ }
+
+ tmp = 0x4;
+ rc = fg_write(chip, MEM_IF_ADDR_MSB(chip), &tmp, 1);
+ if (rc < 0) {
+ pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_ADDR_LSB(chip),
+ rc);
+ return rc;
+ }
+
+ tmp = 0x0;
+ rc = fg_write(chip, MEM_IF_WR_DATA3(chip), &tmp, 1);
+ if (rc < 0) {
+ pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_WR_DATA3(chip),
+ rc);
+ return rc;
+ }
+
+ rc = fg_read(chip, MEM_IF_RD_DATA3(chip), &tmp, 1);
+ if (rc < 0) {
+ pr_err("failed to read 0x%04x, rc=%d\n", MEM_IF_RD_DATA3(chip),
+ rc);
+ return rc;
+ }
+
+ rc = fg_masked_write(chip, MEM_IF_IMA_CFG(chip), IACS_CLR_BIT, 0);
+ if (rc < 0) {
+ pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_IMA_CFG(chip),
+ rc);
+ return rc;
+ }
+
+ fg_dbg(chip, FG_SRAM_READ | FG_SRAM_WRITE, "IACS clear sequence complete\n");
+ return rc;
+}
+
+static int fg_check_for_ima_errors(struct fg_chip *chip)
+{
+ int rc = 0;
+ u8 err_sts, exp_sts = 0, hw_sts = 0;
+
+ rc = fg_read(chip, MEM_IF_IMA_ERR_STS(chip), &err_sts, 1);
+ if (rc < 0) {
+ pr_err("failed to read ima_err_sts rc=%d\n", rc);
+ return rc;
+ }
+
+ if (err_sts & (ADDR_STBL_ERR_BIT | WR_ACS_ERR_BIT | RD_ACS_ERR_BIT)) {
+ rc = fg_read(chip, MEM_IF_IMA_EXP_STS(chip), &exp_sts, 1);
+ if (rc < 0) {
+ pr_err("failed to read ima_exp_sts rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = fg_read(chip, MEM_IF_IMA_HW_STS(chip), &hw_sts, 1);
+ if (rc < 0) {
+ pr_err("failed to read ima_hw_sts rc=%d\n", rc);
+ return rc;
+ }
+
+ pr_err("ima_err_sts=%x ima_exp_sts=%x ima_hw_sts=%x\n",
+ err_sts, exp_sts, hw_sts);
+
+ /* clear the error */
+ rc = fg_run_iacs_clear_sequence(chip);
+ if (rc < 0) {
+ pr_err("failed to run iacs clear sequence rc=%d\n", rc);
+ return rc;
+ }
+
+ /* Retry again as there was an error in the transaction */
+ return -EAGAIN;
+ }
+
+ return rc;
+}
+
+static int fg_check_iacs_ready(struct fg_chip *chip)
+{
+ int rc = 0, timeout = 250;
+ u8 ima_opr_sts = 0;
+
+ /*
+ * Additional delay to make sure IACS ready bit is set after
+ * Read/Write operation.
+ */
+
+ usleep_range(30, 35);
+ while (1) {
+ rc = fg_read(chip, MEM_IF_IMA_OPR_STS(chip), &ima_opr_sts, 1);
+ if (rc < 0) {
+ pr_err("failed to read 0x%04x, rc=%d\n",
+ MEM_IF_IMA_OPR_STS(chip), rc);
+ return rc;
+ }
+
+ if (ima_opr_sts & IACS_RDY_BIT)
+ break;
+
+ if (!(--timeout))
+ break;
+
+ /* delay for iacs_ready to be asserted */
+ usleep_range(5000, 7000);
+ }
+
+ if (!timeout) {
+ pr_err("IACS_RDY not set\n");
+
+ rc = fg_check_for_ima_errors(chip);
+ if (rc < 0) {
+ pr_err("Failed to check for ima errors rc=%d\n", rc);
+ return rc;
+ }
+
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int __fg_interleaved_mem_write(struct fg_chip *chip, u16 address,
+ int offset, u8 *val, int len)
+{
+ int rc = 0, i;
+ u8 *ptr = val, byte_enable = 0, num_bytes = 0;
+
+ fg_dbg(chip, FG_SRAM_WRITE, "length %d addr=%02X offset=%d\n", len,
+ address, offset);
+
+ while (len > 0) {
+ num_bytes = (offset + len) > BYTES_PER_SRAM_WORD ?
+ (BYTES_PER_SRAM_WORD - offset) : len;
+
+ /* write to byte_enable */
+ for (i = offset; i < (offset + num_bytes); i++)
+ byte_enable |= BIT(i);
+
+ rc = fg_write(chip, MEM_IF_IMA_BYTE_EN(chip), &byte_enable, 1);
+ if (rc < 0) {
+ pr_err("Unable to write to byte_en_reg rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /* write data */
+ rc = fg_write(chip, MEM_IF_WR_DATA0(chip) + offset, ptr,
+ num_bytes);
+ if (rc < 0) {
+ pr_err("failed to write to 0x%04x, rc=%d\n",
+ MEM_IF_WR_DATA0(chip) + offset, rc);
+ return rc;
+ }
+
+ /*
+ * The last-byte WR_DATA3 starts the write transaction.
+ * Write a dummy value to WR_DATA3 if it does not have
+ * valid data. This dummy data is not written to the
+ * SRAM as byte_en for WR_DATA3 is not set.
+ */
+ if (!(byte_enable & BIT(3))) {
+ u8 dummy_byte = 0x0;
+
+ rc = fg_write(chip, MEM_IF_WR_DATA3(chip), &dummy_byte,
+ 1);
+ if (rc < 0) {
+ pr_err("failed to write dummy-data to WR_DATA3 rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ /* check for error condition */
+ rc = fg_check_for_ima_errors(chip);
+ if (rc < 0) {
+ pr_err("Failed to check for ima errors rc=%d\n", rc);
+ return rc;
+ }
+
+ ptr += num_bytes;
+ len -= num_bytes;
+ offset = byte_enable = 0;
+
+ rc = fg_check_iacs_ready(chip);
+ if (rc < 0) {
+ pr_debug("IACS_RDY failed rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+static int __fg_interleaved_mem_read(struct fg_chip *chip, u16 address,
+ int offset, u8 *val, int len)
+{
+ int rc = 0, total_len;
+ u8 *rd_data = val, num_bytes;
+ char str[DEBUG_PRINT_BUFFER_SIZE];
+
+ fg_dbg(chip, FG_SRAM_READ, "length %d addr=%02X\n", len, address);
+
+ total_len = len;
+ while (len > 0) {
+ num_bytes = (offset + len) > BYTES_PER_SRAM_WORD ?
+ (BYTES_PER_SRAM_WORD - offset) : len;
+ rc = fg_read(chip, MEM_IF_RD_DATA0(chip) + offset, rd_data,
+ num_bytes);
+ if (rc < 0) {
+ pr_err("failed to read 0x%04x, rc=%d\n",
+ MEM_IF_RD_DATA0(chip) + offset, rc);
+ return rc;
+ }
+
+ rd_data += num_bytes;
+ len -= num_bytes;
+ offset = 0;
+
+ /* check for error condition */
+ rc = fg_check_for_ima_errors(chip);
+ if (rc < 0) {
+ pr_err("Failed to check for ima errors rc=%d\n", rc);
+ return rc;
+ }
+
+ if (len && len < BYTES_PER_SRAM_WORD) {
+ /*
+ * Move to single mode. Changing address is not
+ * required here as it must be in burst mode. Address
+ * will get incremented internally by FG HW once the MSB
+ * of RD_DATA is read.
+ */
+ rc = fg_config_access_mode(chip, FG_READ, 0);
+ if (rc < 0) {
+ pr_err("failed to move to single mode rc=%d\n",
+ rc);
+ return -EIO;
+ }
+ }
+
+ rc = fg_check_iacs_ready(chip);
+ if (rc < 0) {
+ pr_debug("IACS_RDY failed rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (*chip->debug_mask & FG_SRAM_READ) {
+ fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, total_len);
+ pr_info("data read: %s\n", str);
+ }
+
+ return rc;
+}
+
+static int fg_get_mem_access_status(struct fg_chip *chip, bool *status)
+{
+ int rc;
+ u8 mem_if_sts;
+
+ rc = fg_read(chip, MEM_IF_MEM_INTF_CFG(chip), &mem_if_sts, 1);
+ if (rc < 0) {
+ pr_err("failed to read rif_mem status rc=%d\n", rc);
+ return rc;
+ }
+
+ *status = mem_if_sts & MEM_ACCESS_REQ_BIT;
+ return 0;
+}
+
+static bool is_mem_access_available(struct fg_chip *chip, int access)
+{
+ bool rif_mem_sts = true;
+ int rc, time_count = 0;
+
+ while (1) {
+ rc = fg_get_mem_access_status(chip, &rif_mem_sts);
+ if (rc < 0)
+ return rc;
+
+ /* This is an inverting logic */
+ if (!rif_mem_sts)
+ break;
+
+ fg_dbg(chip, FG_SRAM_READ | FG_SRAM_WRITE, "MEM_ACCESS_REQ is not clear yet for IMA_%s\n",
+ access ? "write" : "read");
+
+ /*
+ * Try this no more than 4 times. If MEM_ACCESS_REQ is not
+ * clear, then return an error instead of waiting for it again.
+ */
+ if (time_count > 4) {
+ pr_err("Tried 4 times(~16ms) polling MEM_ACCESS_REQ\n");
+ return false;
+ }
+
+ /* Wait for 4ms before reading MEM_ACCESS_REQ again */
+ usleep_range(4000, 4100);
+ time_count++;
+ }
+ return true;
+}
+
+static int fg_interleaved_mem_config(struct fg_chip *chip, u8 *val,
+ u16 address, int offset, int len, bool access)
+{
+ int rc = 0;
+
+ if (!is_mem_access_available(chip, access))
+ return -EBUSY;
+
+ /* configure for IMA access */
+ rc = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip),
+ MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT,
+ MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT);
+ if (rc < 0) {
+ pr_err("failed to set ima_req_access bit rc=%d\n", rc);
+ return rc;
+ }
+
+ /* configure for the read/write, single/burst mode */
+ rc = fg_config_access_mode(chip, access, (offset + len) > 4);
+ if (rc < 0) {
+ pr_err("failed to set memory access rc = %d\n", rc);
+ return rc;
+ }
+
+ rc = fg_check_iacs_ready(chip);
+ if (rc < 0) {
+ pr_err_ratelimited("IACS_RDY failed rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = fg_set_address(chip, address);
+ if (rc < 0) {
+ pr_err("failed to set address rc = %d\n", rc);
+ return rc;
+ }
+
+ if (access == FG_READ) {
+ rc = fg_check_iacs_ready(chip);
+ if (rc < 0) {
+ pr_debug("IACS_RDY failed rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+static int fg_get_beat_count(struct fg_chip *chip, u8 *count)
+{
+ int rc;
+
+ rc = fg_read(chip, MEM_IF_FG_BEAT_COUNT(chip), count, 1);
+ *count &= BEAT_COUNT_MASK;
+ return rc;
+}
+
+int fg_interleaved_mem_read(struct fg_chip *chip, u16 address, u8 offset,
+ u8 *val, int len)
+{
+ int rc = 0;
+ u8 start_beat_count, end_beat_count, count = 0;
+ bool retry_once = false;
+
+ if (offset > 3) {
+ pr_err("offset too large %d\n", offset);
+ return -EINVAL;
+ }
+
+retry:
+ rc = fg_interleaved_mem_config(chip, val, address, offset, len,
+ FG_READ);
+ if (rc < 0) {
+ pr_err("failed to configure SRAM for IMA rc = %d\n", rc);
+ goto out;
+ }
+
+ /* read the start beat count */
+ rc = fg_get_beat_count(chip, &start_beat_count);
+ if (rc < 0) {
+ pr_err("failed to read beat count rc=%d\n", rc);
+ goto out;
+ }
+
+ /* read data */
+ rc = __fg_interleaved_mem_read(chip, address, offset, val, len);
+ if (rc < 0) {
+ if ((rc == -EAGAIN) && (count < RETRY_COUNT)) {
+ count++;
+ pr_err("IMA access failed retry_count = %d\n", count);
+ goto retry;
+ }
+ pr_err("failed to read SRAM address rc = %d\n", rc);
+ goto out;
+ }
+
+ /* read the end beat count */
+ rc = fg_get_beat_count(chip, &end_beat_count);
+ if (rc < 0) {
+ pr_err("failed to read beat count rc=%d\n", rc);
+ goto out;
+ }
+
+ fg_dbg(chip, FG_SRAM_READ, "Start beat_count = %x End beat_count = %x\n",
+ start_beat_count, end_beat_count);
+
+ if (start_beat_count != end_beat_count && !retry_once) {
+ fg_dbg(chip, FG_SRAM_READ, "Beat count(%d/%d) do not match - retry transaction\n",
+ start_beat_count, end_beat_count);
+ retry_once = true;
+ }
+out:
+ /* Release IMA access */
+ rc = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip),
+ MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT, 0);
+ if (rc < 0) {
+ pr_err("failed to reset IMA access bit rc = %d\n", rc);
+ return rc;
+ }
+
+ if (retry_once)
+ goto retry;
+
+ return rc;
+}
+
+int fg_interleaved_mem_write(struct fg_chip *chip, u16 address, u8 offset,
+ u8 *val, int len, bool atomic_access)
+{
+ int rc = 0;
+ u8 start_beat_count, end_beat_count, count = 0;
+
+ if (offset > 3) {
+ pr_err("offset too large %d\n", offset);
+ return -EINVAL;
+ }
+
+retry:
+ rc = fg_interleaved_mem_config(chip, val, address, offset, len,
+ FG_WRITE);
+ if (rc < 0) {
+ pr_err("failed to configure SRAM for IMA rc = %d\n", rc);
+ goto out;
+ }
+
+ /* read the start beat count */
+ rc = fg_get_beat_count(chip, &start_beat_count);
+ if (rc < 0) {
+ pr_err("failed to read beat count rc=%d\n", rc);
+ goto out;
+ }
+
+ /* write data */
+ rc = __fg_interleaved_mem_write(chip, address, offset, val, len);
+ if (rc < 0) {
+ if ((rc == -EAGAIN) && (count < RETRY_COUNT)) {
+ count++;
+ pr_err("IMA access failed retry_count = %d\n", count);
+ goto retry;
+ }
+ pr_err("failed to write SRAM address rc = %d\n", rc);
+ goto out;
+ }
+
+ /* read the end beat count */
+ rc = fg_get_beat_count(chip, &end_beat_count);
+ if (rc < 0) {
+ pr_err("failed to read beat count rc=%d\n", rc);
+ goto out;
+ }
+
+ if (atomic_access && start_beat_count != end_beat_count)
+ pr_err("Start beat_count = %x End beat_count = %x\n",
+ start_beat_count, end_beat_count);
+out:
+ /* Release IMA access */
+ rc = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip),
+ MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT, 0);
+ if (rc < 0)
+ pr_err("failed to reset IMA access bit rc = %d\n", rc);
+
+ return rc;
+}
+
+int fg_ima_init(struct fg_chip *chip)
+{
+ int rc;
+
+ /*
+ * Change the FG_MEM_INT interrupt to track IACS_READY
+ * condition instead of end-of-transaction. This makes sure
+ * that the next transaction starts only after the hw is ready.
+ */
+ rc = fg_masked_write(chip, MEM_IF_IMA_CFG(chip), IACS_INTR_SRC_SLCT_BIT,
+ IACS_INTR_SRC_SLCT_BIT);
+ if (rc < 0) {
+ pr_err("failed to configure interrupt source %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
diff --git a/drivers/power/qcom-charger/fg-reg.h b/drivers/power/qcom-charger/fg-reg.h
new file mode 100644
index 000000000000..9d5874340a8e
--- /dev/null
+++ b/drivers/power/qcom-charger/fg-reg.h
@@ -0,0 +1,299 @@
+/* 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.
+ */
+
+#ifndef __FG_REG_H__
+#define __FG_REG_H__
+
+/* FG_BATT_SOC register definitions */
+#define BATT_SOC_FG_ALG_STS(chip) (chip->batt_soc_base + 0x06)
+#define BATT_SOC_FG_ALG_AUX_STS0(chip) (chip->batt_soc_base + 0x07)
+#define BATT_SOC_SLEEP_SHUTDOWN_STS(chip) (chip->batt_soc_base + 0x08)
+#define BATT_SOC_FG_MONOTONIC_SOC(chip) (chip->batt_soc_base + 0x09)
+#define BATT_SOC_FG_MONOTONIC_SOC_CP(chip) (chip->batt_soc_base + 0x0A)
+#define BATT_SOC_INT_RT_STS(chip) (chip->batt_soc_base + 0x10)
+#define BATT_SOC_EN_CTL(chip) (chip->batt_soc_base + 0x46)
+#define BATT_SOC_RESTART(chip) (chip->batt_soc_base + 0x48)
+#define BATT_SOC_STS_CLR(chip) (chip->batt_soc_base + 0x4A)
+#define BATT_SOC_LOW_PWR_CFG(chip) (chip->batt_soc_base + 0x52)
+#define BATT_SOC_LOW_PWR_STS(chip) (chip->batt_soc_base + 0x56)
+
+/* BATT_SOC_EN_CTL */
+#define FG_ALGORITHM_EN_BIT BIT(7)
+
+/* BATT_SOC_RESTART */
+#define RESTART_GO_BIT BIT(0)
+
+/* FG_BATT_INFO register definitions */
+#define BATT_INFO_BATT_TEMP_STS(chip) (chip->batt_info_base + 0x06)
+#define BATT_INFO_SYS_BATT(chip) (chip->batt_info_base + 0x07)
+#define BATT_INFO_FG_STS(chip) (chip->batt_info_base + 0x09)
+#define BATT_INFO_INT_RT_STS(chip) (chip->batt_info_base + 0x10)
+#define BATT_INFO_BATT_REM_LATCH(chip) (chip->batt_info_base + 0x4F)
+#define BATT_INFO_BATT_TEMP_LSB(chip) (chip->batt_info_base + 0x50)
+#define BATT_INFO_BATT_TEMP_MSB(chip) (chip->batt_info_base + 0x51)
+#define BATT_INFO_BATT_TEMP_CFG(chip) (chip->batt_info_base + 0x56)
+#define BATT_INFO_BATT_TMPR_INTR(chip) (chip->batt_info_base + 0x59)
+#define BATT_INFO_THERM_C1(chip) (chip->batt_info_base + 0x5C)
+#define BATT_INFO_THERM_C2(chip) (chip->batt_info_base + 0x5D)
+#define BATT_INFO_THERM_C3(chip) (chip->batt_info_base + 0x5E)
+#define BATT_INFO_THERM_HALF_RANGE(chip) (chip->batt_info_base + 0x5F)
+#define BATT_INFO_JEITA_CTLS(chip) (chip->batt_info_base + 0x61)
+#define BATT_INFO_JEITA_TOO_COLD(chip) (chip->batt_info_base + 0x62)
+#define BATT_INFO_JEITA_COLD(chip) (chip->batt_info_base + 0x63)
+#define BATT_INFO_JEITA_HOT(chip) (chip->batt_info_base + 0x64)
+#define BATT_INFO_JEITA_TOO_HOT(chip) (chip->batt_info_base + 0x65)
+
+/* only for v1.1 */
+#define BATT_INFO_ESR_CFG(chip) (chip->batt_info_base + 0x69)
+/* starting from v2.0 */
+#define BATT_INFO_ESR_GENERAL_CFG(chip) (chip->batt_info_base + 0x68)
+#define BATT_INFO_ESR_PULL_DN_CFG(chip) (chip->batt_info_base + 0x69)
+#define BATT_INFO_ESR_FAST_CRG_CFG(chip) (chip->batt_info_base + 0x6A)
+
+#define BATT_INFO_BATT_MISS_CFG(chip) (chip->batt_info_base + 0x6B)
+#define BATT_INFO_WATCHDOG_COUNT(chip) (chip->batt_info_base + 0x70)
+#define BATT_INFO_WATCHDOG_CFG(chip) (chip->batt_info_base + 0x71)
+#define BATT_INFO_IBATT_SENSING_CFG(chip) (chip->batt_info_base + 0x73)
+#define BATT_INFO_QNOVO_CFG(chip) (chip->batt_info_base + 0x74)
+#define BATT_INFO_QNOVO_SCALER(chip) (chip->batt_info_base + 0x75)
+
+/* starting from v2.0 */
+#define BATT_INFO_CRG_SERVICES(chip) (chip->batt_info_base + 0x90)
+
+/* Following LSB/MSB address are for v2.0 and above; v1.1 have them swapped */
+#define BATT_INFO_VBATT_LSB(chip) (chip->batt_info_base + 0xA0)
+#define BATT_INFO_VBATT_MSB(chip) (chip->batt_info_base + 0xA1)
+#define BATT_INFO_IBATT_LSB(chip) (chip->batt_info_base + 0xA2)
+#define BATT_INFO_IBATT_MSB(chip) (chip->batt_info_base + 0xA3)
+#define BATT_INFO_ESR_LSB(chip) (chip->batt_info_base + 0xA4)
+#define BATT_INFO_ESR_MSB(chip) (chip->batt_info_base + 0xA5)
+#define BATT_INFO_VBATT_LSB_CP(chip) (chip->batt_info_base + 0xA6)
+#define BATT_INFO_VBATT_MSB_CP(chip) (chip->batt_info_base + 0xA7)
+#define BATT_INFO_IBATT_LSB_CP(chip) (chip->batt_info_base + 0xA8)
+#define BATT_INFO_IBATT_MSB_CP(chip) (chip->batt_info_base + 0xA9)
+#define BATT_INFO_ESR_LSB_CP(chip) (chip->batt_info_base + 0xAA)
+#define BATT_INFO_ESR_MSB_CP(chip) (chip->batt_info_base + 0xAB)
+#define BATT_INFO_VADC_LSB(chip) (chip->batt_info_base + 0xAC)
+#define BATT_INFO_VADC_MSB(chip) (chip->batt_info_base + 0xAD)
+#define BATT_INFO_IADC_LSB(chip) (chip->batt_info_base + 0xAE)
+#define BATT_INFO_IADC_MSB(chip) (chip->batt_info_base + 0xAF)
+#define BATT_INFO_TM_MISC(chip) (chip->batt_info_base + 0xE5)
+#define BATT_INFO_TM_MISC1(chip) (chip->batt_info_base + 0xE6)
+
+/* BATT_INFO_BATT_TEMP_STS */
+#define JEITA_TOO_HOT_STS_BIT BIT(7)
+#define JEITA_HOT_STS_BIT BIT(6)
+#define JEITA_COLD_STS_BIT BIT(5)
+#define JEITA_TOO_COLD_STS_BIT BIT(4)
+#define BATT_TEMP_DELTA_BIT BIT(1)
+#define BATT_TEMP_AVAIL_BIT BIT(0)
+
+/* BATT_INFO_SYS_BATT */
+#define BATT_REM_LATCH_STS_BIT BIT(4)
+#define BATT_MISSING_HW_BIT BIT(2)
+#define BATT_MISSING_ALG_BIT BIT(1)
+#define BATT_MISSING_CMP_BIT BIT(0)
+
+/* BATT_INFO_FG_STS */
+#define FG_WD_RESET_BIT BIT(7)
+/* This bit is not present in v1.1 */
+#define FG_CRG_TRM_BIT BIT(0)
+
+/* BATT_INFO_INT_RT_STS */
+#define BT_TMPR_DELTA_BIT BIT(6)
+#define WDOG_EXP_BIT BIT(5)
+#define BT_ATTN_BIT BIT(4)
+#define BT_MISS_BIT BIT(3)
+#define ESR_DELTA_BIT BIT(2)
+#define VBT_LOW_BIT BIT(1)
+#define VBT_PRD_DELTA_BIT BIT(0)
+
+/* BATT_INFO_INT_RT_STS */
+#define BATT_REM_LATCH_CLR_BIT BIT(7)
+
+/* BATT_INFO_BATT_TEMP_LSB/MSB */
+#define BATT_TEMP_LSB_MASK GENMASK(7, 0)
+#define BATT_TEMP_MSB_MASK GENMASK(2, 0)
+
+/* BATT_INFO_BATT_TEMP_CFG */
+#define JEITA_TEMP_HYST_MASK GENMASK(5, 4)
+#define JEITA_TEMP_NO_HYST 0x0
+#define JEITA_TEMP_HYST_1C 0x1
+#define JEITA_TEMP_HYST_2C 0x2
+#define JEITA_TEMP_HYST_3C 0x3
+
+/* BATT_INFO_BATT_TMPR_INTR */
+#define CHANGE_THOLD_MASK GENMASK(1, 0)
+#define BTEMP_DELTA_2K 0x0
+#define BTEMP_DELTA_4K 0x1
+#define BTEMP_DELTA_6K 0x2
+#define BTEMP_DELTA_10K 0x3
+
+/* BATT_INFO_THERM_C1/C2/C3 */
+#define BATT_INFO_THERM_COEFF_MASK GENMASK(7, 0)
+
+/* BATT_INFO_THERM_HALF_RANGE */
+#define BATT_INFO_THERM_TEMP_MASK GENMASK(7, 0)
+
+/* BATT_INFO_JEITA_CTLS */
+#define JEITA_STS_CLEAR_BIT BIT(0)
+
+/* BATT_INFO_JEITA_TOO_COLD/COLD/HOT/TOO_HOT */
+#define JEITA_THOLD_MASK GENMASK(7, 0)
+
+/* BATT_INFO_ESR_CFG */
+#define CFG_ACTIVE_PD_MASK GENMASK(2, 1)
+#define CFG_FCC_DEC_MASK GENMASK(4, 3)
+
+/* BATT_INFO_ESR_GENERAL_CFG */
+#define ESR_DEEP_TAPER_EN_BIT BIT(0)
+
+/* BATT_INFO_ESR_PULL_DN_CFG */
+#define ESR_PULL_DOWN_IVAL_MASK GENMASK(3, 2)
+#define ESR_MEAS_CUR_60MA 0x0
+#define ESR_MEAS_CUR_120MA 0x1
+#define ESR_MEAS_CUR_180MA 0x2
+#define ESR_MEAS_CUR_240MA 0x3
+#define ESR_PULL_DOWN_MODE_MASK GENMASK(1, 0)
+#define ESR_NO_PULL_DOWN 0x0
+#define ESR_STATIC_PULL_DOWN 0x1
+#define ESR_CRG_DSC_PULL_DOWN 0x2
+#define ESR_DSC_PULL_DOWN 0x3
+
+/* BATT_INFO_ESR_FAST_CRG_CFG */
+#define ESR_FAST_CRG_IVAL_MASK GENMASK(3, 1)
+#define ESR_FCC_300MA 0x0
+#define ESR_FCC_600MA 0x1
+#define ESR_FCC_1A 0x2
+#define ESR_FCC_2A 0x3
+#define ESR_FCC_3A 0x4
+#define ESR_FCC_4A 0x5
+#define ESR_FCC_5A 0x6
+#define ESR_FCC_6A 0x7
+#define ESR_FAST_CRG_CTL_EN_BIT BIT(0)
+
+/* BATT_INFO_BATT_MISS_CFG */
+#define BM_THERM_TH_MASK GENMASK(5, 4)
+#define RES_TH_0P75_MOHM 0x0
+#define RES_TH_1P00_MOHM 0x1
+#define RES_TH_1P50_MOHM 0x2
+#define RES_TH_3P00_MOHM 0x3
+#define BM_BATT_ID_TH_MASK GENMASK(3, 2)
+#define BM_FROM_THERM_BIT BIT(1)
+#define BM_FROM_BATT_ID_BIT BIT(0)
+
+/* BATT_INFO_WATCHDOG_COUNT */
+#define WATCHDOG_COUNTER GENMASK(7, 0)
+
+/* BATT_INFO_WATCHDOG_CFG */
+#define RESET_CAPABLE_BIT BIT(2)
+#define PET_CTRL_BIT BIT(1)
+#define ENABLE_CTRL_BIT BIT(0)
+
+/* BATT_INFO_IBATT_SENSING_CFG */
+#define ADC_BITSTREAM_INV_BIT BIT(4)
+#define SOURCE_SELECT_MASK GENMASK(1, 0)
+#define SRC_SEL_BATFET 0x0
+#define SRC_SEL_RSENSE 0x1
+#define SRC_SEL_BATFET_SMB 0x2
+#define SRC_SEL_RESERVED 0x3
+
+/* BATT_INFO_QNOVO_CFG */
+#define LD_REG_FORCE_CTL_BIT BIT(2)
+#define LD_REG_CTRL_FORCE_HIGH LD_REG_FORCE_CTL_BIT
+#define LD_REG_CTRL_FORCE_LOW 0
+#define LD_REG_CTRL_BIT BIT(1)
+#define LD_REG_CTRL_REGISTER LD_REG_CTRL_BIT
+#define LD_REG_CTRL_LOGIC 0
+#define BIT_STREAM_CFG_BIT BIT(0)
+
+/* BATT_INFO_QNOVO_SCALER */
+#define QNOVO_SCALER_MASK GENMASK(7, 0)
+
+/* BATT_INFO_CRG_SERVICES */
+#define FG_CRC_TRM_EN_BIT BIT(0)
+
+/* BATT_INFO_VBATT_LSB/MSB */
+#define VBATT_MASK GENMASK(7, 0)
+
+/* BATT_INFO_IBATT_LSB/MSB */
+#define IBATT_MASK GENMASK(7, 0)
+
+/* BATT_INFO_ESR_LSB/MSB */
+#define ESR_LSB_MASK GENMASK(7, 0)
+#define ESR_MSB_MASK GENMASK(5, 0)
+
+/* BATT_INFO_VADC_LSB/MSB */
+#define VADC_LSB_MASK GENMASK(7, 0)
+#define VADC_MSB_MASK GENMASK(6, 0)
+
+/* BATT_INFO_IADC_LSB/MSB */
+#define IADC_LSB_MASK GENMASK(7, 0)
+#define IADC_MSB_MASK GENMASK(6, 0)
+
+/* BATT_INFO_TM_MISC */
+#define FORCE_SEQ_RESP_TOGGLE_BIT BIT(6)
+#define ALG_DIRECT_VALID_DATA_BIT BIT(5)
+#define ALG_DIRECT_MODE_EN_BIT BIT(4)
+#define BATT_VADC_CONV_BIT BIT(3)
+#define BATT_IADC_CONV_BIT BIT(2)
+#define ADC_ENABLE_REG_CTRL_BIT BIT(1)
+#define WDOG_FORCE_EXP_BIT BIT(0)
+/* only for v1.1 */
+#define ESR_PULSE_FORCE_CTRL_BIT BIT(7)
+
+/* BATT_INFO_TM_MISC1 */
+/* for v2.0 and above */
+#define ESR_REQ_CTL_BIT BIT(1)
+#define ESR_REQ_CTL_EN_BIT BIT(0)
+
+/* FG_MEM_IF register and bit definitions */
+#define MEM_IF_MEM_INTF_CFG(chip) ((chip->mem_if_base) + 0x50)
+#define MEM_IF_IMA_CTL(chip) ((chip->mem_if_base) + 0x51)
+#define MEM_IF_IMA_CFG(chip) ((chip->mem_if_base) + 0x52)
+#define MEM_IF_IMA_OPR_STS(chip) ((chip->mem_if_base) + 0x54)
+#define MEM_IF_IMA_EXP_STS(chip) ((chip->mem_if_base) + 0x55)
+#define MEM_IF_IMA_HW_STS(chip) ((chip->mem_if_base) + 0x56)
+#define MEM_IF_FG_BEAT_COUNT(chip) ((chip->mem_if_base) + 0x57)
+#define MEM_IF_IMA_ERR_STS(chip) ((chip->mem_if_base) + 0x5F)
+#define MEM_IF_IMA_BYTE_EN(chip) ((chip->mem_if_base) + 0x60)
+#define MEM_IF_ADDR_LSB(chip) ((chip->mem_if_base) + 0x61)
+#define MEM_IF_ADDR_MSB(chip) ((chip->mem_if_base) + 0x62)
+#define MEM_IF_WR_DATA0(chip) ((chip->mem_if_base) + 0x63)
+#define MEM_IF_WR_DATA3(chip) ((chip->mem_if_base) + 0x66)
+#define MEM_IF_RD_DATA0(chip) ((chip->mem_if_base) + 0x67)
+#define MEM_IF_RD_DATA3(chip) ((chip->mem_if_base) + 0x6A)
+
+/* MEM_IF_MEM_INTF_CFG */
+#define MEM_ACCESS_REQ_BIT BIT(7)
+#define IACS_SLCT_BIT BIT(5)
+
+/* MEM_IF_IMA_CTL */
+#define MEM_ACS_BURST_BIT BIT(7)
+#define IMA_WR_EN_BIT BIT(6)
+#define IMA_CTL_MASK GENMASK(7, 6)
+
+/* MEM_IF_IMA_CFG */
+#define IACS_CLR_BIT BIT(2)
+#define IACS_INTR_SRC_SLCT_BIT BIT(3)
+
+/* MEM_IF_IMA_OPR_STS */
+#define IACS_RDY_BIT BIT(1)
+
+/* MEM_IF_IMA_ERR_STS */
+#define ADDR_STBL_ERR_BIT BIT(7)
+#define WR_ACS_ERR_BIT BIT(6)
+#define RD_ACS_ERR_BIT BIT(5)
+
+/* MEM_IF_FG_BEAT_COUNT */
+#define BEAT_COUNT_MASK GENMASK(3, 0)
+#endif
diff --git a/drivers/power/qcom-charger/fg-util.c b/drivers/power/qcom-charger/fg-util.c
new file mode 100644
index 000000000000..9f2d9973e04b
--- /dev/null
+++ b/drivers/power/qcom-charger/fg-util.c
@@ -0,0 +1,644 @@
+/* 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.
+ */
+
+#define pr_fmt(fmt) "FG: %s: " fmt, __func__
+
+#include "fg-core.h"
+
+static struct fg_dbgfs dbgfs_data = {
+ .help_msg = {
+ .data =
+ "FG Debug-FS support\n"
+ "\n"
+ "Hierarchy schema:\n"
+ "/sys/kernel/debug/fg_sram\n"
+ " /help -- Static help text\n"
+ " /address -- Starting register address for reads or writes\n"
+ " /count -- Number of registers to read (only used for reads)\n"
+ " /data -- Initiates the SRAM read (formatted output)\n"
+ "\n",
+ },
+};
+
+void fill_string(char *str, size_t str_len, u8 *buf, int buf_len)
+{
+ int pos = 0;
+ int i;
+
+ for (i = 0; i < buf_len; i++) {
+ pos += scnprintf(str + pos, str_len - pos, "%02x", buf[i]);
+ if (i < buf_len - 1)
+ pos += scnprintf(str + pos, str_len - pos, " ");
+ }
+}
+
+static inline bool fg_sram_address_valid(u16 address, int len)
+{
+ if (address > FG_SRAM_ADDRESS_MAX)
+ return false;
+
+ if ((address + DIV_ROUND_UP(len, 4)) > FG_SRAM_ADDRESS_MAX + 1)
+ return false;
+
+ return true;
+}
+
+#define SOC_UPDATE_WAIT_MS 1500
+int fg_sram_write(struct fg_chip *chip, u16 address, u8 offset,
+ u8 *val, int len, int flags)
+{
+ int rc = 0;
+ bool tried_again = false;
+ bool atomic_access = false;
+
+ if (!chip)
+ return -ENXIO;
+
+ if (!fg_sram_address_valid(address, len))
+ return -EFAULT;
+
+ vote(chip->awake_votable, SRAM_WRITE, true, 0);
+ mutex_lock(&chip->sram_rw_lock);
+
+ if ((flags & FG_IMA_ATOMIC) && chip->irqs[SOC_UPDATE_IRQ].irq) {
+ /*
+ * This interrupt need to be enabled only when it is
+ * required. It will be kept disabled other times.
+ */
+ enable_irq(chip->irqs[SOC_UPDATE_IRQ].irq);
+ atomic_access = true;
+ } else {
+ flags = FG_IMA_DEFAULT;
+ }
+wait:
+ /*
+ * Atomic access mean waiting upon SOC_UPDATE interrupt from
+ * FG_ALG and do the transaction after that. This is to make
+ * sure that there will be no SOC update happening when an
+ * IMA write is happening. SOC_UPDATE interrupt fires every
+ * FG cycle (~1.47 seconds).
+ */
+ if (atomic_access) {
+ /* Wait for SOC_UPDATE completion */
+ rc = wait_for_completion_interruptible_timeout(
+ &chip->soc_update,
+ msecs_to_jiffies(SOC_UPDATE_WAIT_MS));
+
+ /* If we were interrupted wait again one more time. */
+ if (rc == -ERESTARTSYS && !tried_again) {
+ tried_again = true;
+ goto wait;
+ } else if (rc <= 0) {
+ pr_err("wait for soc_update timed out rc=%d\n", rc);
+ goto out;
+ }
+ }
+
+ rc = fg_interleaved_mem_write(chip, address, offset, val, len,
+ atomic_access);
+ if (rc < 0)
+ pr_err("Error in writing SRAM address 0x%x[%d], rc=%d\n",
+ address, offset, rc);
+out:
+ if (atomic_access)
+ disable_irq_nosync(chip->irqs[SOC_UPDATE_IRQ].irq);
+
+ mutex_unlock(&chip->sram_rw_lock);
+ vote(chip->awake_votable, SRAM_WRITE, false, 0);
+ return rc;
+}
+
+int fg_sram_read(struct fg_chip *chip, u16 address, u8 offset,
+ u8 *val, int len, int flags)
+{
+ int rc = 0;
+
+ if (!chip)
+ return -ENXIO;
+
+ if (!fg_sram_address_valid(address, len))
+ return -EFAULT;
+
+ vote(chip->awake_votable, SRAM_READ, true, 0);
+ mutex_lock(&chip->sram_rw_lock);
+
+ rc = fg_interleaved_mem_read(chip, address, offset, val, len);
+ if (rc < 0)
+ pr_err("Error in reading SRAM address 0x%x[%d], rc=%d\n",
+ address, offset, rc);
+
+ mutex_unlock(&chip->sram_rw_lock);
+ vote(chip->awake_votable, SRAM_READ, false, 0);
+ return rc;
+}
+
+int fg_sram_masked_write(struct fg_chip *chip, u16 address, u8 offset,
+ u8 mask, u8 val, int flags)
+{
+ int rc = 0;
+ u8 buf[4];
+
+ rc = fg_sram_read(chip, address, 0, buf, 4, flags);
+ if (rc < 0) {
+ pr_err("sram read failed: address=%03X, rc=%d\n", address, rc);
+ return rc;
+ }
+
+ buf[offset] &= ~mask;
+ buf[offset] |= val & mask;
+
+ rc = fg_sram_write(chip, address, 0, buf, 4, flags);
+ if (rc < 0) {
+ pr_err("sram write failed: address=%03X, rc=%d\n", address, rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+int fg_read(struct fg_chip *chip, int addr, u8 *val, int len)
+{
+ int rc, i;
+
+ if (!chip || !chip->regmap)
+ return -ENXIO;
+
+ rc = regmap_bulk_read(chip->regmap, addr, val, len);
+
+ if (rc < 0) {
+ dev_err(chip->dev, "regmap_read failed for address %04x rc=%d\n",
+ addr, rc);
+ return rc;
+ }
+
+ if (*chip->debug_mask & FG_BUS_READ) {
+ pr_info("length %d addr=%04x\n", len, addr);
+ for (i = 0; i < len; i++)
+ pr_info("val[%d]: %02x\n", i, val[i]);
+ }
+
+ return 0;
+}
+
+int fg_write(struct fg_chip *chip, int addr, u8 *val, int len)
+{
+ int rc, i;
+ bool sec_access = false;
+
+ if (!chip || !chip->regmap)
+ return -ENXIO;
+
+ mutex_lock(&chip->bus_lock);
+ sec_access = (addr & 0xFF00) > 0xD0;
+ if (sec_access) {
+ rc = regmap_write(chip->regmap, (addr & 0xFF00) | 0xD0, 0xA5);
+ if (rc < 0) {
+ dev_err(chip->dev, "regmap_write failed for address %x rc=%d\n",
+ addr, rc);
+ goto out;
+ }
+ }
+
+ if (len > 1)
+ rc = regmap_bulk_write(chip->regmap, addr, val, len);
+ else
+ rc = regmap_write(chip->regmap, addr, *val);
+
+ if (rc < 0) {
+ dev_err(chip->dev, "regmap_write failed for address %04x rc=%d\n",
+ addr, rc);
+ goto out;
+ }
+
+ if (*chip->debug_mask & FG_BUS_WRITE) {
+ pr_info("length %d addr=%04x\n", len, addr);
+ for (i = 0; i < len; i++)
+ pr_info("val[%d]: %02x\n", i, val[i]);
+ }
+out:
+ mutex_unlock(&chip->bus_lock);
+ return rc;
+}
+
+int fg_masked_write(struct fg_chip *chip, int addr, u8 mask, u8 val)
+{
+ int rc;
+ bool sec_access = false;
+
+ if (!chip || !chip->regmap)
+ return -ENXIO;
+
+ mutex_lock(&chip->bus_lock);
+ sec_access = (addr & 0xFF00) > 0xD0;
+ if (sec_access) {
+ rc = regmap_write(chip->regmap, (addr & 0xFF00) | 0xD0, 0xA5);
+ if (rc < 0) {
+ dev_err(chip->dev, "regmap_write failed for address %x rc=%d\n",
+ addr, rc);
+ goto out;
+ }
+ }
+
+ rc = regmap_update_bits(chip->regmap, addr, mask, val);
+ if (rc < 0) {
+ dev_err(chip->dev, "regmap_update_bits failed for address %04x rc=%d\n",
+ addr, rc);
+ goto out;
+ }
+
+ fg_dbg(chip, FG_BUS_WRITE, "addr=%04x mask: %02x val: %02x\n", addr,
+ mask, val);
+out:
+ mutex_unlock(&chip->bus_lock);
+ return rc;
+}
+
+int64_t twos_compliment_extend(int64_t val, int sign_bit_pos)
+{
+ int i, nbytes = DIV_ROUND_UP(sign_bit_pos, 8);
+ int64_t mask, val_out;
+
+ val_out = val;
+ mask = 1 << sign_bit_pos;
+ if (val & mask) {
+ for (i = 8; i > nbytes; i--) {
+ mask = 0xFFLL << ((i - 1) * 8);
+ val_out |= mask;
+ }
+
+ if ((nbytes * 8) - 1 > sign_bit_pos) {
+ mask = 1 << sign_bit_pos;
+ for (i = 1; i <= (nbytes * 8) - sign_bit_pos; i++)
+ val_out |= mask << i;
+ }
+ }
+
+ pr_debug("nbytes: %d val: %llx val_out: %llx\n", nbytes, val, val_out);
+ return val_out;
+}
+
+/* All the debugfs related functions are defined below */
+static int fg_sram_dfs_open(struct inode *inode, struct file *file)
+{
+ struct fg_log_buffer *log;
+ struct fg_trans *trans;
+ u8 *data_buf;
+
+ size_t logbufsize = SZ_4K;
+ size_t databufsize = SZ_4K;
+
+ if (!dbgfs_data.chip) {
+ pr_err("Not initialized data\n");
+ return -EINVAL;
+ }
+
+ /* Per file "transaction" data */
+ trans = devm_kzalloc(dbgfs_data.chip->dev, sizeof(*trans), GFP_KERNEL);
+ if (!trans)
+ return -ENOMEM;
+
+ /* Allocate log buffer */
+ log = devm_kzalloc(dbgfs_data.chip->dev, logbufsize, GFP_KERNEL);
+ if (!log)
+ return -ENOMEM;
+
+ log->rpos = 0;
+ log->wpos = 0;
+ log->len = logbufsize - sizeof(*log);
+
+ /* Allocate data buffer */
+ data_buf = devm_kzalloc(dbgfs_data.chip->dev, databufsize, GFP_KERNEL);
+ if (!data_buf)
+ return -ENOMEM;
+
+ trans->log = log;
+ trans->data = data_buf;
+ trans->cnt = dbgfs_data.cnt;
+ trans->addr = dbgfs_data.addr;
+ trans->chip = dbgfs_data.chip;
+ trans->offset = trans->addr;
+
+ file->private_data = trans;
+ return 0;
+}
+
+static int fg_sram_dfs_close(struct inode *inode, struct file *file)
+{
+ struct fg_trans *trans = file->private_data;
+
+ if (trans && trans->log && trans->data) {
+ file->private_data = NULL;
+ devm_kfree(trans->chip->dev, trans->log);
+ devm_kfree(trans->chip->dev, trans->data);
+ devm_kfree(trans->chip->dev, trans);
+ }
+
+ return 0;
+}
+
+/**
+ * print_to_log: format a string and place into the log buffer
+ * @log: The log buffer to place the result into.
+ * @fmt: The format string to use.
+ * @...: The arguments for the format string.
+ *
+ * The return value is the number of characters written to @log buffer
+ * not including the trailing '\0'.
+ */
+static int print_to_log(struct fg_log_buffer *log, const char *fmt, ...)
+{
+ va_list args;
+ int cnt;
+ char *buf = &log->data[log->wpos];
+ size_t size = log->len - log->wpos;
+
+ va_start(args, fmt);
+ cnt = vscnprintf(buf, size, fmt, args);
+ va_end(args);
+
+ log->wpos += cnt;
+ return cnt;
+}
+
+/**
+ * write_next_line_to_log: Writes a single "line" of data into the log buffer
+ * @trans: Pointer to SRAM transaction data.
+ * @offset: SRAM address offset to start reading from.
+ * @pcnt: Pointer to 'cnt' variable. Indicates the number of bytes to read.
+ *
+ * The 'offset' is a 12-bit SRAM address.
+ *
+ * On a successful read, the pcnt is decremented by the number of data
+ * bytes read from the SRAM. When the cnt reaches 0, all requested bytes have
+ * been read.
+ */
+static int write_next_line_to_log(struct fg_trans *trans, int offset,
+ size_t *pcnt)
+{
+ int i;
+ u8 data[ITEMS_PER_LINE];
+ u16 address;
+ struct fg_log_buffer *log = trans->log;
+ int cnt = 0;
+ int items_to_read = min(ARRAY_SIZE(data), *pcnt);
+ int items_to_log = min(ITEMS_PER_LINE, items_to_read);
+
+ /* Buffer needs enough space for an entire line */
+ if ((log->len - log->wpos) < MAX_LINE_LENGTH)
+ 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. */
+ address = trans->addr + ((offset - trans->addr) / ITEMS_PER_LINE);
+ cnt = print_to_log(log, "%3.3d ", address & 0xfff);
+ if (cnt == 0)
+ goto done;
+
+ /* Log the data items */
+ for (i = 0; i < items_to_log; ++i) {
+ cnt = print_to_log(log, "%2.2X ", data[i]);
+ if (cnt == 0)
+ goto done;
+ }
+
+ /* If the last character was a space, then replace it with a newline */
+ if (log->wpos > 0 && log->data[log->wpos - 1] == ' ')
+ log->data[log->wpos - 1] = '\n';
+
+done:
+ return cnt;
+}
+
+/**
+ * get_log_data - reads data from SRAM and saves to the log buffer
+ * @trans: Pointer to SRAM transaction data.
+ *
+ * Returns the number of "items" read or SPMI error code for read failures.
+ */
+static int get_log_data(struct fg_trans *trans)
+{
+ int cnt, rc;
+ int last_cnt;
+ int items_read;
+ int total_items_read = 0;
+ u32 offset = trans->offset;
+ size_t item_cnt = trans->cnt;
+ struct fg_log_buffer *log = trans->log;
+
+ if (item_cnt == 0)
+ return 0;
+
+ if (item_cnt > SZ_4K) {
+ pr_err("Reading too many bytes\n");
+ return -EINVAL;
+ }
+
+ pr_debug("addr: %d offset: %d count: %d\n", trans->addr, trans->offset,
+ trans->cnt);
+ rc = fg_sram_read(trans->chip, trans->addr, 0,
+ trans->data, trans->cnt, 0);
+ if (rc < 0) {
+ pr_err("SRAM read failed: rc = %d\n", rc);
+ return rc;
+ }
+ /* Reset the log buffer 'pointers' */
+ log->wpos = log->rpos = 0;
+
+ /* Keep reading data until the log is full */
+ do {
+ last_cnt = item_cnt;
+ cnt = write_next_line_to_log(trans, offset, &item_cnt);
+ items_read = last_cnt - item_cnt;
+ offset += items_read;
+ total_items_read += items_read;
+ } while (cnt && item_cnt > 0);
+
+ /* Adjust the transaction offset and count */
+ trans->cnt = item_cnt;
+ trans->offset += total_items_read;
+
+ return total_items_read;
+}
+
+/**
+ * fg_sram_dfs_reg_read: reads value(s) from SRAM and fills user's buffer a
+ * byte array (coded as string)
+ * @file: file pointer
+ * @buf: where to put the result
+ * @count: maximum space available in @buf
+ * @ppos: starting position
+ * @return number of user bytes read, or negative error value
+ */
+static ssize_t fg_sram_dfs_reg_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct fg_trans *trans = file->private_data;
+ struct fg_log_buffer *log = trans->log;
+ size_t ret;
+ size_t len;
+
+ /* Is the the log buffer empty */
+ if (log->rpos >= log->wpos) {
+ if (get_log_data(trans) <= 0)
+ return 0;
+ }
+
+ len = min(count, log->wpos - log->rpos);
+
+ ret = copy_to_user(buf, &log->data[log->rpos], len);
+ if (ret == len) {
+ pr_err("error copy sram register values to user\n");
+ return -EFAULT;
+ }
+
+ /* 'ret' is the number of bytes not copied */
+ len -= ret;
+
+ *ppos += len;
+ log->rpos += len;
+ return len;
+}
+
+/**
+ * fg_sram_dfs_reg_write: write user's byte array (coded as string) to SRAM.
+ * @file: file pointer
+ * @buf: user data to be written.
+ * @count: maximum space available in @buf
+ * @ppos: starting position
+ * @return number of user byte written, or negative error value
+ */
+static ssize_t fg_sram_dfs_reg_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int bytes_read;
+ int data;
+ int pos = 0;
+ int cnt = 0;
+ u8 *values;
+ char *kbuf;
+ size_t ret = 0;
+ struct fg_trans *trans = file->private_data;
+ u32 address = trans->addr;
+
+ /* Make a copy of the user data */
+ kbuf = kmalloc(count + 1, GFP_KERNEL);
+ if (!kbuf)
+ return -ENOMEM;
+
+ ret = copy_from_user(kbuf, buf, count);
+ if (ret == count) {
+ pr_err("failed to copy data from user\n");
+ ret = -EFAULT;
+ goto free_buf;
+ }
+
+ count -= ret;
+ *ppos += count;
+ kbuf[count] = '\0';
+
+ /* Override the text buffer with the raw data */
+ values = kbuf;
+
+ /* Parse the data in the buffer. It should be a string of numbers */
+ while ((pos < count) &&
+ sscanf(kbuf + pos, "%i%n", &data, &bytes_read) == 1) {
+ pos += bytes_read;
+ values[cnt++] = data & 0xff;
+ }
+
+ if (!cnt)
+ goto free_buf;
+
+ pr_debug("address %d, count %d\n", address, cnt);
+ /* Perform the write(s) */
+
+ ret = fg_sram_write(trans->chip, address, 0, values, cnt, 0);
+ if (ret) {
+ pr_err("SRAM write failed, err = %zu\n", ret);
+ } else {
+ ret = count;
+ trans->offset += cnt > 4 ? 4 : cnt;
+ }
+
+free_buf:
+ kfree(kbuf);
+ return ret;
+}
+
+static const struct file_operations fg_sram_dfs_reg_fops = {
+ .open = fg_sram_dfs_open,
+ .release = fg_sram_dfs_close,
+ .read = fg_sram_dfs_reg_read,
+ .write = fg_sram_dfs_reg_write,
+};
+
+/*
+ * fg_debugfs_create: adds new fg_sram debugfs entry
+ * @return zero on success
+ */
+int fg_sram_debugfs_create(struct fg_chip *chip)
+{
+ struct dentry *root;
+ struct dentry *file;
+ mode_t dfs_mode = S_IRUSR | S_IWUSR;
+
+ pr_debug("Creating FG_SRAM debugfs file-system\n");
+ root = debugfs_create_dir("fg_sram", NULL);
+ if (IS_ERR_OR_NULL(root)) {
+ pr_err("Error creating top level directory err:%ld",
+ (long)root);
+ if (PTR_ERR(root) == -ENODEV)
+ pr_err("debugfs is not enabled in the kernel");
+ return -ENODEV;
+ }
+
+ if (!root)
+ return -ENOENT;
+
+ dbgfs_data.help_msg.size = strlen(dbgfs_data.help_msg.data);
+ file = debugfs_create_blob("help", S_IRUGO, root, &dbgfs_data.help_msg);
+ if (!file) {
+ pr_err("error creating help entry\n");
+ goto err_remove_fs;
+ }
+
+ dbgfs_data.chip = chip;
+
+ file = debugfs_create_u32("count", dfs_mode, root, &(dbgfs_data.cnt));
+ if (!file) {
+ pr_err("error creating 'count' entry\n");
+ goto err_remove_fs;
+ }
+
+ file = debugfs_create_x32("address", dfs_mode,
+ root, &(dbgfs_data.addr));
+ if (!file) {
+ pr_err("error creating 'address' entry\n");
+ goto err_remove_fs;
+ }
+
+ file = debugfs_create_file("data", dfs_mode, root, &dbgfs_data,
+ &fg_sram_dfs_reg_fops);
+ if (!file) {
+ pr_err("error creating 'data' entry\n");
+ goto err_remove_fs;
+ }
+
+ chip->dentry = root;
+ return 0;
+
+err_remove_fs:
+ debugfs_remove_recursive(root);
+ return -ENOMEM;
+}
diff --git a/drivers/power/qcom-charger/qpnp-fg-gen3.c b/drivers/power/qcom-charger/qpnp-fg-gen3.c
new file mode 100644
index 000000000000..2adc07ddc5a0
--- /dev/null
+++ b/drivers/power/qcom-charger/qpnp-fg-gen3.c
@@ -0,0 +1,1495 @@
+/* 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.
+ */
+
+#define pr_fmt(fmt) "FG: %s: " fmt, __func__
+
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/of_batterydata.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/iio/consumer.h>
+#include <linux/qpnp/qpnp-revid.h>
+#include "fg-core.h"
+#include "fg-reg.h"
+
+#define FG_GEN3_DEV_NAME "qcom,fg-gen3"
+
+#define PERPH_SUBTYPE_REG 0x05
+#define FG_BATT_SOC_PMICOBALT 0x10
+#define FG_BATT_INFO_PMICOBALT 0x11
+#define FG_MEM_INFO_PMICOBALT 0x0D
+
+/* SRAM address and offset in ascending order */
+#define CUTOFF_VOLT_WORD 5
+#define CUTOFF_VOLT_OFFSET 0
+#define SYS_TERM_CURR_WORD 6
+#define SYS_TERM_CURR_OFFSET 0
+#define DELTA_SOC_THR_WORD 12
+#define DELTA_SOC_THR_OFFSET 3
+#define RECHARGE_SOC_THR_WORD 14
+#define RECHARGE_SOC_THR_OFFSET 0
+#define CHG_TERM_CURR_WORD 14
+#define CHG_TERM_CURR_OFFSET 1
+#define EMPTY_VOLT_WORD 15
+#define EMPTY_VOLT_OFFSET 0
+#define VBATT_LOW_WORD 15
+#define VBATT_LOW_OFFSET 1
+#define PROFILE_LOAD_WORD 24
+#define PROFILE_LOAD_OFFSET 0
+#define NOM_CAP_WORD 58
+#define NOM_CAP_OFFSET 0
+#define PROFILE_INTEGRITY_WORD 79
+#define PROFILE_INTEGRITY_OFFSET 3
+#define BATT_SOC_WORD 91
+#define BATT_SOC_OFFSET 0
+#define MONOTONIC_SOC_WORD 94
+#define MONOTONIC_SOC_OFFSET 2
+#define VOLTAGE_PRED_WORD 97
+#define VOLTAGE_PRED_OFFSET 0
+#define OCV_WORD 97
+#define OCV_OFFSET 2
+#define RSLOW_WORD 101
+#define RSLOW_OFFSET 0
+#define LAST_BATT_SOC_WORD 119
+#define LAST_BATT_SOC_OFFSET 0
+#define LAST_MONOTONIC_SOC_WORD 119
+#define LAST_MONOTONIC_SOC_OFFSET 2
+
+static int fg_decode_value_16b(struct fg_sram_param *sp,
+ enum fg_sram_param_id id, int val);
+static int fg_decode_default(struct fg_sram_param *sp,
+ enum fg_sram_param_id id, int val);
+static void fg_encode_voltage_16b(struct fg_sram_param *sp,
+ enum fg_sram_param_id id, int val, u8 *buf);
+static void fg_encode_voltage_8b(struct fg_sram_param *sp,
+ enum fg_sram_param_id id, int val, u8 *buf);
+static void fg_encode_current(struct fg_sram_param *sp,
+ enum fg_sram_param_id id, int val, u8 *buf);
+static void fg_encode_default(struct fg_sram_param *sp,
+ enum fg_sram_param_id id, int val, u8 *buf);
+
+#define PARAM(_id, _addr, _offset, _len, _num, _den, _enc, _dec) \
+ [FG_SRAM_##_id] = { \
+ .address = _addr, \
+ .offset = _offset, \
+ .len = _len, \
+ .numrtr = _num, \
+ .denmtr = _den, \
+ .encode = _enc, \
+ .decode = _dec, \
+ } \
+
+static struct fg_sram_param pmicobalt_v1_sram_params[] = {
+ PARAM(BATT_SOC, BATT_SOC_WORD, BATT_SOC_OFFSET, 4, 1, 1, NULL,
+ fg_decode_default),
+ PARAM(VOLTAGE_PRED, VOLTAGE_PRED_WORD, VOLTAGE_PRED_OFFSET, 2, 244141,
+ 1000, NULL, fg_decode_value_16b),
+ PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 244141, 1000, NULL,
+ fg_decode_value_16b),
+ PARAM(RSLOW, RSLOW_WORD, RSLOW_OFFSET, 2, 244141, 1000, NULL,
+ fg_decode_value_16b),
+ /* Entries below here are configurable during initialization */
+ PARAM(CUTOFF_VOLT, CUTOFF_VOLT_WORD, CUTOFF_VOLT_OFFSET, 2, 1000000,
+ 244141, fg_encode_voltage_16b, NULL),
+ PARAM(EMPTY_VOLT, EMPTY_VOLT_WORD, EMPTY_VOLT_OFFSET, 1, 100000, 390625,
+ fg_encode_voltage_8b, NULL),
+ PARAM(VBATT_LOW, VBATT_LOW_WORD, VBATT_LOW_OFFSET, 1, 100000, 390625,
+ fg_encode_voltage_8b, NULL),
+ PARAM(SYS_TERM_CURR, SYS_TERM_CURR_WORD, SYS_TERM_CURR_OFFSET, 3,
+ 1000000, 122070, fg_encode_current, NULL),
+ PARAM(CHG_TERM_CURR, CHG_TERM_CURR_WORD, CHG_TERM_CURR_OFFSET, 1,
+ 100000, 390625, fg_encode_current, NULL),
+ PARAM(DELTA_SOC_THR, DELTA_SOC_THR_WORD, DELTA_SOC_THR_OFFSET, 1, 256,
+ 100, fg_encode_default, NULL),
+ PARAM(RECHARGE_SOC_THR, RECHARGE_SOC_THR_WORD, RECHARGE_SOC_THR_OFFSET,
+ 1, 256, 100, fg_encode_default, NULL),
+};
+
+static int fg_gen3_debug_mask;
+module_param_named(
+ debug_mask, fg_gen3_debug_mask, int, S_IRUSR | S_IWUSR
+);
+
+static int fg_sram_update_period_ms = 30000;
+module_param_named(
+ sram_update_period_ms, fg_sram_update_period_ms, int, S_IRUSR | S_IWUSR
+);
+
+/* Other functions HERE */
+
+static int fg_awake_cb(struct votable *votable, void *data, int awake,
+ const char *client)
+{
+ struct fg_chip *chip = data;
+
+ if (awake)
+ pm_stay_awake(chip->dev);
+ else
+ pm_relax(chip->dev);
+
+ pr_debug("client: %s awake: %d\n", client, awake);
+ return 0;
+}
+
+static bool is_charger_available(struct fg_chip *chip)
+{
+ if (!chip->batt_psy)
+ chip->batt_psy = power_supply_get_by_name("battery");
+
+ if (!chip->batt_psy)
+ return false;
+
+ return true;
+}
+
+static void status_change_work(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip, status_change_work);
+ union power_supply_propval prop = {0, };
+
+ if (!is_charger_available(chip)) {
+ fg_dbg(chip, FG_STATUS, "Charger not available?!\n");
+ return;
+ }
+
+ power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STATUS,
+ &prop);
+ switch (prop.intval) {
+ case POWER_SUPPLY_STATUS_CHARGING:
+ fg_dbg(chip, FG_POWER_SUPPLY, "Charging\n");
+ break;
+ case POWER_SUPPLY_STATUS_DISCHARGING:
+ fg_dbg(chip, FG_POWER_SUPPLY, "Discharging\n");
+ break;
+ case POWER_SUPPLY_STATUS_FULL:
+ fg_dbg(chip, FG_POWER_SUPPLY, "Full\n");
+ break;
+ default:
+ break;
+ }
+}
+
+#define PROFILE_LEN 224
+#define PROFILE_COMP_LEN 32
+#define SOC_READY_WAIT_MS 2000
+static void profile_load_work(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip,
+ profile_load_work.work);
+ int rc;
+ u8 buf[PROFILE_COMP_LEN], val;
+ bool tried_again = false, profiles_same = false;
+
+ if (!chip->batt_id_avail) {
+ pr_err("batt_id not available\n");
+ return;
+ }
+
+ rc = fg_sram_read(chip, PROFILE_INTEGRITY_WORD,
+ PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("failed to read profile integrity rc=%d\n", rc);
+ return;
+ }
+
+ vote(chip->awake_votable, PROFILE_LOAD, true, 0);
+ if (val == 0x01) {
+ fg_dbg(chip, FG_STATUS, "Battery profile integrity bit is set\n");
+ rc = fg_sram_read(chip, PROFILE_LOAD_WORD, PROFILE_LOAD_OFFSET,
+ buf, PROFILE_COMP_LEN, FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in reading battery profile, rc:%d\n", rc);
+ goto out;
+ }
+ profiles_same = memcmp(chip->batt_profile, buf,
+ PROFILE_COMP_LEN) == 0;
+ if (profiles_same) {
+ fg_dbg(chip, FG_STATUS, "Battery profile is same\n");
+ goto done;
+ }
+ fg_dbg(chip, FG_STATUS, "profiles are different?\n");
+ }
+
+ fg_dbg(chip, FG_STATUS, "profile loading started\n");
+ rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, 0);
+ if (rc < 0) {
+ pr_err("Error in writing to %04x, rc=%d\n",
+ BATT_SOC_RESTART(chip), rc);
+ goto out;
+ }
+
+ /* load battery profile */
+ rc = fg_sram_write(chip, PROFILE_LOAD_WORD, PROFILE_LOAD_OFFSET,
+ chip->batt_profile, PROFILE_LEN, FG_IMA_ATOMIC);
+ if (rc < 0) {
+ pr_err("Error in writing battery profile, rc:%d\n", rc);
+ goto out;
+ }
+
+ rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT,
+ RESTART_GO_BIT);
+ if (rc < 0) {
+ pr_err("Error in writing to %04x, rc=%d\n",
+ BATT_SOC_RESTART(chip), rc);
+ goto out;
+ }
+
+wait:
+ rc = wait_for_completion_interruptible_timeout(&chip->soc_ready,
+ msecs_to_jiffies(SOC_READY_WAIT_MS));
+
+ /* If we were interrupted wait again one more time. */
+ if (rc == -ERESTARTSYS && !tried_again) {
+ tried_again = true;
+ goto wait;
+ } else if (rc <= 0) {
+ pr_err("wait for soc_ready timed out rc=%d\n", rc);
+ goto out;
+ }
+
+ fg_dbg(chip, FG_STATUS, "SOC is ready\n");
+
+ /* Set the profile integrity bit */
+ val = 0x1;
+ rc = fg_sram_write(chip, PROFILE_INTEGRITY_WORD,
+ PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("failed to write profile integrity rc=%d\n", rc);
+ goto out;
+ }
+
+ fg_dbg(chip, FG_STATUS, "profile loaded successfully");
+done:
+ rc = fg_sram_read(chip, NOM_CAP_WORD, NOM_CAP_OFFSET, buf, 2,
+ FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in reading %04x[%d] rc=%d\n", NOM_CAP_WORD,
+ NOM_CAP_OFFSET, rc);
+ goto out;
+ }
+
+ chip->nom_cap_uah = (int)(buf[0] | buf[1] << 8) * 1000;
+ chip->profile_loaded = true;
+out:
+ vote(chip->awake_votable, PROFILE_LOAD, false, 0);
+ rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, 0);
+ if (rc < 0)
+ pr_err("Error in writing to %04x, rc=%d\n",
+ BATT_SOC_RESTART(chip), rc);
+}
+
+/* All getters HERE */
+
+static int fg_decode_value_16b(struct fg_sram_param *sp,
+ enum fg_sram_param_id id, int value)
+{
+ sp[id].value = div_u64((u64)(u16)value * sp[id].numrtr, sp[id].denmtr);
+ pr_debug("id: %d raw value: %x decoded value: %x\n", id, value,
+ sp[id].value);
+ return sp[id].value;
+}
+
+static int fg_decode_default(struct fg_sram_param *sp,
+ enum fg_sram_param_id id, int value)
+{
+ return value;
+}
+
+static int fg_decode(struct fg_sram_param *sp, enum fg_sram_param_id id,
+ int value)
+{
+ if (!sp[id].decode) {
+ pr_err("No decoding function for parameter %d\n", id);
+ return -EINVAL;
+ }
+
+ return sp[id].decode(sp, id, value);
+}
+
+static void fg_encode_voltage_16b(struct fg_sram_param *sp,
+ enum fg_sram_param_id id, int val, u8 *buf)
+{
+ int i, mask = 0xff;
+ int64_t temp;
+
+ temp = (int64_t)div_u64((u64)val * sp[id].numrtr, sp[id].denmtr);
+ pr_debug("temp: %llx id: %d, val: %d, buf: [ ", temp, id, val);
+ for (i = 0; i < sp[id].len; i++) {
+ buf[i] = temp & mask;
+ temp >>= 8;
+ pr_debug("%x ", buf[i]);
+ }
+ pr_debug("]\n");
+}
+
+static void fg_encode_voltage_8b(struct fg_sram_param *sp,
+ enum fg_sram_param_id id, int val, u8 *buf)
+{
+ int i, mask = 0xff;
+ int64_t temp;
+
+ /* Offset is 2.5V */
+ val -= 2500;
+ temp = (int64_t)div_u64((u64)val * sp[id].numrtr, sp[id].denmtr);
+ pr_debug("temp: %llx id: %d, val: %d, buf: [ ", temp, id, val);
+ for (i = 0; i < sp[id].len; i++) {
+ buf[i] = temp & mask;
+ temp >>= 8;
+ pr_debug("%x ", buf[i]);
+ }
+ pr_debug("]\n");
+}
+
+static void fg_encode_current(struct fg_sram_param *sp,
+ enum fg_sram_param_id id, int val, u8 *buf)
+{
+ int i, mask = 0xff;
+ int64_t temp;
+ s64 current_ma;
+
+ current_ma = -val;
+ temp = (int64_t)div_s64(current_ma * sp[id].numrtr, sp[id].denmtr);
+ pr_debug("temp: %llx id: %d, val: %d, buf: [ ", temp, id, val);
+ for (i = 0; i < sp[id].len; i++) {
+ buf[i] = temp & mask;
+ temp >>= 8;
+ pr_debug("%x ", buf[i]);
+ }
+ pr_debug("]\n");
+}
+
+static void fg_encode_default(struct fg_sram_param *sp,
+ enum fg_sram_param_id id, int val, u8 *buf)
+{
+ int i, mask = 0xff;
+ int64_t temp;
+
+ temp = DIV_ROUND_CLOSEST(val * sp[id].numrtr, sp[id].denmtr);
+ pr_debug("temp: %llx id: %d, val: %d, buf: [ ", temp, id, val);
+ for (i = 0; i < sp[id].len; i++) {
+ buf[i] = temp & mask;
+ temp >>= 8;
+ pr_debug("%x ", buf[i]);
+ }
+ pr_debug("]\n");
+}
+
+static void fg_encode(struct fg_sram_param *sp, enum fg_sram_param_id id,
+ int val, u8 *buf)
+{
+ if (!sp[id].encode) {
+ pr_err("No encoding function for parameter %d\n", id);
+ return;
+ }
+
+ sp[id].encode(sp, id, val, buf);
+}
+
+/*
+ * Please make sure *_sram_params table has the entry for the parameter
+ * obtained through this function. In addition to address, offset,
+ * length from where this SRAM parameter is read, a decode function
+ * need to be specified.
+ */
+static int fg_get_sram_prop(struct fg_chip *chip, enum fg_sram_param_id id,
+ int *val)
+{
+ int temp, rc, i;
+ u8 buf[4];
+
+ if (id < 0 || id > FG_SRAM_MAX || chip->sp[id].len > sizeof(buf))
+ return -EINVAL;
+
+ vote(chip->awake_votable, SRAM_UPDATE, true, 0);
+ rc = fg_sram_read(chip, chip->sp[id].address, chip->sp[id].offset,
+ buf, chip->sp[id].len, FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error reading address 0x%04x[%d] rc=%d\n",
+ chip->sp[id].address, chip->sp[id].offset, rc);
+ goto out;
+ }
+
+ for (i = 0, temp = 0; i < chip->sp[id].len; i++)
+ temp |= buf[i] << (8 * i);
+
+ *val = fg_decode(chip->sp, id, temp);
+ return 0;
+out:
+ vote(chip->awake_votable, SRAM_UPDATE, false, 0);
+ return rc;
+}
+
+#define BATT_TEMP_NUMR 1
+#define BATT_TEMP_DENR 1
+static int fg_get_battery_temp(struct fg_chip *chip, int *val)
+{
+ int rc = 0;
+ u16 temp = 0;
+ u8 buf[2];
+
+ rc = fg_read(chip, BATT_INFO_BATT_TEMP_LSB(chip), buf, 2);
+ if (rc < 0) {
+ pr_err("failed to read addr=0x%04x, rc=%d\n",
+ BATT_INFO_BATT_TEMP_LSB(chip), rc);
+ return rc;
+ }
+
+ temp = ((buf[1] & BATT_TEMP_MSB_MASK) << 8) |
+ (buf[0] & BATT_TEMP_LSB_MASK);
+ temp = DIV_ROUND_CLOSEST(temp, 4);
+
+ /* Value is in Kelvin; Convert it to deciDegC */
+ temp = (temp - 273) * 10;
+ *val = temp;
+ return 0;
+}
+
+#define BATT_ESR_NUMR 244141
+#define BATT_ESR_DENR 1000
+static int fg_get_battery_esr(struct fg_chip *chip, int *val)
+{
+ int rc = 0;
+ u16 temp = 0;
+ u8 buf[2];
+
+ rc = fg_read(chip, BATT_INFO_ESR_LSB(chip), buf, 2);
+ if (rc < 0) {
+ pr_err("failed to read addr=0x%04x, rc=%d\n",
+ BATT_INFO_ESR_LSB(chip), rc);
+ return rc;
+ }
+
+ if (chip->pmic_rev_id->rev4 < PMICOBALT_V2P0_REV4)
+ temp = ((buf[0] & ESR_MSB_MASK) << 8) |
+ (buf[1] & ESR_LSB_MASK);
+ else
+ temp = ((buf[1] & ESR_MSB_MASK) << 8) |
+ (buf[0] & ESR_LSB_MASK);
+
+ pr_debug("buf: %x %x temp: %x\n", buf[0], buf[1], temp);
+ *val = div_u64((u64)temp * BATT_ESR_NUMR, BATT_ESR_DENR);
+ return 0;
+}
+
+static int fg_get_battery_resistance(struct fg_chip *chip, int *val)
+{
+ int rc, esr, rslow;
+
+ rc = fg_get_battery_esr(chip, &esr);
+ if (rc < 0) {
+ pr_err("failed to get ESR, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = fg_get_sram_prop(chip, FG_SRAM_RSLOW, &rslow);
+ if (rc < 0) {
+ pr_err("failed to get Rslow, rc=%d\n", rc);
+ return rc;
+ }
+
+ *val = esr + rslow;
+ return 0;
+}
+
+#define BATT_CURRENT_NUMR 488281
+#define BATT_CURRENT_DENR 1000
+static int fg_get_battery_current(struct fg_chip *chip, int *val)
+{
+ int rc = 0;
+ int64_t temp = 0;
+ u8 buf[2];
+
+ rc = fg_read(chip, BATT_INFO_IBATT_LSB(chip), buf, 2);
+ if (rc < 0) {
+ pr_err("failed to read addr=0x%04x, rc=%d\n",
+ BATT_INFO_IBATT_LSB(chip), rc);
+ return rc;
+ }
+
+ if (chip->pmic_rev_id->rev4 < PMICOBALT_V2P0_REV4)
+ temp = buf[0] << 8 | buf[1];
+ else
+ temp = buf[1] << 8 | buf[0];
+
+ pr_debug("buf: %x %x temp: %llx\n", buf[0], buf[1], temp);
+ /* Sign bit is bit 15 */
+ temp = twos_compliment_extend(temp, 15);
+ *val = div_s64((s64)temp * BATT_CURRENT_NUMR, BATT_CURRENT_DENR);
+ return 0;
+}
+
+#define BATT_VOLTAGE_NUMR 122070
+#define BATT_VOLTAGE_DENR 1000
+static int fg_get_battery_voltage(struct fg_chip *chip, int *val)
+{
+ int rc = 0;
+ u16 temp = 0;
+ u8 buf[2];
+
+ rc = fg_read(chip, BATT_INFO_VBATT_LSB(chip), buf, 2);
+ if (rc < 0) {
+ pr_err("failed to read addr=0x%04x, rc=%d\n",
+ BATT_INFO_VBATT_LSB(chip), rc);
+ return rc;
+ }
+
+ if (chip->pmic_rev_id->rev4 < PMICOBALT_V2P0_REV4)
+ temp = buf[0] << 8 | buf[1];
+ else
+ temp = buf[1] << 8 | buf[0];
+
+ pr_debug("buf: %x %x temp: %x\n", buf[0], buf[1], temp);
+ *val = div_u64((u64)temp * BATT_VOLTAGE_NUMR, BATT_VOLTAGE_DENR);
+ return 0;
+}
+
+#define MAX_TRIES_SOC 5
+static int fg_get_msoc_raw(struct fg_chip *chip, int *val)
+{
+ u8 cap[2];
+ int rc, tries = 0;
+
+ while (tries < MAX_TRIES_SOC) {
+ rc = fg_read(chip, BATT_SOC_FG_MONOTONIC_SOC(chip), cap, 2);
+ if (rc < 0) {
+ pr_err("failed to read addr=0x%04x, rc=%d\n",
+ BATT_SOC_FG_MONOTONIC_SOC(chip), rc);
+ return rc;
+ }
+
+ if (cap[0] == cap[1])
+ break;
+
+ tries++;
+ }
+
+ if (tries == MAX_TRIES_SOC) {
+ pr_err("shadow registers do not match\n");
+ return -EINVAL;
+ }
+
+ fg_dbg(chip, FG_POWER_SUPPLY, "raw: 0x%02x\n", cap[0]);
+
+ *val = cap[0];
+ return 0;
+}
+
+#define FULL_CAPACITY 100
+#define FULL_SOC_RAW 255
+static int fg_get_prop_capacity(struct fg_chip *chip, int *val)
+{
+ int rc, msoc;
+
+ rc = fg_get_msoc_raw(chip, &msoc);
+ if (rc < 0)
+ return rc;
+
+ *val = DIV_ROUND_CLOSEST(msoc * FULL_CAPACITY, FULL_SOC_RAW);
+ return 0;
+}
+
+#define DEFAULT_BATT_TYPE "Unknown Battery"
+#define MISSING_BATT_TYPE "Missing Battery"
+#define LOADING_BATT_TYPE "Loading Battery"
+static const char *fg_get_battery_type(struct fg_chip *chip)
+{
+ if (chip->battery_missing)
+ return MISSING_BATT_TYPE;
+
+ if (chip->bp.batt_type_str) {
+ if (chip->profile_loaded)
+ return chip->bp.batt_type_str;
+ else
+ return LOADING_BATT_TYPE;
+ }
+
+ return DEFAULT_BATT_TYPE;
+}
+
+static int fg_get_batt_id(struct fg_chip *chip, int *val)
+{
+ int rc, batt_id = -EINVAL;
+
+ if (!chip->batt_id_chan)
+ return -EINVAL;
+
+ rc = iio_read_channel_processed(chip->batt_id_chan, &batt_id);
+ if (rc < 0) {
+ pr_err("Error in reading batt_id channel, rc:%d\n", rc);
+ return rc;
+ }
+
+ chip->batt_id_avail = true;
+ fg_dbg(chip, FG_STATUS, "batt_id: %d\n", batt_id);
+
+ *val = batt_id;
+ return 0;
+}
+
+static int fg_get_batt_profile(struct fg_chip *chip)
+{
+ struct device_node *node = chip->dev->of_node;
+ struct device_node *batt_node, *profile_node;
+ const char *data;
+ int rc, len, batt_id;
+
+ rc = fg_get_batt_id(chip, &batt_id);
+ if (rc < 0) {
+ pr_err("Error in getting batt_id rc:%d\n", rc);
+ return rc;
+ }
+
+ batt_node = of_find_node_by_name(node, "qcom,battery-data");
+ if (!batt_node) {
+ pr_err("Batterydata not available\n");
+ return -ENXIO;
+ }
+
+ batt_id /= 1000;
+ profile_node = of_batterydata_get_best_profile(batt_node, batt_id,
+ NULL);
+ if (IS_ERR(profile_node))
+ return PTR_ERR(profile_node);
+
+ if (!profile_node) {
+ pr_err("couldn't find profile handle\n");
+ return -ENODATA;
+ }
+
+ rc = of_property_read_string(profile_node, "qcom,battery-type",
+ &chip->bp.batt_type_str);
+ if (rc < 0) {
+ pr_err("battery type unavailable, rc:%d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv",
+ &chip->bp.float_volt_uv);
+ if (rc < 0) {
+ pr_err("battery float voltage unavailable, rc:%d\n", rc);
+ chip->bp.float_volt_uv = -EINVAL;
+ }
+
+ rc = of_property_read_u32(profile_node, "qcom,nom-batt-capacity-mah",
+ &chip->bp.fastchg_curr_ma);
+ if (rc < 0) {
+ pr_err("battery nominal capacity unavailable, rc:%d\n", rc);
+ chip->bp.fastchg_curr_ma = -EINVAL;
+ }
+
+ data = of_get_property(profile_node, "qcom,fg-profile-data", &len);
+ if (!data) {
+ pr_err("No profile data available\n");
+ return -ENODATA;
+ }
+
+ if (len != PROFILE_LEN) {
+ pr_err("battery profile incorrect size: %d\n", len);
+ return -EINVAL;
+ }
+
+ memcpy(chip->batt_profile, data, len);
+ return 0;
+}
+
+static inline void get_temp_setpoint(int threshold, u8 *val)
+{
+ /* Resolution is 0.5C. Base is -30C. */
+ *val = DIV_ROUND_CLOSEST((threshold + 30) * 10, 5);
+}
+
+/* PSY CALLBACKS STAY HERE */
+
+static int fg_psy_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *pval)
+{
+ struct fg_chip *chip = power_supply_get_drvdata(psy);
+ int rc = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CAPACITY:
+ rc = fg_get_prop_capacity(chip, &pval->intval);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ rc = fg_get_battery_voltage(chip, &pval->intval);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ rc = fg_get_battery_current(chip, &pval->intval);
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ rc = fg_get_battery_temp(chip, &pval->intval);
+ break;
+ case POWER_SUPPLY_PROP_RESISTANCE:
+ rc = fg_get_battery_resistance(chip, &pval->intval);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_OCV:
+ rc = fg_get_sram_prop(chip, FG_SRAM_OCV, &pval->intval);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ pval->intval = chip->nom_cap_uah;
+ break;
+ case POWER_SUPPLY_PROP_RESISTANCE_ID:
+ rc = fg_get_batt_id(chip, &pval->intval);
+ break;
+ case POWER_SUPPLY_PROP_BATTERY_TYPE:
+ pval->strval = fg_get_battery_type(chip);
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+static int fg_psy_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *pval)
+{
+ switch (psp) {
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int fg_property_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ switch (psp) {
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void fg_external_power_changed(struct power_supply *psy)
+{
+ pr_debug("power supply changed\n");
+}
+
+static int fg_notifier_cb(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct power_supply *psy = data;
+ struct fg_chip *chip = container_of(nb, struct fg_chip, nb);
+
+ if (event != PSY_EVENT_PROP_CHANGED)
+ return NOTIFY_OK;
+
+ if ((strcmp(psy->desc->name, "battery") == 0)
+ || (strcmp(psy->desc->name, "usb") == 0))
+ schedule_work(&chip->status_change_work);
+
+ return NOTIFY_OK;
+}
+
+static enum power_supply_property fg_psy_props[] = {
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_OCV,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_RESISTANCE_ID,
+ POWER_SUPPLY_PROP_RESISTANCE,
+ POWER_SUPPLY_PROP_BATTERY_TYPE,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+};
+
+static const struct power_supply_desc fg_psy_desc = {
+ .name = "bms",
+ .type = POWER_SUPPLY_TYPE_BMS,
+ .properties = fg_psy_props,
+ .num_properties = ARRAY_SIZE(fg_psy_props),
+ .get_property = fg_psy_get_property,
+ .set_property = fg_psy_set_property,
+ .external_power_changed = fg_external_power_changed,
+ .property_is_writeable = fg_property_is_writeable,
+};
+
+/* INIT FUNCTIONS STAY HERE */
+
+static int fg_hw_init(struct fg_chip *chip)
+{
+ int rc;
+ u8 buf[4], val;
+
+ fg_encode(chip->sp, FG_SRAM_CUTOFF_VOLT, chip->dt.cutoff_volt_mv, buf);
+ rc = fg_sram_write(chip, chip->sp[FG_SRAM_CUTOFF_VOLT].address,
+ chip->sp[FG_SRAM_CUTOFF_VOLT].offset, buf,
+ chip->sp[FG_SRAM_CUTOFF_VOLT].len, FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in writing cutoff_volt, rc=%d\n", rc);
+ return rc;
+ }
+
+ fg_encode(chip->sp, FG_SRAM_EMPTY_VOLT, chip->dt.empty_volt_mv, buf);
+ rc = fg_sram_write(chip, chip->sp[FG_SRAM_EMPTY_VOLT].address,
+ chip->sp[FG_SRAM_EMPTY_VOLT].offset, buf,
+ chip->sp[FG_SRAM_EMPTY_VOLT].len, FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in writing empty_volt, rc=%d\n", rc);
+ return rc;
+ }
+
+ fg_encode(chip->sp, FG_SRAM_CHG_TERM_CURR, chip->dt.chg_term_curr_ma,
+ buf);
+ rc = fg_sram_write(chip, chip->sp[FG_SRAM_CHG_TERM_CURR].address,
+ chip->sp[FG_SRAM_CHG_TERM_CURR].offset, buf,
+ chip->sp[FG_SRAM_CHG_TERM_CURR].len, FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in writing chg_term_curr, rc=%d\n", rc);
+ return rc;
+ }
+
+ fg_encode(chip->sp, FG_SRAM_SYS_TERM_CURR, chip->dt.sys_term_curr_ma,
+ buf);
+ rc = fg_sram_write(chip, chip->sp[FG_SRAM_SYS_TERM_CURR].address,
+ chip->sp[FG_SRAM_SYS_TERM_CURR].offset, buf,
+ chip->sp[FG_SRAM_SYS_TERM_CURR].len, FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in writing sys_term_curr, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (chip->dt.vbatt_low_thr_mv > 0) {
+ fg_encode(chip->sp, FG_SRAM_VBATT_LOW,
+ chip->dt.vbatt_low_thr_mv, buf);
+ rc = fg_sram_write(chip, chip->sp[FG_SRAM_VBATT_LOW].address,
+ chip->sp[FG_SRAM_VBATT_LOW].offset, buf,
+ chip->sp[FG_SRAM_VBATT_LOW].len,
+ FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in writing vbatt_low_thr, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (chip->dt.delta_soc_thr > 0 && chip->dt.delta_soc_thr < 100) {
+ fg_encode(chip->sp, FG_SRAM_DELTA_SOC_THR,
+ chip->dt.delta_soc_thr, buf);
+ rc = fg_sram_write(chip,
+ chip->sp[FG_SRAM_DELTA_SOC_THR].address,
+ chip->sp[FG_SRAM_DELTA_SOC_THR].offset,
+ buf, chip->sp[FG_SRAM_DELTA_SOC_THR].len,
+ FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in writing delta_soc_thr, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (chip->dt.recharge_soc_thr > 0 && chip->dt.recharge_soc_thr < 100) {
+ fg_encode(chip->sp, FG_SRAM_RECHARGE_SOC_THR,
+ chip->dt.recharge_soc_thr, buf);
+ rc = fg_sram_write(chip,
+ chip->sp[FG_SRAM_RECHARGE_SOC_THR].address,
+ chip->sp[FG_SRAM_RECHARGE_SOC_THR].offset,
+ buf, chip->sp[FG_SRAM_RECHARGE_SOC_THR].len,
+ FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in writing recharge_soc_thr, rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ if (chip->dt.rsense_sel >= SRC_SEL_BATFET &&
+ chip->dt.rsense_sel < SRC_SEL_RESERVED) {
+ rc = fg_masked_write(chip, BATT_INFO_IBATT_SENSING_CFG(chip),
+ SOURCE_SELECT_MASK, chip->dt.rsense_sel);
+ if (rc < 0) {
+ pr_err("Error in writing rsense_sel, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ get_temp_setpoint(chip->dt.jeita_thresholds[JEITA_COLD], &val);
+ rc = fg_write(chip, BATT_INFO_JEITA_TOO_COLD(chip), &val, 1);
+ if (rc < 0) {
+ pr_err("Error in writing jeita_cold, rc=%d\n", rc);
+ return rc;
+ }
+
+ get_temp_setpoint(chip->dt.jeita_thresholds[JEITA_COOL], &val);
+ rc = fg_write(chip, BATT_INFO_JEITA_COLD(chip), &val, 1);
+ if (rc < 0) {
+ pr_err("Error in writing jeita_cool, rc=%d\n", rc);
+ return rc;
+ }
+
+ get_temp_setpoint(chip->dt.jeita_thresholds[JEITA_WARM], &val);
+ rc = fg_write(chip, BATT_INFO_JEITA_HOT(chip), &val, 1);
+ if (rc < 0) {
+ pr_err("Error in writing jeita_warm, rc=%d\n", rc);
+ return rc;
+ }
+
+ get_temp_setpoint(chip->dt.jeita_thresholds[JEITA_HOT], &val);
+ rc = fg_write(chip, BATT_INFO_JEITA_TOO_HOT(chip), &val, 1);
+ if (rc < 0) {
+ pr_err("Error in writing jeita_hot, rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int fg_memif_init(struct fg_chip *chip)
+{
+ return fg_ima_init(chip);
+}
+
+static int fg_batt_profile_init(struct fg_chip *chip)
+{
+ int rc;
+
+ if (!chip->batt_profile) {
+ chip->batt_profile = devm_kcalloc(chip->dev, PROFILE_LEN,
+ sizeof(*chip->batt_profile),
+ GFP_KERNEL);
+ if (!chip->batt_profile)
+ return -ENOMEM;
+ }
+
+ rc = fg_get_batt_profile(chip);
+ if (rc < 0) {
+ pr_err("Error in getting battery profile, rc:%d\n", rc);
+ return rc;
+ }
+
+ schedule_delayed_work(&chip->profile_load_work, msecs_to_jiffies(0));
+ return 0;
+}
+
+/* INTERRUPT HANDLERS STAY HERE */
+
+static irqreturn_t fg_vbatt_low_irq_handler(int irq, void *data)
+{
+ struct fg_chip *chip = data;
+
+ fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fg_batt_missing_irq_handler(int irq, void *data)
+{
+ struct fg_chip *chip = data;
+ u8 status;
+ int rc;
+
+ fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq);
+ rc = fg_read(chip, BATT_INFO_INT_RT_STS(chip), &status, 1);
+ if (rc < 0) {
+ pr_err("failed to read addr=0x%04x, rc=%d\n",
+ BATT_INFO_INT_RT_STS(chip), rc);
+ return IRQ_HANDLED;
+ }
+
+ chip->battery_missing = (status & BT_MISS_BIT);
+
+ if (chip->battery_missing) {
+ chip->batt_id_avail = false;
+ chip->profile_loaded = false;
+ } else {
+ rc = fg_batt_profile_init(chip);
+ if (rc < 0) {
+ pr_err("Error in initializing battery profile, rc=%d\n",
+ rc);
+ return IRQ_HANDLED;
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fg_delta_batt_temp_irq_handler(int irq, void *data)
+{
+ struct fg_chip *chip = data;
+
+ fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fg_first_est_irq_handler(int irq, void *data)
+{
+ struct fg_chip *chip = data;
+
+ fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq);
+ complete_all(&chip->soc_ready);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fg_soc_update_irq_handler(int irq, void *data)
+{
+ struct fg_chip *chip = data;
+
+ fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq);
+ complete_all(&chip->soc_update);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fg_delta_soc_irq_handler(int irq, void *data)
+{
+ struct fg_chip *chip = data;
+
+ fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fg_empty_soc_irq_handler(int irq, void *data)
+{
+ struct fg_chip *chip = data;
+
+ fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fg_soc_irq_handler(int irq, void *data)
+{
+ struct fg_chip *chip = data;
+
+ fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fg_dummy_irq_handler(int irq, void *data)
+{
+ struct fg_chip *chip = data;
+
+ fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq);
+ return IRQ_HANDLED;
+}
+
+static struct fg_irq_info fg_irqs[FG_IRQ_MAX] = {
+ /* BATT_SOC irqs */
+ [MSOC_FULL_IRQ] = {
+ "msoc-full", fg_soc_irq_handler, true },
+ [MSOC_HIGH_IRQ] = {
+ "msoc-high", fg_soc_irq_handler, true },
+ [MSOC_EMPTY_IRQ] = {
+ "msoc-empty", fg_empty_soc_irq_handler, true },
+ [MSOC_LOW_IRQ] = {
+ "msoc-low", fg_soc_irq_handler },
+ [MSOC_DELTA_IRQ] = {
+ "msoc-delta", fg_delta_soc_irq_handler, true },
+ [BSOC_DELTA_IRQ] = {
+ "bsoc-delta", fg_delta_soc_irq_handler, true },
+ [SOC_READY_IRQ] = {
+ "soc-ready", fg_first_est_irq_handler, true },
+ [SOC_UPDATE_IRQ] = {
+ "soc-update", fg_soc_update_irq_handler },
+ /* BATT_INFO irqs */
+ [BATT_TEMP_DELTA_IRQ] = {
+ "batt-temp-delta", fg_delta_batt_temp_irq_handler },
+ [BATT_MISSING_IRQ] = {
+ "batt-missing", fg_batt_missing_irq_handler, true },
+ [ESR_DELTA_IRQ] = {
+ "esr-delta", fg_dummy_irq_handler },
+ [VBATT_LOW_IRQ] = {
+ "vbatt-low", fg_vbatt_low_irq_handler, true },
+ [VBATT_PRED_DELTA_IRQ] = {
+ "vbatt-pred-delta", fg_dummy_irq_handler },
+ /* MEM_IF irqs */
+ [DMA_GRANT_IRQ] = {
+ "dma-grant", fg_dummy_irq_handler },
+ [MEM_XCP_IRQ] = {
+ "mem-xcp", fg_dummy_irq_handler },
+ [IMA_RDY_IRQ] = {
+ "ima-rdy", fg_dummy_irq_handler },
+};
+
+static int fg_get_irq_index_byname(const char *name)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fg_irqs); i++) {
+ if (strcmp(fg_irqs[i].name, name) == 0)
+ return i;
+ }
+
+ pr_err("%s is not in irq list\n", name);
+ return -ENOENT;
+}
+
+static int fg_register_interrupts(struct fg_chip *chip)
+{
+ struct device_node *child, *node = chip->dev->of_node;
+ struct property *prop;
+ const char *name;
+ int rc, irq, irq_index;
+
+ for_each_available_child_of_node(node, child) {
+ of_property_for_each_string(child, "interrupt-names", prop,
+ name) {
+ irq = of_irq_get_byname(child, name);
+ if (irq < 0) {
+ dev_err(chip->dev, "failed to get irq %s irq:%d\n",
+ name, irq);
+ return irq;
+ }
+
+ irq_index = fg_get_irq_index_byname(name);
+ if (irq_index < 0)
+ return irq_index;
+
+ rc = devm_request_threaded_irq(chip->dev, irq, NULL,
+ fg_irqs[irq_index].handler,
+ IRQF_ONESHOT, name, chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "failed to register irq handler for %s rc:%d\n",
+ name, rc);
+ return rc;
+ }
+
+ fg_irqs[irq_index].irq = irq;
+ if (fg_irqs[irq_index].wakeable)
+ enable_irq_wake(fg_irqs[irq_index].irq);
+ }
+ }
+
+ return 0;
+}
+
+#define DEFAULT_CUTOFF_VOLT_MV 3200
+#define DEFAULT_EMPTY_VOLT_MV 3100
+#define DEFAULT_CHG_TERM_CURR_MA 100
+#define DEFAULT_SYS_TERM_CURR_MA 125
+#define DEFAULT_DELTA_SOC_THR 1
+#define DEFAULT_RECHARGE_SOC_THR 95
+#define DEFAULT_BATT_TEMP_COLD 0
+#define DEFAULT_BATT_TEMP_COOL 5
+#define DEFAULT_BATT_TEMP_WARM 45
+#define DEFAULT_BATT_TEMP_HOT 50
+static int fg_parse_dt(struct fg_chip *chip)
+{
+ struct device_node *child, *revid_node, *node = chip->dev->of_node;
+ u32 base, temp;
+ u8 subtype;
+ int rc, len;
+
+ if (!node) {
+ dev_err(chip->dev, "device tree node missing\n");
+ return -ENXIO;
+ }
+
+ revid_node = of_parse_phandle(node, "qcom,pmic-revid", 0);
+ if (!revid_node) {
+ pr_err("Missing qcom,pmic-revid property - driver failed\n");
+ return -EINVAL;
+ }
+
+ chip->pmic_rev_id = get_revid_data(revid_node);
+ if (IS_ERR_OR_NULL(chip->pmic_rev_id)) {
+ pr_err("Unable to get pmic_revid rc=%ld\n",
+ PTR_ERR(chip->pmic_rev_id));
+ /*
+ * the revid peripheral must be registered, any failure
+ * here only indicates that the rev-id module has not
+ * probed yet.
+ */
+ return -EPROBE_DEFER;
+ }
+
+ pr_debug("PMIC subtype %d Digital major %d\n",
+ chip->pmic_rev_id->pmic_subtype, chip->pmic_rev_id->rev4);
+
+ switch (chip->pmic_rev_id->pmic_subtype) {
+ case PMICOBALT_SUBTYPE:
+ if (chip->pmic_rev_id->rev4 < PMICOBALT_V2P0_REV4)
+ chip->sp = pmicobalt_v1_sram_params;
+ else
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ chip->batt_id_chan = iio_channel_get(chip->dev, "rradc_batt_id");
+ if (IS_ERR(chip->batt_id_chan)) {
+ if (PTR_ERR(chip->batt_id_chan) != -EPROBE_DEFER)
+ pr_err("batt_id_chan unavailable %ld\n",
+ PTR_ERR(chip->batt_id_chan));
+ rc = PTR_ERR(chip->batt_id_chan);
+ chip->batt_id_chan = NULL;
+ return rc;
+ }
+
+ if (of_get_available_child_count(node) == 0) {
+ dev_err(chip->dev, "No child nodes specified!\n");
+ return -ENXIO;
+ }
+
+ for_each_available_child_of_node(node, child) {
+ rc = of_property_read_u32(child, "reg", &base);
+ if (rc < 0) {
+ dev_err(chip->dev, "reg not specified in node %s, rc=%d\n",
+ child->full_name, rc);
+ return rc;
+ }
+
+ rc = fg_read(chip, base + PERPH_SUBTYPE_REG, &subtype, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read subtype for base %d, rc=%d\n",
+ base, rc);
+ return rc;
+ }
+
+ switch (subtype) {
+ case FG_BATT_SOC_PMICOBALT:
+ chip->batt_soc_base = base;
+ break;
+ case FG_BATT_INFO_PMICOBALT:
+ chip->batt_info_base = base;
+ break;
+ case FG_MEM_INFO_PMICOBALT:
+ chip->mem_if_base = base;
+ break;
+ default:
+ dev_err(chip->dev, "Invalid peripheral subtype 0x%x\n",
+ subtype);
+ return -ENXIO;
+ }
+ }
+
+ /* Read all the optional properties below */
+ rc = of_property_read_u32(node, "qcom,fg-cutoff-voltage", &temp);
+ if (rc < 0)
+ chip->dt.cutoff_volt_mv = DEFAULT_CUTOFF_VOLT_MV;
+ else
+ chip->dt.cutoff_volt_mv = temp;
+
+ rc = of_property_read_u32(node, "qcom,fg-empty-voltage", &temp);
+ if (rc < 0)
+ chip->dt.empty_volt_mv = DEFAULT_EMPTY_VOLT_MV;
+ else
+ chip->dt.empty_volt_mv = temp;
+
+ rc = of_property_read_u32(node, "qcom,fg-vbatt-low-thr", &temp);
+ if (rc < 0)
+ chip->dt.vbatt_low_thr_mv = -EINVAL;
+ else
+ chip->dt.vbatt_low_thr_mv = temp;
+
+ rc = of_property_read_u32(node, "qcom,fg-chg-term-current", &temp);
+ if (rc < 0)
+ chip->dt.chg_term_curr_ma = DEFAULT_CHG_TERM_CURR_MA;
+ else
+ chip->dt.chg_term_curr_ma = temp;
+
+ rc = of_property_read_u32(node, "qcom,fg-sys-term-current", &temp);
+ if (rc < 0)
+ chip->dt.sys_term_curr_ma = DEFAULT_SYS_TERM_CURR_MA;
+ else
+ chip->dt.sys_term_curr_ma = temp;
+
+ rc = of_property_read_u32(node, "qcom,fg-delta-soc-thr", &temp);
+ if (rc < 0)
+ chip->dt.delta_soc_thr = DEFAULT_DELTA_SOC_THR;
+ else
+ chip->dt.delta_soc_thr = temp;
+
+ rc = of_property_read_u32(node, "qcom,fg-recharge-soc-thr", &temp);
+ if (rc < 0)
+ chip->dt.recharge_soc_thr = DEFAULT_RECHARGE_SOC_THR;
+ else
+ chip->dt.recharge_soc_thr = temp;
+
+ rc = of_property_read_u32(node, "qcom,fg-rsense-sel", &temp);
+ if (rc < 0)
+ chip->dt.rsense_sel = SRC_SEL_BATFET_SMB;
+ else
+ chip->dt.rsense_sel = (u8)temp & SOURCE_SELECT_MASK;
+
+ chip->dt.jeita_thresholds[JEITA_COLD] = DEFAULT_BATT_TEMP_COLD;
+ chip->dt.jeita_thresholds[JEITA_COOL] = DEFAULT_BATT_TEMP_COOL;
+ chip->dt.jeita_thresholds[JEITA_WARM] = DEFAULT_BATT_TEMP_WARM;
+ chip->dt.jeita_thresholds[JEITA_HOT] = DEFAULT_BATT_TEMP_HOT;
+ if (of_find_property(node, "qcom,fg-jeita-thresholds", &len)) {
+ if (len == NUM_JEITA_LEVELS) {
+ rc = of_property_read_u32_array(node,
+ "qcom,fg-jeita-thresholds",
+ chip->dt.jeita_thresholds, len);
+ if (rc < 0)
+ pr_warn("Error reading Jeita thresholds, default values will be used rc:%d\n",
+ rc);
+ }
+ }
+
+ return 0;
+}
+
+static void fg_cleanup(struct fg_chip *chip)
+{
+ power_supply_unreg_notifier(&chip->nb);
+ debugfs_remove_recursive(chip->dentry);
+ if (chip->awake_votable)
+ destroy_votable(chip->awake_votable);
+
+ if (chip->batt_id_chan)
+ iio_channel_release(chip->batt_id_chan);
+
+ dev_set_drvdata(chip->dev, NULL);
+}
+
+static int fg_gen3_probe(struct platform_device *pdev)
+{
+ struct fg_chip *chip;
+ struct power_supply_config fg_psy_cfg;
+ int rc;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->dev = &pdev->dev;
+ chip->debug_mask = &fg_gen3_debug_mask;
+ chip->irqs = fg_irqs;
+ chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
+ if (!chip->regmap) {
+ dev_err(chip->dev, "Parent regmap is unavailable\n");
+ return -ENXIO;
+ }
+
+ rc = fg_parse_dt(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Error in reading DT parameters, rc:%d\n",
+ rc);
+ return rc;
+ }
+
+ chip->awake_votable = create_votable("FG_WS", VOTE_SET_ANY, fg_awake_cb,
+ chip);
+ if (IS_ERR(chip->awake_votable)) {
+ rc = PTR_ERR(chip->awake_votable);
+ return rc;
+ }
+
+ mutex_init(&chip->bus_lock);
+ mutex_init(&chip->sram_rw_lock);
+ init_completion(&chip->soc_update);
+ init_completion(&chip->soc_ready);
+ INIT_DELAYED_WORK(&chip->profile_load_work, profile_load_work);
+ INIT_WORK(&chip->status_change_work, status_change_work);
+
+ rc = fg_memif_init(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Error in initializing FG_MEMIF, rc:%d\n",
+ rc);
+ goto exit;
+ }
+
+ rc = fg_hw_init(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Error in initializing FG hardware, rc:%d\n",
+ rc);
+ goto exit;
+ }
+
+ platform_set_drvdata(pdev, chip);
+
+ /* Register the power supply */
+ fg_psy_cfg.drv_data = chip;
+ fg_psy_cfg.of_node = NULL;
+ fg_psy_cfg.supplied_to = NULL;
+ fg_psy_cfg.num_supplicants = 0;
+ chip->fg_psy = devm_power_supply_register(chip->dev, &fg_psy_desc,
+ &fg_psy_cfg);
+ if (IS_ERR(chip->fg_psy)) {
+ pr_err("failed to register fg_psy rc = %ld\n",
+ PTR_ERR(chip->fg_psy));
+ goto exit;
+ }
+
+ chip->nb.notifier_call = fg_notifier_cb;
+ rc = power_supply_reg_notifier(&chip->nb);
+ if (rc < 0) {
+ pr_err("Couldn't register psy notifier rc = %d\n", rc);
+ goto exit;
+ }
+
+ rc = fg_register_interrupts(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Error in registering interrupts, rc:%d\n",
+ rc);
+ goto exit;
+ }
+
+ /* Keep SOC_UPDATE irq disabled until we require it */
+ if (fg_irqs[SOC_UPDATE_IRQ].irq)
+ disable_irq_nosync(fg_irqs[SOC_UPDATE_IRQ].irq);
+
+ rc = fg_sram_debugfs_create(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Error in creating debugfs entries, rc:%d\n",
+ rc);
+ goto exit;
+ }
+
+ rc = fg_batt_profile_init(chip);
+ if (rc < 0)
+ dev_warn(chip->dev, "Error in initializing battery profile, rc:%d\n",
+ rc);
+
+ device_init_wakeup(chip->dev, true);
+ pr_debug("FG GEN3 driver successfully probed\n");
+ return 0;
+exit:
+ fg_cleanup(chip);
+ return rc;
+}
+
+static int fg_gen3_remove(struct platform_device *pdev)
+{
+ struct fg_chip *chip = dev_get_drvdata(&pdev->dev);
+
+ fg_cleanup(chip);
+ return 0;
+}
+
+static const struct of_device_id fg_gen3_match_table[] = {
+ {.compatible = FG_GEN3_DEV_NAME},
+ {},
+};
+
+static struct platform_driver fg_gen3_driver = {
+ .driver = {
+ .name = FG_GEN3_DEV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = fg_gen3_match_table,
+ },
+ .probe = fg_gen3_probe,
+ .remove = fg_gen3_remove,
+};
+
+static int __init fg_gen3_init(void)
+{
+ return platform_driver_register(&fg_gen3_driver);
+}
+
+static void __exit fg_gen3_exit(void)
+{
+ return platform_driver_unregister(&fg_gen3_driver);
+}
+
+module_init(fg_gen3_init);
+module_exit(fg_gen3_exit);
+
+MODULE_DESCRIPTION("QPNP Fuel gauge GEN3 driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" FG_GEN3_DEV_NAME);
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/icnss.c b/drivers/soc/qcom/icnss.c
index 883f23d8234d..ea25ed5d0611 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -2387,7 +2387,7 @@ static int icnss_probe(struct platform_device *pdev)
spin_lock_init(&penv->event_lock);
spin_lock_init(&penv->on_off_lock);
- penv->event_wq = alloc_workqueue("icnss_driver_event", 0, 0);
+ penv->event_wq = alloc_workqueue("icnss_driver_event", WQ_UNBOUND, 1);
if (!penv->event_wq) {
icnss_pr_err("Workqueue creation failed\n");
ret = -EFAULT;
@@ -2487,11 +2487,11 @@ static int icnss_suspend(struct platform_device *pdev,
icnss_pr_dbg("Driver suspending, state: 0x%lx\n",
penv->state);
- if (!penv->ops)
+ if (!penv->ops || !penv->ops->suspend ||
+ !test_bit(ICNSS_DRIVER_PROBED, &penv->state))
goto out;
- if (penv->ops->suspend)
- ret = penv->ops->suspend(&pdev->dev, state);
+ ret = penv->ops->suspend(&pdev->dev, state);
out:
if (ret == 0)
@@ -2511,11 +2511,11 @@ static int icnss_resume(struct platform_device *pdev)
icnss_pr_dbg("Driver resuming, state: 0x%lx\n",
penv->state);
- if (!penv->ops)
+ if (!penv->ops || !penv->ops->resume ||
+ !test_bit(ICNSS_DRIVER_PROBED, &penv->state))
goto out;
- if (penv->ops->resume)
- ret = penv->ops->resume(&pdev->dev);
+ ret = penv->ops->resume(&pdev->dev);
out:
if (ret == 0)
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..4635edf0189b 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -1796,6 +1796,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 +1899,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 +2049,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 +2366,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;
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 468a7bcd8dbd..5612645d7237 100644
--- a/drivers/usb/gadget/function/f_gsi.c
+++ b/drivers/usb/gadget/function/f_gsi.c
@@ -256,8 +256,14 @@ static int ipa_connect_channels(struct gsi_data_port *d_port)
in_params->dir = GSI_CHAN_DIR_FROM_GSI;
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 =
- d_port->in_last_trb_addr = gsi_channel_info.last_trb_addr;
+ in_params->xfer_scratch.last_trb_addr_iova =
+ 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,8 +295,14 @@ 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->xfer_scratch.last_trb_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 =
gsi_channel_info.const_buffer_size;
@@ -2787,7 +2799,7 @@ static ssize_t gsi_info_show(struct config_item *item, char *page)
ipa_chnl_params->xfer_scratch.depcmd_low_addr);
len += scnprintf(buf + len, PAGE_SIZE - len,
"%25s %10x\n", "IN LastTRB Addr Off: ",
- ipa_chnl_params->xfer_scratch.last_trb_addr);
+ ipa_chnl_params->xfer_scratch.last_trb_addr_iova);
len += scnprintf(buf + len, PAGE_SIZE - len,
"%25s %10u\n", "IN Buffer Size: ",
ipa_chnl_params->xfer_scratch.const_buffer_size);
@@ -2821,7 +2833,7 @@ static ssize_t gsi_info_show(struct config_item *item, char *page)
ipa_chnl_params->xfer_scratch.depcmd_low_addr);
len += scnprintf(buf + len, PAGE_SIZE - len,
"%25s %10x\n", "OUT LastTRB Addr Off: ",
- ipa_chnl_params->xfer_scratch.last_trb_addr);
+ ipa_chnl_params->xfer_scratch.last_trb_addr_iova);
len += scnprintf(buf + len, PAGE_SIZE - len,
"%25s %10u\n", "OUT Buffer Size: ",
ipa_chnl_params->xfer_scratch.const_buffer_size);
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index 8dd65884067f..f29c275c490b 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -733,12 +733,13 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
pd->current_dr = DR_UFP;
if (pd->psy_type == POWER_SUPPLY_TYPE_USB ||
- pd->psy_type == POWER_SUPPLY_TYPE_USB_CDP)
+ pd->psy_type == POWER_SUPPLY_TYPE_USB_CDP) {
extcon_set_cable_state_(pd->extcon,
EXTCON_USB_CC,
is_cable_flipped(pd));
extcon_set_cable_state_(pd->extcon,
EXTCON_USB, 1);
+ }
}
pd->rx_msg_len = 0;
diff --git a/drivers/usb/phy/phy-msm-qusb-v2.c b/drivers/usb/phy/phy-msm-qusb-v2.c
index 1bc6b6ba4517..5a29fc96f940 100644
--- a/drivers/usb/phy/phy-msm-qusb-v2.c
+++ b/drivers/usb/phy/phy-msm-qusb-v2.c
@@ -66,6 +66,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");
@@ -87,6 +90,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 +379,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 +523,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 +563,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 +574,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);
@@ -603,6 +653,9 @@ static int qusb_phy_notify_connect(struct usb_phy *phy,
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,
@@ -767,9 +820,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 +927,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));
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/Kconfig b/drivers/video/fbdev/msm/Kconfig
index c49ce06430be..ef5c96214c19 100644
--- a/drivers/video/fbdev/msm/Kconfig
+++ b/drivers/video/fbdev/msm/Kconfig
@@ -96,13 +96,13 @@ config FB_MSM_MDSS_DSI_CTRL_STATUS
fails to acknowledge the BTA command, it sends PANEL_ALIVE=0 status
to HAL layer to reset the controller.
-config FB_MSM_MDSS_EDP_PANEL
+config FB_MSM_MDSS_DP_PANEL
depends on FB_MSM_MDSS
- bool "MDSS eDP Panel"
+ bool "MDSS DP Panel"
---help---
- The MDSS eDP Panel provides support for eDP host controller driver
+ The MDSS DP Panel provides support for DP host controller driver
which runs in Video mode only and is responsible for transmitting
- frame buffer from host SOC to eDP display panel.
+ frame buffer from host SOC to DP display panel.
config FB_MSM_MDSS_MDP3
depends on FB_MSM_MDSS
diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile
index 9d25b08c753a..dccd7bcc7219 100644
--- a/drivers/video/fbdev/msm/Makefile
+++ b/drivers/video/fbdev/msm/Makefile
@@ -43,8 +43,8 @@ obj-$(CONFIG_FB_MSM_MDSS) += mdss_hdmi_util.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss_hdmi_edid.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss_cec_core.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss_dba_utils.o
-obj-$(CONFIG_FB_MSM_MDSS_EDP_PANEL) += mdss_edp.o
-obj-$(CONFIG_FB_MSM_MDSS_EDP_PANEL) += mdss_edp_aux.o
+obj-$(CONFIG_FB_MSM_MDSS_DP_PANEL) += mdss_dp.o mdss_dp_util.o
+obj-$(CONFIG_FB_MSM_MDSS_DP_PANEL) += mdss_dp_aux.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss_io_util.o
obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_tx.o
diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c
new file mode 100644
index 000000000000..07e3445c51f7
--- /dev/null
+++ b/drivers/video/fbdev/msm/mdss_dp.c
@@ -0,0 +1,1148 @@
+/* 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.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/err.h>
+#include <linux/regulator/consumer.h>
+#include <linux/qpnp/pwm.h>
+#include <linux/clk.h>
+#include <linux/spinlock_types.h>
+#include <linux/kthread.h>
+
+#include "mdss.h"
+#include "mdss_dp.h"
+#include "mdss_dp_util.h"
+#include "mdss_debug.h"
+
+#define RGB_COMPONENTS 3
+#define VDDA_MIN_UV 1800000 /* uV units */
+#define VDDA_MAX_UV 1800000 /* uV units */
+#define VDDA_UA_ON_LOAD 100000 /* uA units */
+#define VDDA_UA_OFF_LOAD 100 /* uA units */
+
+
+
+static void mdss_dp_put_dt_clk_data(struct device *dev,
+ struct dss_module_power *module_power)
+{
+ if (!module_power) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ if (module_power->clk_config) {
+ devm_kfree(dev, module_power->clk_config);
+ module_power->clk_config = NULL;
+ }
+ module_power->num_clk = 0;
+} /* mdss_dp_put_dt_clk_data */
+
+static int mdss_dp_is_clk_prefix(const char *clk_prefix, const char *clk_name)
+{
+ return !strncmp(clk_name, clk_prefix, strlen(clk_prefix));
+}
+
+static int mdss_dp_init_clk_power_data(struct device *dev,
+ struct mdss_dp_drv_pdata *pdata)
+{
+ int num_clk = 0, i = 0, rc = 0;
+ int core_clk_count = 0, ctrl_clk_count = 0;
+ const char *core_clk = "core";
+ const char *ctrl_clk = "ctrl";
+ struct dss_module_power *core_power_data = NULL;
+ struct dss_module_power *ctrl_power_data = NULL;
+ const char *clk_name;
+
+ num_clk = of_property_count_strings(dev->of_node,
+ "clock-names");
+ if (num_clk <= 0) {
+ pr_err("no clocks are defined\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ core_power_data = &pdata->power_data[DP_CORE_PM];
+ ctrl_power_data = &pdata->power_data[DP_CTRL_PM];
+
+ for (i = 0; i < num_clk; i++) {
+ of_property_read_string_index(dev->of_node, "clock-names",
+ i, &clk_name);
+
+ if (mdss_dp_is_clk_prefix(core_clk, clk_name))
+ core_clk_count++;
+ if (mdss_dp_is_clk_prefix(ctrl_clk, clk_name))
+ ctrl_clk_count++;
+ }
+
+ /* Initialize the CORE power module */
+ if (core_clk_count <= 0) {
+ pr_err("no core clocks are defined\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ core_power_data->num_clk = core_clk_count;
+ core_power_data->clk_config = devm_kzalloc(dev, sizeof(struct dss_clk) *
+ core_power_data->num_clk, GFP_KERNEL);
+ if (!core_power_data->clk_config) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Initialize the CTRL power module */
+ if (ctrl_clk_count <= 0) {
+ pr_err("no ctrl clocks are defined\n");
+ rc = -EINVAL;
+ goto ctrl_clock_error;
+ }
+
+ ctrl_power_data->num_clk = ctrl_clk_count;
+ ctrl_power_data->clk_config = devm_kzalloc(dev, sizeof(struct dss_clk) *
+ ctrl_power_data->num_clk, GFP_KERNEL);
+ if (!ctrl_power_data->clk_config) {
+ ctrl_power_data->num_clk = 0;
+ rc = -EINVAL;
+ goto ctrl_clock_error;
+ }
+
+ return rc;
+
+ctrl_clock_error:
+ mdss_dp_put_dt_clk_data(dev, core_power_data);
+exit:
+ return rc;
+}
+
+static int mdss_dp_get_dt_clk_data(struct device *dev,
+ struct mdss_dp_drv_pdata *pdata)
+{
+ int rc = 0, i = 0;
+ const char *clk_name;
+ int num_clk = 0;
+ int core_clk_index = 0, ctrl_clk_index = 0;
+ int core_clk_count = 0, ctrl_clk_count = 0;
+ const char *core_clk = "core";
+ const char *ctrl_clk = "ctrl";
+ struct dss_module_power *core_power_data = NULL;
+ struct dss_module_power *ctrl_power_data = NULL;
+
+ if (!dev || !pdata) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ rc = mdss_dp_init_clk_power_data(dev, pdata);
+ if (rc) {
+ pr_err("failed to initialize power data\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ core_power_data = &pdata->power_data[DP_CORE_PM];
+ core_clk_count = core_power_data->num_clk;
+ ctrl_power_data = &pdata->power_data[DP_CTRL_PM];
+ ctrl_clk_count = ctrl_power_data->num_clk;
+
+ num_clk = core_clk_count + ctrl_clk_count;
+
+ for (i = 0; i < num_clk; i++) {
+ of_property_read_string_index(dev->of_node, "clock-names",
+ i, &clk_name);
+
+ if (mdss_dp_is_clk_prefix(core_clk, clk_name)
+ && core_clk_index < core_clk_count) {
+ struct dss_clk *clk =
+ &core_power_data->clk_config[core_clk_index];
+ strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
+ clk->type = DSS_CLK_AHB;
+ core_clk_index++;
+ } else if (mdss_dp_is_clk_prefix(ctrl_clk, clk_name)
+ && ctrl_clk_index < ctrl_clk_count) {
+ struct dss_clk *clk =
+ &ctrl_power_data->clk_config[ctrl_clk_index];
+ strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
+ ctrl_clk_index++;
+ if (!strcmp(clk_name, "ctrl_link_clk"))
+ clk->type = DSS_CLK_PCLK;
+ else if (!strcmp(clk_name, "ctrl_pixel_clk"))
+ clk->type = DSS_CLK_PCLK;
+ else
+ clk->type = DSS_CLK_AHB;
+ }
+ }
+
+ pr_debug("Display-port clock parsing successful\n");
+
+exit:
+ return rc;
+} /* mdss_dp_get_dt_clk_data */
+
+/*
+ * This clock control function supports enabling/disabling
+ * of core and ctrl power module clocks
+ */
+static int mdss_dp_clk_ctrl(struct mdss_dp_drv_pdata *dp_drv,
+ int pm_type, bool enable)
+{
+ int ret = 0;
+
+ if ((pm_type != DP_CORE_PM)
+ && (pm_type != DP_CTRL_PM)) {
+ pr_err("unsupported power module: %s\n",
+ __mdss_dp_pm_name(pm_type));
+ return -EINVAL;
+ }
+
+ if (enable) {
+ if ((pm_type == DP_CORE_PM)
+ && (dp_drv->core_clks_on)) {
+ pr_debug("core clks already enabled\n");
+ return 0;
+ }
+
+ if ((pm_type == DP_CTRL_PM)
+ && (dp_drv->link_clks_on)) {
+ pr_debug("links clks already enabled\n");
+ return 0;
+ }
+
+ if ((pm_type == DP_CTRL_PM)
+ && (!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);
+ if (ret) {
+ pr_err("failed to enable clks for %s\n",
+ __mdss_dp_pm_name(pm_type));
+ 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;
+ }
+ }
+
+ if (pm_type == DP_CORE_PM)
+ dp_drv->core_clks_on = enable;
+ else
+ dp_drv->link_clks_on = enable;
+
+error:
+ return ret;
+}
+
+static int mdss_dp_regulator_ctrl(struct mdss_dp_drv_pdata *dp_drv,
+ bool enable)
+{
+ int i, ret = 0;
+
+ if (dp_drv->core_power == enable) {
+ pr_debug("regulators already %s\n",
+ enable ? "enabled" : "disabled");
+ 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",
+ __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;
+ }
+ }
+ }
+
+ dp_drv->core_power = enable;
+
+error:
+ return ret;
+}
+
+static void mdss_dp_put_dt_vreg_data(struct device *dev,
+ struct dss_module_power *module_power)
+{
+ if (!module_power) {
+ DEV_ERR("invalid input\n");
+ return;
+ }
+
+ if (module_power->vreg_config) {
+ devm_kfree(dev, module_power->vreg_config);
+ module_power->vreg_config = NULL;
+ }
+ module_power->num_vreg = 0;
+} /* mdss_dp_put_dt_vreg_data */
+
+static int mdss_dp_get_dt_vreg_data(struct device *dev,
+ struct device_node *of_node, struct dss_module_power *mp,
+ enum dp_pm_type module)
+{
+ int i = 0, rc = 0;
+ u32 tmp = 0;
+ struct device_node *supply_node = NULL;
+ const char *pm_supply_name = NULL;
+ struct device_node *supply_root_node = NULL;
+
+ if (!dev || !mp) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ return rc;
+ }
+
+ mp->num_vreg = 0;
+ pm_supply_name = __mdss_dp_pm_supply_node_name(module);
+ supply_root_node = of_get_child_by_name(of_node, pm_supply_name);
+ if (!supply_root_node) {
+ pr_err("no supply entry present: %s\n", pm_supply_name);
+ goto novreg;
+ }
+
+ mp->num_vreg =
+ of_get_available_child_count(supply_root_node);
+
+ if (mp->num_vreg == 0) {
+ pr_debug("no vreg\n");
+ goto novreg;
+ } else {
+ pr_debug("vreg found. count=%d\n", mp->num_vreg);
+ }
+
+ mp->vreg_config = devm_kzalloc(dev, sizeof(struct dss_vreg) *
+ mp->num_vreg, GFP_KERNEL);
+ if (!mp->vreg_config) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ for_each_child_of_node(supply_root_node, supply_node) {
+ const char *st = NULL;
+ /* vreg-name */
+ rc = of_property_read_string(supply_node,
+ "qcom,supply-name", &st);
+ if (rc) {
+ pr_err("error reading name. rc=%d\n",
+ rc);
+ goto error;
+ }
+ snprintf(mp->vreg_config[i].vreg_name,
+ ARRAY_SIZE((mp->vreg_config[i].vreg_name)), "%s", st);
+ /* vreg-min-voltage */
+ 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;
+
+ /* vreg-max-voltage */
+ 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;
+
+ /* enable-load */
+ 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;
+
+ /* disable-load */
+ 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;
+
+ pr_debug("%s min=%d, max=%d, enable=%d, disable=%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
+ );
+ ++i;
+ }
+
+ return rc;
+
+error:
+ if (mp->vreg_config) {
+ devm_kfree(dev, mp->vreg_config);
+ mp->vreg_config = NULL;
+ }
+novreg:
+ mp->num_vreg = 0;
+
+ return rc;
+} /* mdss_dp_get_dt_vreg_data */
+
+static int mdss_dp_regulator_init(struct platform_device *pdev,
+ struct mdss_dp_drv_pdata *dp_drv)
+{
+ int rc = 0, i = 0, j = 0;
+
+ if (!pdev || !dp_drv) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ for (i = DP_CORE_PM; !rc && (i < DP_MAX_PM); i++) {
+ rc = msm_dss_config_vreg(&pdev->dev,
+ dp_drv->power_data[i].vreg_config,
+ dp_drv->power_data[i].num_vreg, 1);
+ if (rc) {
+ pr_err("failed to init vregs for %s\n",
+ __mdss_dp_pm_name(i));
+ for (j = i-1; j >= DP_CORE_PM; j--) {
+ msm_dss_config_vreg(&pdev->dev,
+ dp_drv->power_data[j].vreg_config,
+ dp_drv->power_data[j].num_vreg, 0);
+ }
+ }
+ }
+
+ return rc;
+}
+
+void mdss_dp_phy_initialize(struct mdss_dp_drv_pdata *dp)
+{
+ /*
+ * To siwtch the usb3_phy to operate in DP mode, the phy and PLL
+ * should have the reset lines asserted
+ */
+ mdss_dp_assert_phy_reset(&dp->ctrl_io, true);
+ /* Delay to make sure the assert is propagated */
+ udelay(2000);
+ mdss_dp_switch_usb3_phy_to_dp_mode(&dp->tcsr_reg_io);
+ wmb(); /* ensure that the register write is successful */
+ mdss_dp_assert_phy_reset(&dp->ctrl_io, false);
+}
+
+void mdss_dp_config_ctrl(struct mdss_dp_drv_pdata *dp)
+{
+ struct dpcd_cap *cap;
+ struct display_timing_desc *timing;
+ u32 data = 0;
+
+ timing = &dp->edid.timing[0];
+
+ cap = &dp->dpcd;
+
+ data = dp->lane_cnt - 1;
+ data <<= 4;
+
+ if (cap->enhanced_frame)
+ data |= 0x40;
+
+ if (dp->edid.color_depth == 8) {
+ /* 0 == 6 bits, 1 == 8 bits */
+ data |= 0x100; /* bit 8 */
+ }
+
+ if (!timing->interlaced) /* progressive */
+ data |= 0x04;
+
+ data |= 0x03; /* sycn clock & static Mvid */
+
+ mdss_dp_configuration_ctrl(&dp->ctrl_io, data);
+}
+
+int mdss_dp_wait4train(struct mdss_dp_drv_pdata *dp_drv)
+{
+ int ret = 0;
+
+ if (dp_drv->cont_splash)
+ return ret;
+
+ ret = wait_for_completion_timeout(&dp_drv->video_comp, 30);
+ if (ret <= 0) {
+ pr_err("Link Train timedout\n");
+ ret = -EINVAL;
+ } else {
+ ret = 0;
+ }
+
+ pr_debug("End--\n");
+
+ return ret;
+}
+
+#define DEFAULT_VIDEO_RESOLUTION HDMI_VFRMT_640x480p60_4_3
+
+static int dp_init_panel_info(struct mdss_dp_drv_pdata *dp_drv)
+{
+ struct mdss_panel_info *pinfo;
+ struct msm_hdmi_mode_timing_info timing = {0};
+ u32 ret;
+
+ if (!dp_drv) {
+ DEV_ERR("invalid input\n");
+ return -EINVAL;
+ }
+
+ dp_drv->ds_data.ds_registered = false;
+ ret = hdmi_get_supported_mode(&timing, &dp_drv->ds_data,
+ DEFAULT_VIDEO_RESOLUTION);
+ pinfo = &dp_drv->panel_data.panel_info;
+
+ if (ret || !timing.supported || !pinfo) {
+ DEV_ERR("%s: invalid timing data\n", __func__);
+ return -EINVAL;
+ }
+
+ pinfo->xres = timing.active_h;
+ pinfo->yres = timing.active_v;
+ pinfo->clk_rate = timing.pixel_freq * 1000;
+
+ pinfo->lcdc.h_back_porch = timing.back_porch_h;
+ pinfo->lcdc.h_front_porch = timing.front_porch_h;
+ pinfo->lcdc.h_pulse_width = timing.pulse_width_h;
+ pinfo->lcdc.v_back_porch = timing.back_porch_v;
+ pinfo->lcdc.v_front_porch = timing.front_porch_v;
+ pinfo->lcdc.v_pulse_width = timing.pulse_width_v;
+
+ pinfo->type = EDP_PANEL;
+ pinfo->pdest = DISPLAY_4;
+ pinfo->wait_cycle = 0;
+ pinfo->bpp = 24;
+ pinfo->fb_num = 1;
+
+ pinfo->lcdc.border_clr = 0; /* blk */
+ pinfo->lcdc.underflow_clr = 0xff; /* blue */
+ pinfo->lcdc.hsync_skew = 0;
+
+ return 0;
+} /* dp_init_panel_info */
+
+
+int mdss_dp_on(struct mdss_panel_data *pdata)
+{
+ struct mdss_dp_drv_pdata *dp_drv = NULL;
+ int ret = 0;
+
+ if (!pdata) {
+ pr_err("Invalid input data\n");
+ return -EINVAL;
+ }
+
+ dp_drv = container_of(pdata, struct mdss_dp_drv_pdata,
+ panel_data);
+
+ pr_debug("++ cont_splash=%d\n", dp_drv->cont_splash);
+
+ if (!dp_drv->cont_splash) { /* vote for clocks */
+ ret = mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, true);
+ if (ret) {
+ pr_err("Unabled to start core clocks\n");
+ return ret;
+ }
+ 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);
+
+ 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);
+ ret = mdss_dp_clk_ctrl(dp_drv, DP_CTRL_PM, true);
+ if (ret) {
+ mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, false);
+ pr_err("Unabled to start link clocks\n");
+ return ret;
+ }
+
+ mdss_dp_mainlink_reset(&dp_drv->ctrl_io);
+
+ reinit_completion(&dp_drv->idle_comp);
+ mdss_dp_fill_link_cfg(dp_drv);
+ mdss_dp_mainlink_ctrl(&dp_drv->ctrl_io, true);
+ mdss_dp_config_ctrl(dp_drv);
+ mdss_dp_sw_mvid_nvid(&dp_drv->ctrl_io);
+ mdss_dp_timing_cfg(&dp_drv->ctrl_io,
+ &dp_drv->panel_data.panel_info);
+ } else {
+ mdss_dp_aux_ctrl(&dp_drv->ctrl_io, true);
+ }
+
+ pr_debug("call link_training\n");
+ mdss_dp_link_train(dp_drv);
+
+ mdss_dp_wait4train(dp_drv);
+
+ dp_drv->cont_splash = 0;
+
+ if (mdss_dp_mainlink_ready(dp_drv, BIT(0)))
+ pr_debug("mainlink ready\n");
+
+ pr_debug("End-\n");
+ return ret;
+}
+
+int mdss_dp_off(struct mdss_panel_data *pdata)
+{
+ struct mdss_dp_drv_pdata *dp_drv = NULL;
+ int ret = 0;
+
+ dp_drv = container_of(pdata, struct mdss_dp_drv_pdata,
+ panel_data);
+ if (!dp_drv) {
+ pr_err("Invalid input data\n");
+ return -EINVAL;
+ }
+ pr_debug("Entered++, cont_splash=%d\n", dp_drv->cont_splash);
+
+ /* wait until link training is completed */
+ mutex_lock(&dp_drv->train_mutex);
+
+ reinit_completion(&dp_drv->idle_comp);
+ mdss_dp_state_ctrl(&dp_drv->ctrl_io, ST_PUSH_IDLE);
+
+ ret = wait_for_completion_timeout(&dp_drv->idle_comp,
+ msecs_to_jiffies(100));
+ if (ret == 0)
+ pr_err("idle pattern timedout\n");
+
+ mdss_dp_state_ctrl(&dp_drv->ctrl_io, 0);
+
+ mdss_dp_irq_disable(dp_drv);
+
+ mdss_dp_mainlink_reset(&dp_drv->ctrl_io);
+ mdss_dp_mainlink_ctrl(&dp_drv->ctrl_io, 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);
+
+ mdss_dp_regulator_ctrl(dp_drv, false);
+
+ pr_debug("End--: state_ctrl=%x\n",
+ dp_read(dp_drv->base + DP_STATE_CTRL));
+
+ mutex_unlock(&dp_drv->train_mutex);
+ return 0;
+}
+
+static int mdss_dp_host_init(struct mdss_panel_data *pdata)
+{
+ struct mdss_dp_drv_pdata *dp_drv = NULL;
+ int ret = 0;
+
+ if (!pdata) {
+ pr_err("Invalid input data\n");
+ return -EINVAL;
+ }
+
+ dp_drv = container_of(pdata, struct mdss_dp_drv_pdata,
+ panel_data);
+
+ ret = mdss_dp_regulator_ctrl(dp_drv, true);
+ if (ret) {
+ pr_err("failed to enable regulators\n");
+ goto vreg_error;
+ }
+
+ ret = mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, true);
+ if (ret) {
+ pr_err("Unabled to start core clocks\n");
+ goto clk_error;
+ }
+
+ mdss_dp_aux_init(dp_drv);
+
+ mdss_dp_phy_reset(&dp_drv->ctrl_io);
+ mdss_dp_aux_reset(&dp_drv->ctrl_io);
+ mdss_dp_phy_initialize(dp_drv);
+ mdss_dp_aux_ctrl(&dp_drv->ctrl_io, true);
+
+ return ret;
+
+clk_error:
+ mdss_dp_regulator_ctrl(dp_drv, false);
+vreg_error:
+ return ret;
+}
+
+static int mdss_dp_event_handler(struct mdss_panel_data *pdata,
+ int event, void *arg)
+{
+ int rc = 0;
+
+ pr_debug("event=%d\n", event);
+ switch (event) {
+ case MDSS_EVENT_UNBLANK:
+ rc = mdss_dp_on(pdata);
+ break;
+ case MDSS_EVENT_PANEL_OFF:
+ rc = mdss_dp_off(pdata);
+ break;
+ }
+ return rc;
+}
+
+static int mdss_dp_remove(struct platform_device *pdev)
+{
+ struct mdss_dp_drv_pdata *dp_drv = NULL;
+
+ dp_drv = platform_get_drvdata(pdev);
+
+ iounmap(dp_drv->ctrl_io.base);
+ dp_drv->ctrl_io.base = NULL;
+ iounmap(dp_drv->phy_io.base);
+ dp_drv->phy_io.base = NULL;
+
+ return 0;
+}
+
+static int mdss_dp_device_register(struct mdss_dp_drv_pdata *dp_drv)
+{
+ int ret;
+
+ ret = dp_init_panel_info(dp_drv);
+ if (ret) {
+ DEV_ERR("%s: dp_init_panel_info failed\n", __func__);
+ return ret;
+ }
+
+ dp_drv->panel_data.event_handler = mdss_dp_event_handler;
+
+ dp_drv->panel_data.panel_info.cont_splash_enabled =
+ dp_drv->cont_splash;
+
+ ret = mdss_register_panel(dp_drv->pdev, &dp_drv->panel_data);
+ if (ret) {
+ dev_err(&(dp_drv->pdev->dev), "unable to register dp\n");
+ return ret;
+ }
+
+ pr_info("dp initialized\n");
+
+ return 0;
+}
+
+/*
+ * Retrieve dp Resources
+ */
+static int mdss_retrieve_dp_ctrl_resources(struct platform_device *pdev,
+ struct mdss_dp_drv_pdata *dp_drv)
+{
+ int rc = 0;
+ u32 index;
+
+ rc = of_property_read_u32(pdev->dev.of_node, "cell-index", &index);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "Cell-index not specified, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = msm_dss_ioremap_byname(pdev, &dp_drv->ctrl_io, "dp_ctrl");
+ if (rc) {
+ pr_err("%d unable to remap dp ctrl resources\n",
+ __LINE__);
+ return rc;
+ }
+ dp_drv->base = dp_drv->ctrl_io.base;
+ dp_drv->base_size = dp_drv->ctrl_io.len;
+
+ rc = msm_dss_ioremap_byname(pdev, &dp_drv->phy_io, "dp_phy");
+ if (rc) {
+ pr_err("%d unable to remap dp PHY resources\n",
+ __LINE__);
+ return rc;
+ }
+
+ rc = msm_dss_ioremap_byname(pdev, &dp_drv->tcsr_reg_io,
+ "tcsr_regs");
+ if (rc) {
+ pr_err("%d unable to remap dp tcsr_reg resources\n",
+ __LINE__);
+ return rc;
+ }
+
+ pr_debug("DP Driver base=%p size=%x\n",
+ dp_drv->base, dp_drv->base_size);
+
+ mdss_debug_register_base("dp",
+ dp_drv->base, dp_drv->base_size, NULL);
+
+ return 0;
+}
+
+static void mdss_dp_video_ready(struct mdss_dp_drv_pdata *dp)
+{
+ pr_debug("dp_video_ready\n");
+ complete(&dp->video_comp);
+}
+
+static void mdss_dp_idle_patterns_sent(struct mdss_dp_drv_pdata *dp)
+{
+ pr_debug("idle_patterns_sent\n");
+ complete(&dp->idle_comp);
+}
+
+static void mdss_dp_do_link_train(struct mdss_dp_drv_pdata *dp)
+{
+ if (dp->cont_splash)
+ return;
+
+ mdss_dp_link_train(dp);
+}
+
+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;
+
+ if (!dw) {
+ pr_err("invalid work structure\n");
+ return;
+ }
+
+ dp = container_of(dw, struct mdss_dp_drv_pdata, dwork);
+
+ spin_lock_irqsave(&dp->event_lock, flag);
+ todo = dp->current_event;
+ dp->current_event = 0;
+ spin_unlock_irqrestore(&dp->event_lock, flag);
+
+ pr_debug("todo=%x\n", todo);
+
+ switch (todo) {
+ case (EV_EDID_READ):
+ mdss_dp_edid_read(dp, 0);
+ break;
+ case (EV_DPCD_CAP_READ):
+ mdss_dp_dpcd_cap_read(dp);
+ break;
+ case (EV_DPCD_STATUS_READ):
+ mdss_dp_dpcd_status_read(dp);
+ break;
+ case (EV_LINK_TRAIN):
+ mdss_dp_do_link_train(dp);
+ break;
+ case (EV_VIDEO_READY):
+ mdss_dp_video_ready(dp);
+ break;
+ case (EV_IDLE_PATTERNS_SENT):
+ mdss_dp_idle_patterns_sent(dp);
+ break;
+ default:
+ pr_err("Unknown event:%d\n", todo);
+ }
+}
+
+static void dp_send_events(struct mdss_dp_drv_pdata *dp, u32 events)
+{
+ spin_lock(&dp->event_lock);
+ dp->current_event = events;
+ queue_delayed_work(dp->workq,
+ &dp->dwork, HZ);
+ spin_unlock(&dp->event_lock);
+}
+
+irqreturn_t dp_isr(int irq, void *ptr)
+{
+ struct mdss_dp_drv_pdata *dp = (struct mdss_dp_drv_pdata *)ptr;
+ unsigned char *base = dp->base;
+ u32 isr1, isr2, mask1, mask2;
+ u32 ack;
+
+ spin_lock(&dp->lock);
+ isr1 = dp_read(base + DP_INTR_STATUS);
+ isr2 = dp_read(base + DP_INTR_STATUS2);
+
+ mask1 = isr1 & dp->mask1;
+ mask2 = isr2 & dp->mask2;
+
+ isr1 &= ~mask1; /* remove masks bit */
+ isr2 &= ~mask2;
+
+ pr_debug("isr=%x mask=%x isr2=%x mask2=%x\n",
+ isr1, mask1, isr2, mask2);
+
+ ack = isr1 & EDP_INTR_STATUS1;
+ ack <<= 1; /* ack bits */
+ ack |= mask1;
+ dp_write(base + DP_INTR_STATUS, ack);
+
+ ack = isr2 & EDP_INTR_STATUS2;
+ ack <<= 1; /* ack bits */
+ ack |= mask2;
+ dp_write(base + DP_INTR_STATUS2, ack);
+ spin_unlock(&dp->lock);
+
+ if (isr1 & EDP_INTR_HPD) {
+ isr1 &= ~EDP_INTR_HPD; /* clear */
+ mdss_dp_host_init(&dp->panel_data);
+ dp_send_events(dp, EV_LINK_TRAIN);
+ }
+
+ if (isr2 & EDP_INTR_READY_FOR_VIDEO)
+ dp_send_events(dp, EV_VIDEO_READY);
+
+ if (isr2 & EDP_INTR_IDLE_PATTERNs_SENT)
+ dp_send_events(dp, EV_IDLE_PATTERNS_SENT);
+
+ if (isr1 && dp->aux_cmd_busy) {
+ /* clear DP_AUX_TRANS_CTRL */
+ dp_write(base + DP_AUX_TRANS_CTRL, 0);
+ /* read DP_INTERRUPT_TRANS_NUM */
+ dp->aux_trans_num =
+ dp_read(base + DP_INTERRUPT_TRANS_NUM);
+
+ if (dp->aux_cmd_i2c)
+ dp_aux_i2c_handler(dp, isr1);
+ else
+ dp_aux_native_handler(dp, isr1);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int mdss_dp_event_setup(struct mdss_dp_drv_pdata *dp)
+{
+
+ spin_lock_init(&dp->event_lock);
+ dp->workq = create_workqueue("mdss_dp_hpd");
+ if (!dp->workq) {
+ pr_err("%s: Error creating workqueue\n", __func__);
+ return -EPERM;
+ }
+
+ INIT_DELAYED_WORK(&dp->dwork, mdss_dp_event_work);
+ return 0;
+}
+
+static int mdss_dp_probe(struct platform_device *pdev)
+{
+ int ret, i;
+ struct mdss_dp_drv_pdata *dp_drv;
+ struct mdss_panel_cfg *pan_cfg = NULL;
+ struct mdss_util_intf *util;
+
+ util = mdss_get_util_intf();
+ if (!util) {
+ pr_err("Failed to get mdss utility functions\n");
+ return -ENODEV;
+ }
+
+ if (!util->mdp_probe_done) {
+ pr_err("MDP not probed yet!\n");
+ return -EPROBE_DEFER;
+ }
+
+ if (!pdev || !pdev->dev.of_node) {
+ pr_err("pdev not found for DP controller\n");
+ return -ENODEV;
+ }
+
+ pan_cfg = mdss_panel_intf_type(MDSS_PANEL_INTF_EDP);
+ if (IS_ERR(pan_cfg)) {
+ return PTR_ERR(pan_cfg);
+ } else if (pan_cfg) {
+ pr_debug("DP as prim not supported\n");
+ return -ENODEV;
+ }
+
+ dp_drv = devm_kzalloc(&pdev->dev, sizeof(*dp_drv), GFP_KERNEL);
+ if (dp_drv == NULL)
+ return -ENOMEM;
+
+ dp_drv->pdev = pdev;
+ dp_drv->pdev->id = 1;
+ dp_drv->mdss_util = util;
+ dp_drv->clk_on = 0;
+ dp_drv->aux_rate = 19200000;
+ dp_drv->mask1 = EDP_INTR_MASK1;
+ dp_drv->mask2 = EDP_INTR_MASK2;
+ mutex_init(&dp_drv->emutex);
+ spin_lock_init(&dp_drv->lock);
+
+ ret = mdss_retrieve_dp_ctrl_resources(pdev, dp_drv);
+ if (ret)
+ goto probe_err;
+
+ /* Parse the regulator information */
+ for (i = DP_CORE_PM; i < DP_MAX_PM; i++) {
+ ret = mdss_dp_get_dt_vreg_data(&pdev->dev,
+ pdev->dev.of_node, &dp_drv->power_data[i], i);
+ if (ret) {
+ pr_err("get_dt_vreg_data failed for %s. rc=%d\n",
+ __mdss_dp_pm_name(i), ret);
+ i--;
+ for (; i >= DP_CORE_PM; i--)
+ mdss_dp_put_dt_vreg_data(&pdev->dev,
+ &dp_drv->power_data[i]);
+ goto probe_err;
+ }
+ }
+
+ ret = mdss_dp_get_dt_clk_data(&pdev->dev, dp_drv);
+ if (ret) {
+ DEV_ERR("get_dt_clk_data failed.ret=%d\n",
+ ret);
+ goto probe_err;
+ }
+
+ ret = mdss_dp_regulator_init(pdev, dp_drv);
+ if (ret)
+ goto probe_err;
+
+ ret = mdss_dp_irq_setup(dp_drv);
+ if (ret)
+ goto probe_err;
+
+ ret = mdss_dp_event_setup(dp_drv);
+ if (ret)
+ goto probe_err;
+
+ ret = mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, true);
+ if (ret) {
+ pr_err("Unabled to enable core clocks\n");
+ 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);
+ if (ret) {
+ pr_err("Unabled to disable core clocks\n");
+ 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");
+
+ return 0;
+
+probe_err:
+ iounmap(dp_drv->ctrl_io.base);
+ iounmap(dp_drv->phy_io.base);
+ if (dp_drv)
+ devm_kfree(&pdev->dev, dp_drv);
+ return ret;
+
+}
+
+static const struct of_device_id msm_mdss_dp_dt_match[] = {
+ {.compatible = "qcom,mdss-dp"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, msm_mdss_dp_dt_match);
+
+static struct platform_driver mdss_dp_driver = {
+ .probe = mdss_dp_probe,
+ .remove = mdss_dp_remove,
+ .shutdown = NULL,
+ .driver = {
+ .name = "mdss_dp",
+ .of_match_table = msm_mdss_dp_dt_match,
+ },
+};
+
+static int __init mdss_dp_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&mdss_dp_driver);
+ if (ret) {
+ pr_err("driver register failed");
+ return ret;
+ }
+
+ return ret;
+}
+module_init(mdss_dp_init);
+
+static void __exit mdss_dp_driver_cleanup(void)
+{
+ platform_driver_unregister(&mdss_dp_driver);
+}
+module_exit(mdss_dp_driver_cleanup);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("DP controller driver");
diff --git a/drivers/video/fbdev/msm/mdss_edp.h b/drivers/video/fbdev/msm/mdss_dp.h
index cd83c382a227..b63318dcca06 100644
--- a/drivers/video/fbdev/msm/mdss_edp.h
+++ b/drivers/video/fbdev/msm/mdss_dp.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-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
@@ -11,13 +11,23 @@
*
*/
-#ifndef MDSS_EDP_H
-#define MDSS_EDP_H
+#ifndef MDSS_DP_H
+#define MDSS_DP_H
+#include <linux/list.h>
+#include <linux/mdss_io_util.h>
+#include <linux/irqreturn.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/gpio.h>
#include <linux/of_gpio.h>
-#define edp_read(offset) readl_relaxed((offset))
-#define edp_write(offset, data) writel_relaxed((data), (offset))
+#include "mdss_hdmi_util.h"
+#include "video/msm_hdmi_modes.h"
+#include "mdss.h"
+#include "mdss_panel.h"
+
+#define dp_read(offset) readl_relaxed((offset))
+#define dp_write(offset, data) writel_relaxed((data), (offset))
#define AUX_CMD_FIFO_LEN 144
#define AUX_CMD_MAX 16
@@ -73,7 +83,7 @@
#define EDP_INTR_STATUS1 \
- (EDP_INTR_HPD | EDP_INTR_AUX_I2C_DONE| \
+ (EDP_INTR_AUX_I2C_DONE| \
EDP_INTR_WRONG_ADDR | EDP_INTR_TIMEOUT | \
EDP_INTR_NACK_DEFER | EDP_INTR_WRONG_DATA_CNT | \
EDP_INTR_I2C_NACK | EDP_INTR_I2C_DEFER | \
@@ -94,17 +104,6 @@
#define EDP_INTR_MASK2 (EDP_INTR_STATUS2 << 2)
-#define EDP_MAINLINK_CTRL 0x004
-#define EDP_STATE_CTRL 0x008
-#define EDP_MAINLINK_READY 0x084
-
-#define EDP_AUX_CTRL 0x300
-#define EDP_INTERRUPT_STATUS 0x308
-#define EDP_INTERRUPT_STATUS_2 0x30c
-#define EDP_AUX_DATA 0x314
-#define EDP_AUX_TRANS_CTRL 0x318
-#define EDP_AUX_STATUS 0x324
-
#define EDP_PHY_EDPPHY_GLB_VM_CFG0 0x510
#define EDP_PHY_EDPPHY_GLB_VM_CFG1 0x514
@@ -127,6 +126,13 @@ struct edp_buf {
char i2c; /* 1 == i2c cmd, 0 == native cmd */
};
+enum dp_pm_type {
+ DP_CORE_PM,
+ DP_CTRL_PM,
+ DP_PHY_PM,
+ DP_MAX_PM
+};
+
#define DPCD_ENHANCED_FRAME BIT(0)
#define DPCD_TPS3 BIT(1)
#define DPCD_MAX_DOWNSPREAD_0_5 BIT(2)
@@ -156,9 +162,10 @@ struct edp_buf {
#define SINK_POWER_ON 1
#define SINK_POWER_OFF 2
-#define EDP_LINK_RATE_162 6 /* 1.62G = 270M * 6 */
-#define EDP_LINK_RATE_270 10 /* 2.70G = 270M * 10 */
-#define EDP_LINK_RATE_MAX EDP_LINK_RATE_270
+#define DP_LINK_RATE_162 6 /* 1.62G = 270M * 6 */
+#define DP_LINK_RATE_270 10 /* 2.70G = 270M * 10 */
+#define DP_LINK_RATE_540 20 /* 5.40G = 270M * 20 */
+#define DP_LINK_RATE_MAX DP_LINK_RATE_540
struct dpcd_cap {
char major;
@@ -215,7 +222,7 @@ struct edp_edid {
short id_product;
char version;
char revision;
- char video_intf; /* edp == 0x5 */
+ char video_intf; /* dp == 0x5 */
char color_depth; /* 6, 8, 10, 12 and 14 bits */
char color_format; /* RGB 4:4:4, YCrCb 4:4:4, Ycrcb 4:2:2 */
char dpm; /* display power management */
@@ -227,7 +234,7 @@ struct edp_edid {
struct display_timing_desc timing[4];
};
-struct edp_statistic {
+struct dp_statistic {
u32 intr_hpd;
u32 intr_aux_i2c_done;
u32 intr_wrong_addr;
@@ -251,9 +258,9 @@ struct edp_statistic {
#define DPCD_LINK_VOLTAGE_MAX 4
#define DPCD_LINK_PRE_EMPHASIS_MAX 4
-#define HPD_EVENT_MAX 8
+irqreturn_t dp_isr(int irq, void *ptr);
-struct mdss_edp_drv_pdata {
+struct mdss_dp_drv_pdata {
/* device driver */
int (*on) (struct mdss_panel_data *pdata);
int (*off) (struct mdss_panel_data *pdata);
@@ -263,10 +270,15 @@ struct mdss_edp_drv_pdata {
int clk_cnt;
int cont_splash;
bool inited;
- int delay_link_train;
+ bool core_power;
+ bool core_clks_on;
+ bool link_clks_on;
- /* edp specific */
+ /* dp specific */
unsigned char *base;
+ struct dss_io_data ctrl_io;
+ struct dss_io_data phy_io;
+ struct dss_io_data tcsr_reg_io;
int base_size;
unsigned char *mmss_cc_base;
u32 mask1;
@@ -275,8 +287,8 @@ struct mdss_edp_drv_pdata {
struct mdss_panel_data panel_data;
struct mdss_util_intf *mdss_util;
- int edp_on_cnt;
- int edp_off_cnt;
+ int dp_on_cnt;
+ int dp_off_cnt;
u32 pixel_rate;
u32 aux_rate;
@@ -289,26 +301,9 @@ struct mdss_edp_drv_pdata {
struct dpcd_cap dpcd;
/* regulators */
- struct regulator *vdda_vreg;
-
- /* clocks */
- struct clk *aux_clk;
- struct clk *pixel_clk;
- struct clk *ahb_clk;
- struct clk *link_clk;
- struct clk *mdp_core_clk;
+ struct dss_module_power power_data[DP_MAX_PM];
int clk_on;
- /* gpios */
- int gpio_panel_en;
- int gpio_lvl_en;
-
- /* backlight */
- struct pwm_device *bl_pwm;
- bool is_pwm_enabled;
- int lpg_channel;
- int pwm_period;
-
/* hpd */
int gpio_panel_hpd;
enum of_gpio_flags hpd_flags;
@@ -338,43 +333,51 @@ struct mdss_edp_drv_pdata {
char valid_boundary;
char delay_start;
u32 bpp;
- struct edp_statistic edp_stat;
+ struct dp_statistic dp_stat;
/* event */
- wait_queue_head_t event_q;
- u32 event_pndx;
- u32 event_gndx;
- u32 event_todo_list[HPD_EVENT_MAX];
+ struct workqueue_struct *workq;
+ struct delayed_work dwork;
+ u32 current_event;
spinlock_t event_lock;
spinlock_t lock;
+ struct hdmi_util_ds_data ds_data;
};
-int mdss_edp_aux_clk_enable(struct mdss_edp_drv_pdata *edp_drv);
-void mdss_edp_aux_clk_disable(struct mdss_edp_drv_pdata *edp_drv);
-int mdss_edp_clk_enable(struct mdss_edp_drv_pdata *edp_drv);
-void mdss_edp_clk_disable(struct mdss_edp_drv_pdata *edp_drv);
-int mdss_edp_clk_init(struct mdss_edp_drv_pdata *edp_drv);
-void mdss_edp_clk_deinit(struct mdss_edp_drv_pdata *edp_drv);
-int mdss_edp_prepare_aux_clocks(struct mdss_edp_drv_pdata *edp_drv);
-void mdss_edp_unprepare_aux_clocks(struct mdss_edp_drv_pdata *edp_drv);
-int mdss_edp_prepare_clocks(struct mdss_edp_drv_pdata *edp_drv);
-void mdss_edp_unprepare_clocks(struct mdss_edp_drv_pdata *edp_drv);
-
-void mdss_edp_dpcd_cap_read(struct mdss_edp_drv_pdata *edp);
-int mdss_edp_dpcd_status_read(struct mdss_edp_drv_pdata *edp);
-void mdss_edp_edid_read(struct mdss_edp_drv_pdata *edp, int block);
-int mdss_edp_link_train(struct mdss_edp_drv_pdata *edp);
-void edp_aux_i2c_handler(struct mdss_edp_drv_pdata *edp, u32 isr);
-void edp_aux_native_handler(struct mdss_edp_drv_pdata *edp, u32 isr);
-void mdss_edp_aux_init(struct mdss_edp_drv_pdata *ep);
-
-void mdss_edp_fill_link_cfg(struct mdss_edp_drv_pdata *ep);
-void mdss_edp_sink_power_down(struct mdss_edp_drv_pdata *ep);
-void mdss_edp_state_ctrl(struct mdss_edp_drv_pdata *ep, u32 state);
-int mdss_edp_sink_power_state(struct mdss_edp_drv_pdata *ep, char state);
-void mdss_edp_lane_power_ctrl(struct mdss_edp_drv_pdata *ep, int up);
-void mdss_edp_config_ctrl(struct mdss_edp_drv_pdata *ep);
-
-void mdss_edp_clk_debug(unsigned char *edp_base, unsigned char *mmss_cc_base);
-
-#endif /* MDSS_EDP_H */
+static inline const char *__mdss_dp_pm_name(enum dp_pm_type module)
+{
+ switch (module) {
+ case DP_CORE_PM: return "DP_CORE_PM";
+ case DP_CTRL_PM: return "DP_CTRL_PM";
+ case DP_PHY_PM: return "DP_PHY_PM";
+ default: return "???";
+ }
+}
+
+static inline const char *__mdss_dp_pm_supply_node_name(
+ enum dp_pm_type module)
+{
+ switch (module) {
+ case DP_CORE_PM: return "qcom,core-supply-entries";
+ case DP_CTRL_PM: return "qcom,ctrl-supply-entries";
+ case DP_PHY_PM: return "qcom,phy-supply-entries";
+ default: return "???";
+ }
+}
+
+void mdss_dp_phy_initialize(struct mdss_dp_drv_pdata *dp);
+
+void mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *dp);
+int mdss_dp_dpcd_status_read(struct mdss_dp_drv_pdata *dp);
+void mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp, int block);
+int mdss_dp_link_train(struct mdss_dp_drv_pdata *dp);
+void dp_aux_i2c_handler(struct mdss_dp_drv_pdata *dp, u32 isr);
+void dp_aux_native_handler(struct mdss_dp_drv_pdata *dp, u32 isr);
+void mdss_dp_aux_init(struct mdss_dp_drv_pdata *ep);
+
+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);
+
+#endif /* MDSS_DP_H */
diff --git a/drivers/video/fbdev/msm/mdss_edp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c
index d3a060d6f288..39f11a8c35d1 100644
--- a/drivers/video/fbdev/msm/mdss_edp_aux.c
+++ b/drivers/video/fbdev/msm/mdss_dp_aux.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013, 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
@@ -11,6 +11,8 @@
*
*/
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
@@ -28,17 +30,14 @@
#include <linux/of_gpio.h>
#include <linux/clk/msm-clk.h>
-#include <mach/hardware.h>
-#include <mach/gpio.h>
-#include <mach/dma.h>
-
#include "mdss_panel.h"
-#include "mdss_edp.h"
+#include "mdss_dp.h"
+#include "mdss_dp_util.h"
/*
* edp buffer operation
*/
-static char *edp_buf_init(struct edp_buf *eb, char *buf, int size)
+static char *dp_buf_init(struct edp_buf *eb, char *buf, int size)
{
eb->start = buf;
eb->size = size;
@@ -50,7 +49,7 @@ static char *edp_buf_init(struct edp_buf *eb, char *buf, int size)
return eb->data;
}
-static char *edp_buf_reset(struct edp_buf *eb)
+static char *dp_buf_reset(struct edp_buf *eb)
{
eb->data = eb->start;
eb->len = 0;
@@ -59,23 +58,23 @@ static char *edp_buf_reset(struct edp_buf *eb)
return eb->data;
}
-static char *edp_buf_push(struct edp_buf *eb, int len)
+static char *dp_buf_push(struct edp_buf *eb, int len)
{
eb->data += len;
eb->len += len;
return eb->data;
}
-static int edp_buf_trailing(struct edp_buf *eb)
+static int dp_buf_trailing(struct edp_buf *eb)
{
return (int)(eb->end - eb->data);
}
/*
- * edp aux edp_buf_add_cmd:
+ * edp aux dp_buf_add_cmd:
* NO native and i2c command mix allowed
*/
-static int edp_buf_add_cmd(struct edp_buf *eb, struct edp_cmd *cmd)
+static int dp_buf_add_cmd(struct edp_buf *eb, struct edp_cmd *cmd)
{
char data;
char *bp, *cp;
@@ -86,7 +85,7 @@ static int edp_buf_add_cmd(struct edp_buf *eb, struct edp_cmd *cmd)
else
len = cmd->len + 4;
- if (edp_buf_trailing(eb) < len)
+ if (dp_buf_trailing(eb) < len)
return 0;
/*
@@ -111,7 +110,7 @@ static int edp_buf_add_cmd(struct edp_buf *eb, struct edp_cmd *cmd)
for (i = 0; i < cmd->len; i++)
*bp++ = *cp++;
}
- edp_buf_push(eb, len);
+ dp_buf_push(eb, len);
if (cmd->i2c)
eb->i2c++;
@@ -121,7 +120,7 @@ static int edp_buf_add_cmd(struct edp_buf *eb, struct edp_cmd *cmd)
return cmd->len - 1;
}
-static int edp_cmd_fifo_tx(struct edp_buf *tp, unsigned char *base)
+static int dp_cmd_fifo_tx(struct edp_buf *tp, unsigned char *base)
{
u32 data;
char *dp;
@@ -140,8 +139,8 @@ static int edp_cmd_fifo_tx(struct edp_buf *tp, unsigned char *base)
data &= 0x00ff00; /* index = 0, write */
if (cnt == 0)
data |= BIT(31); /* INDEX_WRITE */
- pr_debug("%s: data=%x\n", __func__, data);
- edp_write(base + EDP_AUX_DATA, data);
+ pr_debug("data=%x\n", data);
+ dp_write(base + DP_AUX_DATA, data);
cnt++;
dp++;
}
@@ -151,13 +150,13 @@ static int edp_cmd_fifo_tx(struct edp_buf *tp, unsigned char *base)
data |= BIT(8); /* I2C */
data |= BIT(9); /* GO */
- pr_debug("%s: data=%x\n", __func__, data);
- edp_write(base + EDP_AUX_TRANS_CTRL, data);
+ pr_debug("data=%x\n", data);
+ dp_write(base + DP_AUX_TRANS_CTRL, data);
return tp->len;
}
-static int edp_cmd_fifo_rx(struct edp_buf *rp, int len, unsigned char *base)
+static int dp_cmd_fifo_rx(struct edp_buf *rp, int len, unsigned char *base)
{
u32 data;
char *dp;
@@ -166,15 +165,15 @@ static int edp_cmd_fifo_rx(struct edp_buf *rp, int len, unsigned char *base)
data = 0; /* index = 0 */
data |= BIT(31); /* INDEX_WRITE */
data |= BIT(0); /* read */
- edp_write(base + EDP_AUX_DATA, data);
+ dp_write(base + DP_AUX_DATA, data);
dp = rp->data;
/* discard first byte */
- data = edp_read(base + EDP_AUX_DATA);
+ data = dp_read(base + DP_AUX_DATA);
for (i = 0; i < len; i++) {
- data = edp_read(base + EDP_AUX_DATA);
- pr_debug("%s: data=%x\n", __func__, data);
+ data = dp_read(base + DP_AUX_DATA);
+ pr_debug("data=%x\n", data);
*dp++ = (char)((data >> 8) & 0xff);
}
@@ -182,7 +181,7 @@ static int edp_cmd_fifo_rx(struct edp_buf *rp, int len, unsigned char *base)
return len;
}
-static int edp_aux_write_cmds(struct mdss_edp_drv_pdata *ep,
+static int dp_aux_write_cmds(struct mdss_dp_drv_pdata *ep,
struct edp_cmd *cmd)
{
struct edp_cmd *cm;
@@ -193,14 +192,14 @@ static int edp_aux_write_cmds(struct mdss_edp_drv_pdata *ep,
ep->aux_cmd_busy = 1;
tp = &ep->txp;
- edp_buf_reset(tp);
+ dp_buf_reset(tp);
cm = cmd;
while (cm) {
- pr_debug("%s: i2c=%d read=%d addr=%x len=%d next=%d\n",
- __func__, cm->i2c, cm->read, cm->addr, cm->len,
+ pr_debug("i2c=%d read=%d addr=%x len=%d next=%d\n",
+ cm->i2c, cm->read, cm->addr, cm->len,
cm->next);
- ret = edp_buf_add_cmd(tp, cm);
+ ret = dp_buf_add_cmd(tp, cm);
if (ret <= 0)
break;
if (cm->next == 0)
@@ -215,7 +214,7 @@ static int edp_aux_write_cmds(struct mdss_edp_drv_pdata *ep,
reinit_completion(&ep->aux_comp);
- len = edp_cmd_fifo_tx(&ep->txp, ep->base);
+ len = dp_cmd_fifo_tx(&ep->txp, ep->base);
wait_for_completion(&ep->aux_comp);
@@ -229,7 +228,7 @@ static int edp_aux_write_cmds(struct mdss_edp_drv_pdata *ep,
return ret;
}
-static int edp_aux_read_cmds(struct mdss_edp_drv_pdata *ep,
+static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep,
struct edp_cmd *cmds)
{
struct edp_cmd *cm;
@@ -242,16 +241,16 @@ static int edp_aux_read_cmds(struct mdss_edp_drv_pdata *ep,
tp = &ep->txp;
rp = &ep->rxp;
- edp_buf_reset(tp);
- edp_buf_reset(rp);
+ dp_buf_reset(tp);
+ dp_buf_reset(rp);
cm = cmds;
len = 0;
while (cm) {
- pr_debug("%s: i2c=%d read=%d addr=%x len=%d next=%d\n",
- __func__, cm->i2c, cm->read, cm->addr, cm->len,
+ pr_debug("i2c=%d read=%d addr=%x len=%d next=%d\n",
+ cm->i2c, cm->read, cm->addr, cm->len,
cm->next);
- ret = edp_buf_add_cmd(tp, cm);
+ ret = dp_buf_add_cmd(tp, cm);
len += cm->len;
if (ret <= 0)
break;
@@ -267,12 +266,12 @@ static int edp_aux_read_cmds(struct mdss_edp_drv_pdata *ep,
reinit_completion(&ep->aux_comp);
- edp_cmd_fifo_tx(tp, ep->base);
+ dp_cmd_fifo_tx(tp, ep->base);
wait_for_completion(&ep->aux_comp);
if (ep->aux_error_num == EDP_AUX_ERR_NONE)
- ret = edp_cmd_fifo_rx(rp, len, ep->base);
+ ret = dp_cmd_fifo_rx(rp, len, ep->base);
else
ret = ep->aux_error_num;
@@ -282,10 +281,10 @@ static int edp_aux_read_cmds(struct mdss_edp_drv_pdata *ep,
return ret;
}
-void edp_aux_native_handler(struct mdss_edp_drv_pdata *ep, u32 isr)
+void dp_aux_native_handler(struct mdss_dp_drv_pdata *ep, u32 isr)
{
- pr_debug("%s: isr=%x\n", __func__, isr);
+ pr_debug("isr=%x\n", isr);
if (isr & EDP_INTR_AUX_I2C_DONE)
ep->aux_error_num = EDP_AUX_ERR_NONE;
@@ -299,10 +298,10 @@ void edp_aux_native_handler(struct mdss_edp_drv_pdata *ep, u32 isr)
complete(&ep->aux_comp);
}
-void edp_aux_i2c_handler(struct mdss_edp_drv_pdata *ep, u32 isr)
+void dp_aux_i2c_handler(struct mdss_dp_drv_pdata *ep, u32 isr)
{
- pr_debug("%s: isr=%x\n", __func__, isr);
+ pr_debug("isr=%x\n", isr);
if (isr & EDP_INTR_AUX_I2C_DONE) {
if (isr & (EDP_INTR_I2C_NACK | EDP_INTR_I2C_DEFER))
@@ -325,7 +324,7 @@ void edp_aux_i2c_handler(struct mdss_edp_drv_pdata *ep, u32 isr)
complete(&ep->aux_comp);
}
-static int edp_aux_write_buf(struct mdss_edp_drv_pdata *ep, u32 addr,
+static int dp_aux_write_buf(struct mdss_dp_drv_pdata *ep, u32 addr,
char *buf, int len, int i2c)
{
struct edp_cmd cmd;
@@ -337,10 +336,10 @@ static int edp_aux_write_buf(struct mdss_edp_drv_pdata *ep, u32 addr,
cmd.len = len & 0x0ff;
cmd.next = 0;
- return edp_aux_write_cmds(ep, &cmd);
+ return dp_aux_write_cmds(ep, &cmd);
}
-static int edp_aux_read_buf(struct mdss_edp_drv_pdata *ep, u32 addr,
+static int dp_aux_read_buf(struct mdss_dp_drv_pdata *ep, u32 addr,
int len, int i2c)
{
struct edp_cmd cmd;
@@ -352,7 +351,7 @@ static int edp_aux_read_buf(struct mdss_edp_drv_pdata *ep, u32 addr,
cmd.len = len & 0x0ff;
cmd.next = 0;
- return edp_aux_read_cmds(ep, &cmd);
+ return dp_aux_read_cmds(ep, &cmd);
}
/*
@@ -360,7 +359,7 @@ static int edp_aux_read_buf(struct mdss_edp_drv_pdata *ep, u32 addr,
*/
static char edid_hdr[8] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00};
-int edp_edid_buf_error(char *buf, int len)
+int dp_edid_buf_error(char *buf, int len)
{
char *bp;
int i;
@@ -368,7 +367,7 @@ int edp_edid_buf_error(char *buf, int len)
bp = buf;
if (len < 128) {
- pr_err("%s: Error: len=%x\n", __func__, len);
+ pr_err("Error: len=%x\n", len);
return -EINVAL;
}
@@ -376,12 +375,12 @@ int edp_edid_buf_error(char *buf, int len)
csum += *bp++;
if (csum != 0) {
- pr_err("%s: Error: csum=%x\n", __func__, csum);
+ pr_err("Error: csum=%x\n", csum);
return -EINVAL;
}
if (strncmp(buf, edid_hdr, strlen(edid_hdr))) {
- pr_err("%s: Error: header\n", __func__);
+ pr_err("Error: header\n");
return -EINVAL;
}
@@ -389,7 +388,7 @@ int edp_edid_buf_error(char *buf, int len)
}
-void edp_extract_edid_manufacturer(struct edp_edid *edid, char *buf)
+void dp_extract_edid_manufacturer(struct edp_edid *edid, char *buf)
{
char *bp;
char data;
@@ -407,10 +406,10 @@ void edp_extract_edid_manufacturer(struct edp_edid *edid, char *buf)
edid->id_name[2] = 'A' + data - 1;
edid->id_name[3] = 0;
- pr_debug("%s: edid manufacturer = %s\n", __func__, edid->id_name);
+ pr_debug("edid manufacturer = %s\n", edid->id_name);
}
-void edp_extract_edid_product(struct edp_edid *edid, char *buf)
+void dp_extract_edid_product(struct edp_edid *edid, char *buf)
{
char *bp;
u32 data;
@@ -423,25 +422,25 @@ void edp_extract_edid_product(struct edp_edid *edid, char *buf)
data <<= 8;
edid->id_product |= data;
- pr_debug("%s: edid product = 0x%x\n", __func__, edid->id_product);
+ pr_debug("edid product = 0x%x\n", edid->id_product);
};
-void edp_extract_edid_version(struct edp_edid *edid, char *buf)
+void dp_extract_edid_version(struct edp_edid *edid, char *buf)
{
edid->version = buf[0x12];
edid->revision = buf[0x13];
- pr_debug("%s: edid version = %d.%d\n", __func__, edid->version,
+ pr_debug("edid version = %d.%d\n", edid->version,
edid->revision);
};
-void edp_extract_edid_ext_block_cnt(struct edp_edid *edid, char *buf)
+void dp_extract_edid_ext_block_cnt(struct edp_edid *edid, char *buf)
{
edid->ext_block_cnt = buf[0x7e];
- pr_debug("%s: edid extension = %d\n", __func__,
+ pr_debug("edid extension = %d\n",
edid->ext_block_cnt);
};
-void edp_extract_edid_video_support(struct edp_edid *edid, char *buf)
+void dp_extract_edid_video_support(struct edp_edid *edid, char *buf)
{
char *bp;
@@ -454,14 +453,14 @@ void edp_extract_edid_video_support(struct edp_edid *edid, char *buf)
edid->color_depth *= 2;
edid->color_depth += 4;
}
- pr_debug("%s: Digital Video intf=%d color_depth=%d\n",
- __func__, edid->video_intf, edid->color_depth);
+ pr_debug("Digital Video intf=%d color_depth=%d\n",
+ edid->video_intf, edid->color_depth);
} else {
- pr_err("%s: Error, Analog video interface\n", __func__);
+ pr_err("Error, Analog video interface\n");
}
};
-void edp_extract_edid_feature(struct edp_edid *edid, char *buf)
+void dp_extract_edid_feature(struct edp_edid *edid, char *buf)
{
char *bp;
char data;
@@ -481,11 +480,11 @@ void edp_extract_edid_feature(struct edp_edid *edid, char *buf)
}
}
- pr_debug("%s: edid dpm=%d color_format=%d\n", __func__,
+ pr_debug("edid dpm=%d color_format=%d\n",
edid->dpm, edid->color_format);
};
-void edp_extract_edid_detailed_timing_description(struct edp_edid *edid,
+void dp_extract_edid_detailed_timing_description(struct edp_edid *edid,
char *buf)
{
char *bp;
@@ -589,22 +588,22 @@ void edp_extract_edid_detailed_timing_description(struct edp_edid *edid,
}
}
- pr_debug("%s: pixel_clock = %d\n", __func__, dp->pclk);
+ pr_debug("pixel_clock = %d\n", dp->pclk);
- pr_debug("%s: horizontal=%d, blank=%d, porch=%d, sync=%d\n"
- , __func__, dp->h_addressable, dp->h_blank,
+ pr_debug("horizontal=%d, blank=%d, porch=%d, sync=%d\n",
+ dp->h_addressable, dp->h_blank,
dp->h_fporch, dp->h_sync_pulse);
- pr_debug("%s: vertical=%d, blank=%d, porch=%d, vsync=%d\n"
- , __func__, dp->v_addressable, dp->v_blank,
+ pr_debug("vertical=%d, blank=%d, porch=%d, vsync=%d\n",
+ dp->v_addressable, dp->v_blank,
dp->v_fporch, dp->v_sync_pulse);
- pr_debug("%s: panel size in mm, width=%d height=%d\n", __func__,
+ pr_debug("panel size in mm, width=%d height=%d\n",
dp->width_mm, dp->height_mm);
- pr_debug("%s: panel border horizontal=%d vertical=%d\n", __func__,
+ pr_debug("panel border horizontal=%d vertical=%d\n",
dp->h_border, dp->v_border);
- pr_debug("%s: flags: interlaced=%d stereo=%d sync_type=%d sync_sep=%d\n"
- , __func__, dp->interlaced, dp->stereo,
+ pr_debug("flags: interlaced=%d stereo=%d sync_type=%d sync_sep=%d\n",
+ dp->interlaced, dp->stereo,
dp->sync_type, dp->sync_separate);
- pr_debug("%s: polarity vsync=%d, hsync=%d", __func__,
+ pr_debug("polarity vsync=%d, hsync=%d",
dp->vsync_pol, dp->hsync_pol);
}
@@ -629,67 +628,67 @@ void edp_extract_edid_detailed_timing_description(struct edp_edid *edid,
* 0, 75 };
*/
-static int edp_aux_chan_ready(struct mdss_edp_drv_pdata *ep)
+static int dp_aux_chan_ready(struct mdss_dp_drv_pdata *ep)
{
int cnt, ret;
char data = 0;
for (cnt = 5; cnt; cnt--) {
- ret = edp_aux_write_buf(ep, 0x50, &data, 1, 1);
- pr_debug("%s: ret=%d\n", __func__, ret);
+ ret = dp_aux_write_buf(ep, 0x50, &data, 1, 1);
+ pr_debug("ret=%d\n", ret);
if (ret >= 0)
break;
msleep(100);
}
if (cnt <= 0) {
- pr_err("%s: aux chan NOT ready\n", __func__);
- return 0;
+ pr_err("aux chan NOT ready\n");
+ return -EIO;
}
- return 1;
+ return 0;
}
-static int edp_sink_edid_read(struct mdss_edp_drv_pdata *ep, int block)
+static int dp_sink_edid_read(struct mdss_dp_drv_pdata *ep, int block)
{
struct edp_buf *rp;
int cnt, rlen;
int ret = 0;
- ret = edp_aux_chan_ready(ep);
- if (ret == 0) {
- pr_err("%s: aux chan NOT ready\n", __func__);
+ ret = dp_aux_chan_ready(ep);
+ if (ret) {
+ pr_err("aux chan NOT ready\n");
return ret;
}
for (cnt = 5; cnt; cnt--) {
- rlen = edp_aux_read_buf(ep, 0x50, 128, 1);
+ rlen = dp_aux_read_buf(ep, 0x50, 128, 1);
if (rlen > 0) {
- pr_debug("%s: rlen=%d\n", __func__, rlen);
+ pr_debug("rlen=%d\n", rlen);
rp = &ep->rxp;
- if (!edp_edid_buf_error(rp->data, rp->len))
+ if (!dp_edid_buf_error(rp->data, rp->len))
break;
}
}
if (cnt <= 0) {
- pr_err("%s: Failed\n", __func__);
+ pr_err("Failed\n");
return -EINVAL;
}
- edp_extract_edid_manufacturer(&ep->edid, rp->data);
- edp_extract_edid_product(&ep->edid, rp->data);
- edp_extract_edid_version(&ep->edid, rp->data);
- edp_extract_edid_ext_block_cnt(&ep->edid, rp->data);
- edp_extract_edid_video_support(&ep->edid, rp->data);
- edp_extract_edid_feature(&ep->edid, rp->data);
- edp_extract_edid_detailed_timing_description(&ep->edid, rp->data);
+ dp_extract_edid_manufacturer(&ep->edid, rp->data);
+ dp_extract_edid_product(&ep->edid, rp->data);
+ dp_extract_edid_version(&ep->edid, rp->data);
+ dp_extract_edid_ext_block_cnt(&ep->edid, rp->data);
+ dp_extract_edid_video_support(&ep->edid, rp->data);
+ dp_extract_edid_feature(&ep->edid, rp->data);
+ dp_extract_edid_detailed_timing_description(&ep->edid, rp->data);
return 128;
}
-static void edp_sink_capability_read(struct mdss_edp_drv_pdata *ep,
+static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
int len)
{
char *bp;
@@ -698,9 +697,9 @@ static void edp_sink_capability_read(struct mdss_edp_drv_pdata *ep,
struct edp_buf *rp;
int rlen;
- rlen = edp_aux_read_buf(ep, 0, len, 0);
+ rlen = dp_aux_read_buf(ep, 0, len, 0);
if (rlen <= 0) {
- pr_err("%s: edp aux read failed\n", __func__);
+ pr_err("edp aux read failed\n");
return;
}
rp = &ep->rxp;
@@ -712,14 +711,14 @@ static void edp_sink_capability_read(struct mdss_edp_drv_pdata *ep,
cap->minor = data & 0x0f;
if (--rlen <= 0)
return;
- pr_debug("%s: version: %d.%d\n", __func__, cap->major, cap->minor);
+ pr_debug("version: %d.%d\n", cap->major, cap->minor);
data = *bp++; /* byte 1 */
/* 162, 270 and 540 MB, symbol rate, NOT bit rate */
cap->max_link_rate = data;
if (--rlen <= 0)
return;
- pr_debug("%s: link_rate=%d\n", __func__, cap->max_link_rate);
+ pr_debug("link_rate=%d\n", cap->max_link_rate);
data = *bp++; /* byte 2 */
if (data & BIT(7))
@@ -731,24 +730,24 @@ static void edp_sink_capability_read(struct mdss_edp_drv_pdata *ep,
cap->max_lane_count = data;
if (--rlen <= 0)
return;
- pr_debug("%s: lane_count=%d\n", __func__, cap->max_lane_count);
+ pr_debug("lane_count=%d\n", cap->max_lane_count);
data = *bp++; /* byte 3 */
if (data & BIT(0)) {
cap->flags |= DPCD_MAX_DOWNSPREAD_0_5;
- pr_debug("%s: max_downspread\n", __func__);
+ pr_debug("max_downspread\n");
}
if (data & BIT(6)) {
cap->flags |= DPCD_NO_AUX_HANDSHAKE;
- pr_debug("%s: NO Link Training\n", __func__);
+ pr_debug("NO Link Training\n");
}
if (--rlen <= 0)
return;
data = *bp++; /* byte 4 */
cap->num_rx_port = (data & BIT(0)) + 1;
- pr_debug("%s: rx_ports=%d", __func__, cap->num_rx_port);
+ pr_debug("rx_ports=%d", cap->num_rx_port);
if (--rlen <= 0)
return;
@@ -760,14 +759,14 @@ static void edp_sink_capability_read(struct mdss_edp_drv_pdata *ep,
data = *bp++; /* byte 8 */
if (data & BIT(1)) {
cap->flags |= DPCD_PORT_0_EDID_PRESENTED;
- pr_debug("%s: edid presented\n", __func__);
+ pr_debug("edid presented\n");
}
if (--rlen <= 0)
return;
data = *bp++; /* byte 9 */
cap->rx_port0_buf_size = (data + 1) * 32;
- pr_debug("%s: lane_buf_size=%d", __func__, cap->rx_port0_buf_size);
+ pr_debug("lane_buf_size=%d", cap->rx_port0_buf_size);
if (--rlen <= 0)
return;
@@ -779,19 +778,19 @@ static void edp_sink_capability_read(struct mdss_edp_drv_pdata *ep,
data = *bp++; /* byte 12 */
cap->i2c_speed_ctrl = data;
if (cap->i2c_speed_ctrl > 0)
- pr_debug("%s: i2c_rate=%d", __func__, cap->i2c_speed_ctrl);
+ pr_debug("i2c_rate=%d", cap->i2c_speed_ctrl);
if (--rlen <= 0)
return;
data = *bp++; /* byte 13 */
cap->scrambler_reset = data & BIT(0);
- pr_debug("%s: scrambler_reset=%d\n", __func__,
+ pr_debug("scrambler_reset=%d\n",
cap->scrambler_reset);
if (data & BIT(1))
cap->enhanced_frame++;
- pr_debug("%s: enhanced_framing=%d\n", __func__,
+ pr_debug("enhanced_framing=%d\n",
cap->enhanced_frame);
if (--rlen <= 0)
return;
@@ -801,11 +800,11 @@ static void edp_sink_capability_read(struct mdss_edp_drv_pdata *ep,
cap->training_read_interval = 4000; /* us */
else
cap->training_read_interval = 4000 * data; /* us */
- pr_debug("%s: training_interval=%d\n", __func__,
+ pr_debug("training_interval=%d\n",
cap->training_read_interval);
}
-static int edp_link_status_read(struct mdss_edp_drv_pdata *ep, int len)
+static int dp_link_status_read(struct mdss_dp_drv_pdata *ep, int len)
{
char *bp;
char data;
@@ -813,11 +812,11 @@ static int edp_link_status_read(struct mdss_edp_drv_pdata *ep, int len)
struct edp_buf *rp;
int rlen;
- pr_debug("%s: len=%d", __func__, len);
+ pr_debug("len=%d", len);
/* skip byte 0x200 and 0x201 */
- rlen = edp_aux_read_buf(ep, 0x202, len, 0);
+ rlen = dp_aux_read_buf(ep, 0x202, len, 0);
if (rlen < len) {
- pr_err("%s: edp aux read failed\n", __func__);
+ pr_err("edp aux read failed\n");
return 0;
}
rp = &ep->rxp;
@@ -860,7 +859,7 @@ static int edp_link_status_read(struct mdss_edp_drv_pdata *ep, int len)
return len;
}
-static int edp_cap_lane_rate_set(struct mdss_edp_drv_pdata *ep)
+static int dp_cap_lane_rate_set(struct mdss_dp_drv_pdata *ep)
{
char buf[4];
int len = 0;
@@ -868,17 +867,17 @@ static int edp_cap_lane_rate_set(struct mdss_edp_drv_pdata *ep)
cap = &ep->dpcd;
- pr_debug("%s: bw=%x lane=%d\n", __func__, ep->link_rate, ep->lane_cnt);
+ pr_debug("bw=%x lane=%d\n", ep->link_rate, ep->lane_cnt);
buf[0] = ep->link_rate;
buf[1] = ep->lane_cnt;
if (cap->enhanced_frame)
buf[1] |= 0x80;
- len = edp_aux_write_buf(ep, 0x100, buf, 2, 0);
+ len = dp_aux_write_buf(ep, 0x100, buf, 2, 0);
return len;
}
-static int edp_lane_set_write(struct mdss_edp_drv_pdata *ep, int voltage_level,
+static int dp_lane_set_write(struct mdss_dp_drv_pdata *ep, int voltage_level,
int pre_emphasis_level)
{
int i;
@@ -895,21 +894,21 @@ static int edp_lane_set_write(struct mdss_edp_drv_pdata *ep, int voltage_level,
for (i = 0; i < 4; i++)
buf[i] = voltage_level | pre_emphasis_level;
- pr_debug("%s: p|v=0x%x", __func__, voltage_level | pre_emphasis_level);
- return edp_aux_write_buf(ep, 0x103, buf, 4, 0);
+ pr_debug("p|v=0x%x", voltage_level | pre_emphasis_level);
+ return dp_aux_write_buf(ep, 0x103, buf, 4, 0);
}
-static int edp_train_pattern_set_write(struct mdss_edp_drv_pdata *ep,
+static int dp_train_pattern_set_write(struct mdss_dp_drv_pdata *ep,
int pattern)
{
char buf[4];
- pr_debug("%s: pattern=%x\n", __func__, pattern);
+ pr_debug("pattern=%x\n", pattern);
buf[0] = pattern;
- return edp_aux_write_buf(ep, 0x102, buf, 1, 0);
+ return dp_aux_write_buf(ep, 0x102, buf, 1, 0);
}
-static int edp_sink_clock_recovery_done(struct mdss_edp_drv_pdata *ep)
+static int dp_sink_clock_recovery_done(struct mdss_dp_drv_pdata *ep)
{
u32 mask;
u32 data;
@@ -927,7 +926,7 @@ static int edp_sink_clock_recovery_done(struct mdss_edp_drv_pdata *ep)
data |= ep->link_status.lane_01_status;
}
- pr_debug("%s: data=%x mask=%x\n", __func__, data, mask);
+ pr_debug("data=%x mask=%x\n", data, mask);
data &= mask;
if (data == mask) /* all done */
return 1;
@@ -935,15 +934,15 @@ static int edp_sink_clock_recovery_done(struct mdss_edp_drv_pdata *ep)
return 0;
}
-static int edp_sink_channel_eq_done(struct mdss_edp_drv_pdata *ep)
+static int dp_sink_channel_eq_done(struct mdss_dp_drv_pdata *ep)
{
u32 mask;
u32 data;
- pr_debug("%s:\n", __func__);
+ pr_debug("Entered++\n");
if (!ep->link_status.interlane_align_done) { /* not align */
- pr_err("%s: interlane align failed\n", __func__);
+ pr_err("interlane align failed\n");
return 0;
}
@@ -960,7 +959,7 @@ static int edp_sink_channel_eq_done(struct mdss_edp_drv_pdata *ep)
data |= ep->link_status.lane_01_status;
}
- pr_debug("%s: data=%x mask=%x\n", __func__, data, mask);
+ pr_debug("data=%x mask=%x\n", data, mask);
data &= mask;
if (data == mask)/* all done */
@@ -969,7 +968,7 @@ static int edp_sink_channel_eq_done(struct mdss_edp_drv_pdata *ep)
return 0;
}
-void edp_sink_train_set_adjust(struct mdss_edp_drv_pdata *ep)
+void dp_sink_train_set_adjust(struct mdss_dp_drv_pdata *ep)
{
int i;
int max = 0;
@@ -977,8 +976,8 @@ void edp_sink_train_set_adjust(struct mdss_edp_drv_pdata *ep)
/* use the max level across lanes */
for (i = 0; i < ep->lane_cnt; i++) {
- pr_debug("%s: lane=%d req_voltage_swing=%d",
- __func__, i, ep->link_status.req_voltage_swing[i]);
+ pr_debug("lane=%d req_voltage_swing=%d",
+ i, ep->link_status.req_voltage_swing[i]);
if (max < ep->link_status.req_voltage_swing[i])
max = ep->link_status.req_voltage_swing[i];
}
@@ -988,18 +987,18 @@ void edp_sink_train_set_adjust(struct mdss_edp_drv_pdata *ep)
/* use the max level across lanes */
max = 0;
for (i = 0; i < ep->lane_cnt; i++) {
- pr_debug(" %s: lane=%d req_pre_emphasis=%d",
- __func__, i, ep->link_status.req_pre_emphasis[i]);
+ pr_debug("lane=%d req_pre_emphasis=%d",
+ i, ep->link_status.req_pre_emphasis[i]);
if (max < ep->link_status.req_pre_emphasis[i])
max = ep->link_status.req_pre_emphasis[i];
}
ep->p_level = max;
- pr_debug("%s: v_level=%d, p_level=%d", __func__,
+ pr_debug("v_level=%d, p_level=%d",
ep->v_level, ep->p_level);
}
-static void edp_host_train_set(struct mdss_edp_drv_pdata *ep, int train)
+static void dp_host_train_set(struct mdss_dp_drv_pdata *ep, int train)
{
int bit, cnt;
u32 data;
@@ -1007,20 +1006,20 @@ static void edp_host_train_set(struct mdss_edp_drv_pdata *ep, int train)
bit = 1;
bit <<= (train - 1);
- pr_debug("%s: bit=%d train=%d\n", __func__, bit, train);
- edp_write(ep->base + EDP_STATE_CTRL, bit);
+ pr_debug("bit=%d train=%d\n", bit, train);
+ dp_write(ep->base + DP_STATE_CTRL, bit);
bit = 8;
bit <<= (train - 1);
cnt = 10;
while (cnt--) {
- data = edp_read(ep->base + EDP_MAINLINK_READY);
+ data = dp_read(ep->base + DP_MAINLINK_READY);
if (data & bit)
break;
}
if (cnt == 0)
- pr_err("%s: set link_train=%d failed\n", __func__, train);
+ pr_err("set link_train=%d failed\n", train);
}
char vm_pre_emphasis[4][4] = {
@@ -1038,38 +1037,38 @@ char vm_voltage_swing[4][4] = {
{0x1E, 0xFF, 0xFF, 0xFF} /* sw1, 1.2 v, optional */
};
-static void edp_voltage_pre_emphasise_set(struct mdss_edp_drv_pdata *ep)
+static void dp_voltage_pre_emphasise_set(struct mdss_dp_drv_pdata *ep)
{
u32 value0 = 0;
u32 value1 = 0;
- pr_debug("%s: v=%d p=%d\n", __func__, ep->v_level, ep->p_level);
+ pr_debug("v=%d p=%d\n", ep->v_level, ep->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)];
/* Configure host and panel only if both values are allowed */
if (value0 != 0xFF && value1 != 0xFF) {
- edp_write(ep->base + EDP_PHY_EDPPHY_GLB_VM_CFG0, value0);
- edp_write(ep->base + EDP_PHY_EDPPHY_GLB_VM_CFG1, value1);
- pr_debug("%s: value0=0x%x value1=0x%x", __func__,
+ dp_write(ep->base + EDP_PHY_EDPPHY_GLB_VM_CFG0, value0);
+ dp_write(ep->base + EDP_PHY_EDPPHY_GLB_VM_CFG1, value1);
+ pr_debug("value0=0x%x value1=0x%x",
value0, value1);
- edp_lane_set_write(ep, ep->v_level, ep->p_level);
+ dp_lane_set_write(ep, ep->v_level, ep->p_level);
}
}
-static int edp_start_link_train_1(struct mdss_edp_drv_pdata *ep)
+static int dp_start_link_train_1(struct mdss_dp_drv_pdata *ep)
{
int tries, old_v_level;
int ret = 0;
int usleep_time;
- pr_debug("%s:", __func__);
+ pr_debug("Entered++");
- edp_host_train_set(ep, 0x01); /* train_1 */
- edp_voltage_pre_emphasise_set(ep);
- edp_train_pattern_set_write(ep, 0x21); /* train_1 */
+ dp_host_train_set(ep, 0x01); /* train_1 */
+ dp_voltage_pre_emphasise_set(ep);
+ dp_train_pattern_set_write(ep, 0x21); /* train_1 */
tries = 0;
old_v_level = ep->v_level;
@@ -1077,8 +1076,8 @@ static int edp_start_link_train_1(struct mdss_edp_drv_pdata *ep)
usleep_time = ep->dpcd.training_read_interval;
usleep_range(usleep_time, usleep_time);
- edp_link_status_read(ep, 6);
- if (edp_sink_clock_recovery_done(ep)) {
+ dp_link_status_read(ep, 6);
+ if (dp_sink_clock_recovery_done(ep)) {
ret = 0;
break;
}
@@ -1099,39 +1098,39 @@ static int edp_start_link_train_1(struct mdss_edp_drv_pdata *ep)
old_v_level = ep->v_level;
}
- edp_sink_train_set_adjust(ep);
- edp_voltage_pre_emphasise_set(ep);
+ dp_sink_train_set_adjust(ep);
+ dp_voltage_pre_emphasise_set(ep);
}
return ret;
}
-static int edp_start_link_train_2(struct mdss_edp_drv_pdata *ep)
+static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep)
{
int tries;
int ret = 0;
int usleep_time;
char pattern;
- pr_debug("%s:", __func__);
+ pr_debug("Entered++");
if (ep->dpcd.flags & DPCD_TPS3)
pattern = 0x03;
else
pattern = 0x02;
- edp_host_train_set(ep, pattern); /* train_2 */
- edp_voltage_pre_emphasise_set(ep);
- edp_train_pattern_set_write(ep, pattern | 0x20);/* train_2 */
+ dp_host_train_set(ep, pattern); /* train_2 */
+ dp_voltage_pre_emphasise_set(ep);
+ dp_train_pattern_set_write(ep, pattern | 0x20);/* train_2 */
tries = 0;
while (1) {
usleep_time = ep->dpcd.training_read_interval;
usleep_range(usleep_time, usleep_time);
- edp_link_status_read(ep, 6);
+ dp_link_status_read(ep, 6);
- if (edp_sink_channel_eq_done(ep)) {
+ if (dp_sink_channel_eq_done(ep)) {
ret = 0;
break;
}
@@ -1142,14 +1141,14 @@ static int edp_start_link_train_2(struct mdss_edp_drv_pdata *ep)
break;
}
- edp_sink_train_set_adjust(ep);
- edp_voltage_pre_emphasise_set(ep);
+ dp_sink_train_set_adjust(ep);
+ dp_voltage_pre_emphasise_set(ep);
}
return ret;
}
-static int edp_link_rate_down_shift(struct mdss_edp_drv_pdata *ep)
+static int dp_link_rate_down_shift(struct mdss_dp_drv_pdata *ep)
{
u32 prate, lrate;
int rate, lane, max_lane;
@@ -1164,7 +1163,7 @@ static int edp_link_rate_down_shift(struct mdss_edp_drv_pdata *ep)
prate *= ep->bpp;
prate /= 8; /* byte */
- if (rate > EDP_LINK_RATE_162 && rate <= EDP_LINK_RATE_MAX) {
+ if (rate > DP_LINK_RATE_162 && rate <= DP_LINK_RATE_MAX) {
rate -= 4; /* reduce rate */
changed++;
}
@@ -1179,13 +1178,13 @@ static int edp_link_rate_down_shift(struct mdss_edp_drv_pdata *ep)
lrate /= 10; /* byte, 10 bits --> 8 bits */
lrate *= lane;
- pr_debug("%s: new lrate=%u prate=%u rate=%d lane=%d p=%d b=%d\n",
- __func__, lrate, prate, rate, lane, ep->pixel_rate, ep->bpp);
+ pr_debug("new lrate=%u prate=%u rate=%d lane=%d p=%d b=%d\n",
+ lrate, prate, rate, lane, ep->pixel_rate, ep->bpp);
if (lrate > prate) {
ep->link_rate = rate;
ep->lane_cnt = lane;
- pr_debug("%s: new rate=%d %d\n", __func__, rate, lane);
+ pr_debug("new rate=%d %d\n", rate, lane);
return 0;
}
}
@@ -1194,90 +1193,95 @@ static int edp_link_rate_down_shift(struct mdss_edp_drv_pdata *ep)
return -EINVAL;
}
-static void edp_clear_training_pattern(struct mdss_edp_drv_pdata *ep)
+static int mdss_dp_sink_power_state(struct mdss_dp_drv_pdata *ep, char state)
+{
+ int ret;
+
+ ret = dp_aux_write_buf(ep, 0x600, &state, 1, 0);
+ pr_debug("state=%d ret=%d\n", state, ret);
+ return ret;
+}
+
+static void dp_clear_training_pattern(struct mdss_dp_drv_pdata *ep)
{
int usleep_time;
- pr_debug("%s:\n", __func__);
- edp_train_pattern_set_write(ep, 0);
+
+ pr_debug("Entered++\n");
+ dp_train_pattern_set_write(ep, 0);
usleep_time = ep->dpcd.training_read_interval;
usleep_range(usleep_time, usleep_time);
}
-static int edp_aux_link_train(struct mdss_edp_drv_pdata *ep)
+static int dp_aux_link_train(struct mdss_dp_drv_pdata *ep)
{
int ret = 0;
int usleep_time;
- ret = edp_aux_chan_ready(ep);
- if (ret == 0) {
- pr_err("%s: LINK Train failed: aux chan NOT ready\n", __func__);
+ ret = dp_aux_chan_ready(ep);
+ if (ret) {
+ pr_err("LINK Train failed: aux chan NOT ready\n");
complete(&ep->train_comp);
return ret;
}
- edp_write(ep->base + EDP_MAINLINK_CTRL, 0x1);
+ dp_write(ep->base + DP_MAINLINK_CTRL, 0x1);
- mdss_edp_sink_power_state(ep, SINK_POWER_ON);
+ mdss_dp_sink_power_state(ep, SINK_POWER_ON);
train_start:
ep->v_level = 0; /* start from default level */
ep->p_level = 0;
- edp_cap_lane_rate_set(ep);
- mdss_edp_config_ctrl(ep);
- mdss_edp_lane_power_ctrl(ep, 1);
+ dp_cap_lane_rate_set(ep);
- mdss_edp_state_ctrl(ep, 0);
- edp_clear_training_pattern(ep);
+ dp_clear_training_pattern(ep);
usleep_time = ep->dpcd.training_read_interval;
usleep_range(usleep_time, usleep_time);
- ret = edp_start_link_train_1(ep);
+ ret = dp_start_link_train_1(ep);
if (ret < 0) {
- if (edp_link_rate_down_shift(ep) == 0) {
+ if (dp_link_rate_down_shift(ep) == 0) {
goto train_start;
} else {
- pr_err("%s: Training 1 failed\n", __func__);
+ pr_err("Training 1 failed\n");
ret = -1;
goto clear;
}
}
- pr_debug("%s: Training 1 completed successfully\n", __func__);
+ pr_debug("Training 1 completed successfully\n");
- mdss_edp_state_ctrl(ep, 0);
- edp_clear_training_pattern(ep);
- ret = edp_start_link_train_2(ep);
+ dp_clear_training_pattern(ep);
+ ret = dp_start_link_train_2(ep);
if (ret < 0) {
- if (edp_link_rate_down_shift(ep) == 0) {
+ if (dp_link_rate_down_shift(ep) == 0) {
goto train_start;
} else {
- pr_err("%s: Training 2 failed\n", __func__);
+ pr_err("Training 2 failed\n");
ret = -1;
goto clear;
}
}
- pr_debug("%s: Training 2 completed successfully\n", __func__);
+ pr_debug("Training 2 completed successfully\n");
- mdss_edp_state_ctrl(ep, ST_SEND_VIDEO);
clear:
- edp_clear_training_pattern(ep);
+ dp_clear_training_pattern(ep);
complete(&ep->train_comp);
return ret;
}
-void mdss_edp_dpcd_cap_read(struct mdss_edp_drv_pdata *ep)
+void mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep)
{
- edp_sink_capability_read(ep, 16);
+ dp_sink_capability_read(ep, 16);
}
-int mdss_edp_dpcd_status_read(struct mdss_edp_drv_pdata *ep)
+int mdss_dp_dpcd_status_read(struct mdss_dp_drv_pdata *ep)
{
struct dpcd_link_status *sp;
int ret = 0; /* not sync */
- ret = edp_link_status_read(ep, 6);
+ ret = dp_link_status_read(ep, 6);
if (ret) {
sp = &ep->link_status;
@@ -1287,45 +1291,34 @@ int mdss_edp_dpcd_status_read(struct mdss_edp_drv_pdata *ep)
return ret;
}
-void mdss_edp_fill_link_cfg(struct mdss_edp_drv_pdata *ep)
+void mdss_dp_fill_link_cfg(struct mdss_dp_drv_pdata *ep)
{
struct display_timing_desc *dp;
dp = &ep->edid.timing[0];
- ep->pixel_rate = dp->pclk;
ep->lane_cnt = ep->dpcd.max_lane_count;
- ep->link_rate = ep->dpcd.max_link_rate;
- pr_debug("%s: pclk=%d rate=%d lane=%d\n", __func__,
+ pr_debug("pclk=%d rate=%d lane=%d\n",
ep->pixel_rate, ep->link_rate, ep->lane_cnt);
}
-void mdss_edp_edid_read(struct mdss_edp_drv_pdata *ep, int block)
+void mdss_dp_edid_read(struct mdss_dp_drv_pdata *ep, int block)
{
- edp_sink_edid_read(ep, block);
-}
-
-int mdss_edp_sink_power_state(struct mdss_edp_drv_pdata *ep, char state)
-{
- int ret;
-
- ret = edp_aux_write_buf(ep, 0x600, &state, 1, 0);
- pr_debug("%s: state=%d ret=%d\n", __func__, state, ret);
- return ret;
+ dp_sink_edid_read(ep, block);
}
-int mdss_edp_link_train(struct mdss_edp_drv_pdata *ep)
+int mdss_dp_link_train(struct mdss_dp_drv_pdata *ep)
{
int ret;
mutex_lock(&ep->train_mutex);
- ret = edp_aux_link_train(ep);
+ ret = dp_aux_link_train(ep);
mutex_unlock(&ep->train_mutex);
return ret;
}
-void mdss_edp_aux_init(struct mdss_edp_drv_pdata *ep)
+void mdss_dp_aux_init(struct mdss_dp_drv_pdata *ep)
{
mutex_init(&ep->aux_mutex);
mutex_init(&ep->train_mutex);
@@ -1336,6 +1329,6 @@ void mdss_edp_aux_init(struct mdss_edp_drv_pdata *ep)
complete(&ep->train_comp); /* make non block at first time */
complete(&ep->video_comp); /* make non block at first time */
- edp_buf_init(&ep->txp, ep->txbuf, sizeof(ep->txbuf));
- edp_buf_init(&ep->rxp, ep->rxbuf, sizeof(ep->rxbuf));
+ dp_buf_init(&ep->txp, ep->txbuf, sizeof(ep->txbuf));
+ dp_buf_init(&ep->rxp, ep->rxbuf, sizeof(ep->rxbuf));
}
diff --git a/drivers/video/fbdev/msm/mdss_dp_util.c b/drivers/video/fbdev/msm/mdss_dp_util.c
new file mode 100644
index 000000000000..c1d29987a5fa
--- /dev/null
+++ b/drivers/video/fbdev/msm/mdss_dp_util.c
@@ -0,0 +1,284 @@
+/* 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.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/io.h>
+#include <linux/delay.h>
+
+#include "mdss_dp_util.h"
+
+struct mdss_hw mdss_dp_hw = {
+ .hw_ndx = MDSS_HW_EDP,
+ .ptr = NULL,
+ .irq_handler = dp_isr,
+};
+
+/* DP retrieve ctrl HW version */
+u32 mdss_dp_get_ctrl_hw_version(struct dss_io_data *ctrl_io)
+{
+ return readl_relaxed(ctrl_io->base + DP_HW_VERSION);
+}
+
+/* DP retrieve phy HW version */
+u32 mdss_dp_get_phy_hw_version(struct dss_io_data *phy_io)
+{
+ return readl_relaxed(phy_io->base + DP_PHY_REVISION_ID3);
+}
+/* DP PHY SW reset */
+void mdss_dp_phy_reset(struct dss_io_data *ctrl_io)
+{
+ writel_relaxed(0x04, ctrl_io->base + DP_PHY_CTRL); /* bit 2 */
+ udelay(1000);
+ writel_relaxed(0x00, ctrl_io->base + DP_PHY_CTRL);
+}
+
+void mdss_dp_switch_usb3_phy_to_dp_mode(struct dss_io_data *tcsr_reg_io)
+{
+ writel_relaxed(0x01, tcsr_reg_io->base + TCSR_USB3_DP_PHYMODE);
+}
+
+/* DP PHY assert reset for PHY and PLL */
+void mdss_dp_assert_phy_reset(struct dss_io_data *ctrl_io, bool assert)
+{
+ if (assert) {
+ /* assert reset line for PHY and PLL */
+ writel_relaxed(0x5,
+ ctrl_io->base + DP_PHY_CTRL); /* bit 0 & 2 */
+ } else {
+ /* remove assert for PLL and PHY reset line */
+ writel_relaxed(0x00, ctrl_io->base + DP_PHY_CTRL);
+ }
+}
+
+/* reset AUX */
+void mdss_dp_aux_reset(struct dss_io_data *ctrl_io)
+{
+ u32 aux_ctrl = readl_relaxed(ctrl_io->base + DP_AUX_CTRL);
+
+ aux_ctrl |= BIT(1);
+ writel_relaxed(aux_ctrl, ctrl_io->base + DP_AUX_CTRL);
+ udelay(1000);
+ aux_ctrl &= ~BIT(1);
+ writel_relaxed(aux_ctrl, ctrl_io->base + DP_AUX_CTRL);
+}
+
+/* reset DP Mainlink */
+void mdss_dp_mainlink_reset(struct dss_io_data *ctrl_io)
+{
+ u32 mainlink_ctrl = readl_relaxed(ctrl_io->base + DP_MAINLINK_CTRL);
+
+ mainlink_ctrl |= BIT(1);
+ writel_relaxed(mainlink_ctrl, ctrl_io->base + DP_MAINLINK_CTRL);
+ udelay(1000);
+ mainlink_ctrl &= ~BIT(1);
+ writel_relaxed(mainlink_ctrl, ctrl_io->base + DP_MAINLINK_CTRL);
+}
+
+/* Configure HPD */
+void mdss_dp_hpd_configure(struct dss_io_data *ctrl_io, bool enable)
+{
+ if (enable) {
+ u32 reftimer =
+ readl_relaxed(ctrl_io->base + DP_DP_HPD_REFTIMER);
+
+ writel_relaxed(0xf, ctrl_io->base + DP_DP_HPD_INT_ACK);
+ writel_relaxed(0xf, ctrl_io->base + DP_DP_HPD_INT_MASK);
+
+ /* Enabling REFTIMER */
+ reftimer |= BIT(16);
+ writel_relaxed(0xf, ctrl_io->base + DP_DP_HPD_REFTIMER);
+ /* Enable HPD */
+ writel_relaxed(0x1, ctrl_io->base + DP_DP_HPD_CTRL);
+ } else {
+ /*Disable HPD */
+ writel_relaxed(0x0, ctrl_io->base + DP_DP_HPD_CTRL);
+ }
+}
+
+/* Enable/Disable AUX controller */
+void mdss_dp_aux_ctrl(struct dss_io_data *ctrl_io, bool enable)
+{
+ u32 aux_ctrl = readl_relaxed(ctrl_io->base + DP_AUX_CTRL);
+
+ if (enable)
+ aux_ctrl |= BIT(0);
+ else
+ aux_ctrl &= ~BIT(0);
+
+ writel_relaxed(aux_ctrl, ctrl_io->base + DP_AUX_CTRL);
+}
+
+/* DP Mainlink controller*/
+void mdss_dp_mainlink_ctrl(struct dss_io_data *ctrl_io, bool enable)
+{
+ u32 mainlink_ctrl = readl_relaxed(ctrl_io->base + DP_MAINLINK_CTRL);
+
+ if (enable)
+ mainlink_ctrl |= BIT(0);
+ else
+ mainlink_ctrl &= ~BIT(0);
+
+ writel_relaxed(mainlink_ctrl, ctrl_io->base + DP_MAINLINK_CTRL);
+}
+
+int mdss_dp_mainlink_ready(struct mdss_dp_drv_pdata *dp, u32 which)
+{
+ u32 data;
+ int cnt = 10;
+
+ while (--cnt) {
+ /* DP_MAINLINK_READY */
+ data = readl_relaxed(dp->base + DP_MAINLINK_READY);
+ if (data & which) {
+ pr_debug("which=%x ready\n", which);
+ return 1;
+ }
+ udelay(1000);
+ }
+ pr_err("which=%x NOT ready\n", which);
+
+ return 0;
+}
+
+/* DP Configuration controller*/
+void mdss_dp_configuration_ctrl(struct dss_io_data *ctrl_io, u32 data)
+{
+ writel_relaxed(data, ctrl_io->base + DP_CONFIGURATION_CTRL);
+}
+
+/* DP state controller*/
+void mdss_dp_state_ctrl(struct dss_io_data *ctrl_io, u32 data)
+{
+ writel_relaxed(data, ctrl_io->base + DP_STATE_CTRL);
+}
+
+void mdss_dp_timing_cfg(struct dss_io_data *ctrl_io,
+ struct mdss_panel_info *pinfo)
+{
+ u32 total_ver, total_hor;
+ u32 data;
+
+ pr_debug("width=%d hporch= %d %d %d\n",
+ pinfo->xres, pinfo->lcdc.h_back_porch,
+ pinfo->lcdc.h_front_porch, pinfo->lcdc.h_pulse_width);
+
+ pr_debug("height=%d vporch= %d %d %d\n",
+ pinfo->yres, pinfo->lcdc.v_back_porch,
+ pinfo->lcdc.v_front_porch, pinfo->lcdc.v_pulse_width);
+
+ total_hor = pinfo->xres + pinfo->lcdc.h_back_porch +
+ pinfo->lcdc.h_front_porch + pinfo->lcdc.h_pulse_width;
+
+ total_ver = pinfo->yres + pinfo->lcdc.v_back_porch +
+ pinfo->lcdc.v_front_porch + pinfo->lcdc.v_pulse_width;
+
+ data = total_ver;
+ data <<= 16;
+ data |= total_hor;
+ /* DP_TOTAL_HOR_VER */
+ writel_relaxed(data, ctrl_io->base + DP_TOTAL_HOR_VER);
+
+ data = (pinfo->lcdc.v_back_porch + pinfo->lcdc.v_pulse_width);
+ data <<= 16;
+ data |= (pinfo->lcdc.h_back_porch + pinfo->lcdc.h_pulse_width);
+ /* DP_START_HOR_VER_FROM_SYNC */
+ writel_relaxed(data, ctrl_io->base + DP_START_HOR_VER_FROM_SYNC);
+
+ data = pinfo->lcdc.v_pulse_width;
+ data <<= 16;
+ data |= pinfo->lcdc.h_pulse_width;
+ /* DP_HSYNC_VSYNC_WIDTH_POLARITY */
+ writel_relaxed(data, ctrl_io->base + DP_HSYNC_VSYNC_WIDTH_POLARITY);
+
+ data = pinfo->yres;
+ data <<= 16;
+ data |= pinfo->xres;
+ /* DP_ACTIVE_HOR_VER */
+ writel_relaxed(data, ctrl_io->base + DP_ACTIVE_HOR_VER);
+}
+
+void mdss_dp_sw_mvid_nvid(struct dss_io_data *ctrl_io)
+{
+ writel_relaxed(0x37, ctrl_io->base + DP_SOFTWARE_MVID);
+ writel_relaxed(0x3c, ctrl_io->base + DP_SOFTWARE_NVID);
+}
+
+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)));
+ writel_relaxed(lane_map,
+ ctrl_io->base + DP_LOGICAL2PHYSCIAL_LANE_MAPPING);
+}
+
+void mdss_dp_phy_aux_setup(struct dss_io_data *phy_io)
+{
+ writel_relaxed(0x3d, phy_io->base + DP_PHY_PD_CTL);
+ writel_relaxed(0x03, phy_io->base + DP_PHY_AUX_CFG1);
+ writel_relaxed(0x00, phy_io->base + DP_PHY_AUX_CFG3);
+ writel_relaxed(0x0a, phy_io->base + DP_PHY_AUX_CFG4);
+ writel_relaxed(0x26, phy_io->base + DP_PHY_AUX_CFG5);
+ writel_relaxed(0x0a, phy_io->base + DP_PHY_AUX_CFG6);
+ writel_relaxed(0x03, phy_io->base + DP_PHY_AUX_CFG7);
+ writel_relaxed(0xbb, phy_io->base + DP_PHY_AUX_CFG8);
+ writel_relaxed(0x03, phy_io->base + DP_PHY_AUX_CFG9);
+ writel_relaxed(0x1f, phy_io->base + DP_PHY_AUX_INTERRUPT_MASK);
+}
+
+int mdss_dp_irq_setup(struct mdss_dp_drv_pdata *dp_drv)
+{
+ int ret = 0;
+
+ mdss_dp_hw.irq_info = mdss_intr_line();
+ if (mdss_dp_hw.irq_info == NULL) {
+ pr_err("Failed to get mdss irq information\n");
+ return -ENODEV;
+ }
+
+ mdss_dp_hw.ptr = (void *)(dp_drv);
+
+ ret = dp_drv->mdss_util->register_irq(&mdss_dp_hw);
+ if (ret)
+ pr_err("mdss_register_irq failed.\n");
+
+ return ret;
+}
+
+void mdss_dp_irq_enable(struct mdss_dp_drv_pdata *dp_drv)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dp_drv->lock, flags);
+ writel_relaxed(dp_drv->mask1, dp_drv->base + DP_INTR_STATUS);
+ writel_relaxed(dp_drv->mask2, dp_drv->base + DP_INTR_STATUS2);
+ spin_unlock_irqrestore(&dp_drv->lock, flags);
+
+ dp_drv->mdss_util->enable_irq(&mdss_dp_hw);
+}
+
+void mdss_dp_irq_disable(struct mdss_dp_drv_pdata *dp_drv)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dp_drv->lock, flags);
+ writel_relaxed(0x00, dp_drv->base + DP_INTR_STATUS);
+ writel_relaxed(0x00, dp_drv->base + DP_INTR_STATUS2);
+ spin_unlock_irqrestore(&dp_drv->lock, flags);
+
+ dp_drv->mdss_util->disable_irq(&mdss_dp_hw);
+}
diff --git a/drivers/video/fbdev/msm/mdss_dp_util.h b/drivers/video/fbdev/msm/mdss_dp_util.h
new file mode 100644
index 000000000000..d9064cafad9a
--- /dev/null
+++ b/drivers/video/fbdev/msm/mdss_dp_util.h
@@ -0,0 +1,111 @@
+/* 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.
+ */
+
+#ifndef __DP_UTIL_H__
+#define __DP_UTIL_H__
+
+#include "mdss_dp.h"
+
+/* DP_TX Registers */
+#define DP_HW_VERSION (0x00000000)
+#define DP_SW_RESET (0x00000010)
+#define DP_PHY_CTRL (0x00000014)
+#define DP_CLK_CTRL (0x00000018)
+#define DP_CLK_ACTIVE (0x0000001C)
+#define DP_INTR_STATUS (0x00000020)
+#define DP_INTR_STATUS2 (0x00000024)
+#define DP_INTR_STATUS3 (0x00000028)
+
+#define DP_DP_HPD_CTRL (0x00000200)
+#define DP_DP_HPD_INT_STATUS (0x00000204)
+#define DP_DP_HPD_INT_ACK (0x00000208)
+#define DP_DP_HPD_INT_MASK (0x0000020C)
+#define DP_DP_HPD_REFTIMER (0x00000218)
+#define DP_DP_HPD_EVENT_TIME_0 (0x0000021C)
+#define DP_DP_HPD_EVENT_TIME_1 (0x00000220)
+#define DP_AUX_CTRL (0x00000230)
+#define DP_AUX_DATA (0x00000234)
+#define DP_AUX_TRANS_CTRL (0x00000238)
+#define DP_AUX_STATUS (0x00000244)
+
+#define DP_INTERRUPT_TRANS_NUM (0x000002A0)
+
+#define DP_MAINLINK_CTRL (0x00000400)
+#define DP_STATE_CTRL (0x00000404)
+#define DP_CONFIGURATION_CTRL (0x00000408)
+#define DP_SOFTWARE_MVID (0x00000410)
+#define DP_SOFTWARE_NVID (0x00000418)
+#define DP_TOTAL_HOR_VER (0x0000041C)
+#define DP_START_HOR_VER_FROM_SYNC (0x00000420)
+#define DP_HSYNC_VSYNC_WIDTH_POLARITY (0x00000424)
+#define DP_ACTIVE_HOR_VER (0x00000428)
+
+#define DP_LOGICAL2PHYSCIAL_LANE_MAPPING (0x00000438)
+
+#define DP_MAINLINK_READY (0x00000440)
+
+/*DP PHY Register offsets */
+#define DP_PHY_REVISION_ID0 (0x00000000)
+#define DP_PHY_REVISION_ID1 (0x00000004)
+#define DP_PHY_REVISION_ID2 (0x00000008)
+#define DP_PHY_REVISION_ID3 (0x0000000C)
+
+#define DP_PHY_CFG (0x00000010)
+#define DP_PHY_PD_CTL (0x00000014)
+#define DP_PHY_MODE (0x00000018)
+
+#define DP_PHY_AUX_CFG0 (0x0000001C)
+#define DP_PHY_AUX_CFG1 (0x00000020)
+#define DP_PHY_AUX_CFG2 (0x00000024)
+#define DP_PHY_AUX_CFG3 (0x00000028)
+#define DP_PHY_AUX_CFG4 (0x0000002C)
+#define DP_PHY_AUX_CFG5 (0x00000030)
+#define DP_PHY_AUX_CFG6 (0x00000034)
+#define DP_PHY_AUX_CFG7 (0x00000038)
+#define DP_PHY_AUX_CFG8 (0x0000003C)
+#define DP_PHY_AUX_CFG9 (0x00000040)
+#define DP_PHY_AUX_INTERRUPT_MASK (0x00000044)
+#define DP_PHY_AUX_INTERRUPT_CLEAR (0x00000048)
+
+#define TCSR_USB3_DP_PHYMODE 0x48
+
+struct lane_mapping {
+ char lane0;
+ char lane1;
+ char lane2;
+ char lane3;
+};
+
+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);
+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_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);
+void mdss_dp_mainlink_ctrl(struct dss_io_data *ctrl_io, bool enable);
+void mdss_dp_ctrl_lane_mapping(struct dss_io_data *ctrl_io,
+ struct lane_mapping l_map);
+int mdss_dp_mainlink_ready(struct mdss_dp_drv_pdata *dp, u32 which);
+void mdss_dp_timing_cfg(struct dss_io_data *ctrl_io,
+ struct mdss_panel_info *pinfo);
+void mdss_dp_configuration_ctrl(struct dss_io_data *ctrl_io, u32 data);
+void mdss_dp_state_ctrl(struct dss_io_data *ctrl_io, u32 data);
+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);
+
+#endif /* __DP_UTIL_H__ */
diff --git a/drivers/video/fbdev/msm/mdss_edp.c b/drivers/video/fbdev/msm/mdss_edp.c
deleted file mode 100644
index add757c34e50..000000000000
--- a/drivers/video/fbdev/msm/mdss_edp.c
+++ /dev/null
@@ -1,1273 +0,0 @@
-/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/time.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/platform_device.h>
-#include <linux/of_platform.h>
-#include <linux/of_gpio.h>
-#include <linux/gpio.h>
-#include <linux/err.h>
-#include <linux/regulator/consumer.h>
-#include <linux/qpnp/pwm.h>
-#include <linux/clk.h>
-#include <linux/spinlock_types.h>
-#include <linux/kthread.h>
-#include <mach/hardware.h>
-#include <mach/dma.h>
-
-#include "mdss.h"
-#include "mdss_edp.h"
-#include "mdss_debug.h"
-
-#define RGB_COMPONENTS 3
-#define VDDA_MIN_UV 1800000 /* uV units */
-#define VDDA_MAX_UV 1800000 /* uV units */
-#define VDDA_UA_ON_LOAD 100000 /* uA units */
-#define VDDA_UA_OFF_LOAD 100 /* uA units */
-
-static int mdss_edp_regulator_on(struct mdss_edp_drv_pdata *edp_drv);
-/*
- * Init regulator needed for edp, 8974_l12
- */
-static int mdss_edp_regulator_init(struct mdss_edp_drv_pdata *edp_drv)
-{
- int ret;
-
- edp_drv->vdda_vreg = devm_regulator_get(&(edp_drv->pdev->dev), "vdda");
- if (IS_ERR(edp_drv->vdda_vreg)) {
- pr_err("%s: Could not get 8941_l12, ret = %ld\n", __func__,
- PTR_ERR(edp_drv->vdda_vreg));
- return -ENODEV;
- }
-
- ret = regulator_set_voltage(edp_drv->vdda_vreg,
- VDDA_MIN_UV, VDDA_MAX_UV);
- if (ret) {
- pr_err("%s: vdda_vreg set_voltage failed, ret=%d\n", __func__,
- ret);
- return -EINVAL;
- }
-
- ret = mdss_edp_regulator_on(edp_drv);
- if (ret)
- return ret;
-
- return 0;
-}
-
-/*
- * Set uA and enable vdda
- */
-static int mdss_edp_regulator_on(struct mdss_edp_drv_pdata *edp_drv)
-{
- int ret;
-
- ret = regulator_set_optimum_mode(edp_drv->vdda_vreg, VDDA_UA_ON_LOAD);
- if (ret < 0) {
- pr_err("%s: vdda_vreg set regulator mode failed.\n", __func__);
- return ret;
- }
-
- ret = regulator_enable(edp_drv->vdda_vreg);
- if (ret) {
- pr_err("%s: Failed to enable vdda_vreg regulator.\n", __func__);
- return ret;
- }
-
- return 0;
-}
-
-/*
- * Disable vdda and set uA
- */
-static int mdss_edp_regulator_off(struct mdss_edp_drv_pdata *edp_drv)
-{
- int ret;
-
- ret = regulator_disable(edp_drv->vdda_vreg);
- if (ret) {
- pr_err("%s: Failed to disable vdda_vreg regulator.\n",
- __func__);
- return ret;
- }
-
- ret = regulator_set_optimum_mode(edp_drv->vdda_vreg, VDDA_UA_OFF_LOAD);
- if (ret < 0) {
- pr_err("%s: vdda_vreg set regulator mode failed.\n",
- __func__);
- return ret;
- }
-
- return 0;
-}
-
-/*
- * Enables the gpio that supply power to the panel and enable the backlight
- */
-static int mdss_edp_gpio_panel_en(struct mdss_edp_drv_pdata *edp_drv)
-{
- int ret = 0;
-
- edp_drv->gpio_panel_en = of_get_named_gpio(edp_drv->pdev->dev.of_node,
- "gpio-panel-en", 0);
- if (!gpio_is_valid(edp_drv->gpio_panel_en)) {
- pr_err("%s: gpio_panel_en=%d not specified\n", __func__,
- edp_drv->gpio_panel_en);
- goto gpio_err;
- }
-
- ret = gpio_request(edp_drv->gpio_panel_en, "disp_enable");
- if (ret) {
- pr_err("%s: Request reset gpio_panel_en failed, ret=%d\n",
- __func__, ret);
- return ret;
- }
-
- ret = gpio_direction_output(edp_drv->gpio_panel_en, 1);
- if (ret) {
- pr_err("%s: Set direction for gpio_panel_en failed, ret=%d\n",
- __func__, ret);
- goto gpio_free;
- }
-
- return 0;
-
-gpio_free:
- gpio_free(edp_drv->gpio_panel_en);
-gpio_err:
- return -ENODEV;
-}
-
-static int mdss_edp_gpio_lvl_en(struct mdss_edp_drv_pdata *edp_drv)
-{
- int ret = 0;
-
- edp_drv->gpio_lvl_en = of_get_named_gpio(edp_drv->pdev->dev.of_node,
- "gpio-lvl-en", 0);
- if (!gpio_is_valid(edp_drv->gpio_lvl_en)) {
- pr_err("%s: gpio_lvl_en=%d not specified\n", __func__,
- edp_drv->gpio_lvl_en);
- ret = -ENODEV;
- goto gpio_err;
- }
-
- ret = gpio_request(edp_drv->gpio_lvl_en, "lvl_enable");
- if (ret) {
- pr_err("%s: Request reset gpio_lvl_en failed, ret=%d\n",
- __func__, ret);
- return ret;
- }
-
- ret = gpio_direction_output(edp_drv->gpio_lvl_en, 1);
- if (ret) {
- pr_err("%s: Set direction for gpio_lvl_en failed, ret=%d\n",
- __func__, ret);
- goto gpio_free;
- }
-
- return ret;
-
-gpio_free:
- gpio_free(edp_drv->gpio_lvl_en);
-gpio_err:
- return ret;
-}
-
-static int mdss_edp_pwm_config(struct mdss_edp_drv_pdata *edp_drv)
-{
- int ret = 0;
-
- ret = of_property_read_u32(edp_drv->pdev->dev.of_node,
- "qcom,panel-pwm-period", &edp_drv->pwm_period);
- if (ret) {
- pr_warn("%s: panel pwm period is not specified, %d", __func__,
- edp_drv->pwm_period);
- edp_drv->pwm_period = -EINVAL;
- }
-
- ret = of_property_read_u32(edp_drv->pdev->dev.of_node,
- "qcom,panel-lpg-channel", &edp_drv->lpg_channel);
- if (ret) {
- pr_warn("%s: panel lpg channel is not specified, %d", __func__,
- edp_drv->lpg_channel);
- edp_drv->lpg_channel = -EINVAL;
- }
-
- if (edp_drv->pwm_period != -EINVAL &&
- edp_drv->lpg_channel != -EINVAL) {
- edp_drv->bl_pwm = pwm_request(edp_drv->lpg_channel,
- "lcd-backlight");
- if (edp_drv->bl_pwm == NULL || IS_ERR(edp_drv->bl_pwm)) {
- pr_err("%s: pwm request failed", __func__);
- edp_drv->bl_pwm = NULL;
- return -EIO;
- }
- } else {
- edp_drv->bl_pwm = NULL;
- }
-
- return 0;
-}
-
-void mdss_edp_set_backlight(struct mdss_panel_data *pdata, u32 bl_level)
-{
- int ret = 0;
- struct mdss_edp_drv_pdata *edp_drv = NULL;
- int bl_max;
- int period_ns;
-
- edp_drv = container_of(pdata, struct mdss_edp_drv_pdata, panel_data);
- if (!edp_drv) {
- pr_err("%s: Invalid input data\n", __func__);
- return;
- }
-
- if (edp_drv->bl_pwm != NULL) {
- bl_max = edp_drv->panel_data.panel_info.bl_max;
- if (bl_level > bl_max)
- bl_level = bl_max;
-
- /* In order to avoid overflow, use the microsecond version
- * of pwm_config if the pwm_period is greater than or equal
- * to 1 second.
- */
- if (edp_drv->pwm_period >= USEC_PER_SEC) {
- ret = pwm_config_us(edp_drv->bl_pwm,
- bl_level * edp_drv->pwm_period / bl_max,
- edp_drv->pwm_period);
- if (ret) {
- pr_err("%s: pwm_config_us() failed err=%d.\n",
- __func__, ret);
- return;
- }
- } else {
- period_ns = edp_drv->pwm_period * NSEC_PER_USEC;
- ret = pwm_config(edp_drv->bl_pwm,
- bl_level * period_ns / bl_max,
- period_ns);
- if (ret) {
- pr_err("%s: pwm_config() failed err=%d.\n",
- __func__, ret);
- return;
- }
- }
-
- if (edp_drv->is_pwm_enabled) {
- pwm_disable(edp_drv->bl_pwm);
- edp_drv->is_pwm_enabled = 0;
- }
-
- ret = pwm_enable(edp_drv->bl_pwm);
- if (ret) {
- pr_err("%s: pwm_enable() failed err=%d\n", __func__,
- ret);
- return;
- }
- edp_drv->is_pwm_enabled = 1;
- }
-}
-
-int mdss_edp_mainlink_ready(struct mdss_edp_drv_pdata *ep, u32 which)
-{
- u32 data;
- int cnt = 10;
-
- while (--cnt) {
- data = edp_read(ep->base + 0x84); /* EDP_MAINLINK_READY */
- if (data & which) {
- pr_debug("%s: which=%x ready\n", __func__, which);
- return 1;
- }
- usleep_range(1000, 1000);
- }
- pr_err("%s: which=%x NOT ready\n", __func__, which);
-
- return 0;
-}
-
-void mdss_edp_mainlink_reset(struct mdss_edp_drv_pdata *ep)
-{
- edp_write(ep->base + 0x04, 0x02); /* EDP_MAINLINK_CTRL */
- usleep_range(1000, 1000);
- edp_write(ep->base + 0x04, 0); /* EDP_MAINLINK_CTRL */
-}
-
-void mdss_edp_mainlink_ctrl(struct mdss_edp_drv_pdata *ep, int enable)
-{
- u32 data;
-
- data = edp_read(ep->base + 0x04);
- data &= ~BIT(0);
-
- if (enable)
- data |= 0x1;
-
- edp_write(ep->base + 0x04, data);
-}
-
-void mdss_edp_state_ctrl(struct mdss_edp_drv_pdata *ep, u32 state)
-{
- edp_write(ep->base + EDP_STATE_CTRL, state);
-}
-
-void mdss_edp_aux_reset(struct mdss_edp_drv_pdata *ep)
-{
- /* reset AUX */
- edp_write(ep->base + 0x300, BIT(1)); /* EDP_AUX_CTRL */
- usleep_range(1000, 1000);
- edp_write(ep->base + 0x300, 0); /* EDP_AUX_CTRL */
-}
-
-void mdss_edp_aux_ctrl(struct mdss_edp_drv_pdata *ep, int enable)
-{
- u32 data;
-
- data = edp_read(ep->base + 0x300);
- if (enable)
- data |= 0x01;
- else
- data |= ~0x01;
- edp_write(ep->base + 0x300, data); /* EDP_AUX_CTRL */
-}
-
-void mdss_edp_phy_pll_reset(struct mdss_edp_drv_pdata *ep)
-{
- /* EDP_PHY_CTRL */
- edp_write(ep->base + 0x74, 0x005); /* bit 0, 2 */
- usleep_range(1000, 1000);
- edp_write(ep->base + 0x74, 0x000); /* EDP_PHY_CTRL */
-}
-
-int mdss_edp_phy_pll_ready(struct mdss_edp_drv_pdata *ep)
-{
- int cnt;
- u32 status = 0;
-
- cnt = 100;
- while (--cnt) {
- status = edp_read(ep->base + 0x6c0);
- if (status & 0x01)
- break;
- usleep_range(100, 100);
- }
-
- pr_debug("%s: PLL cnt=%d status=%x\n", __func__, cnt, (int)status);
-
- if (cnt <= 0) {
- pr_err("%s: PLL NOT ready\n", __func__);
- return 0;
- } else
- return 1;
-}
-
-int mdss_edp_phy_ready(struct mdss_edp_drv_pdata *ep)
-{
- u32 status;
-
- status = edp_read(ep->base + 0x598);
- status &= 0x01;
-
- return status;
-}
-
-void mdss_edp_phy_power_ctrl(struct mdss_edp_drv_pdata *ep, int enable)
-{
- if (enable) {
- /* EDP_PHY_EDPPHY_GLB_PD_CTL */
- edp_write(ep->base + 0x52c, 0x3f);
- /* EDP_PHY_EDPPHY_GLB_CFG */
- edp_write(ep->base + 0x528, 0x1);
- /* EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG */
- edp_write(ep->base + 0x620, 0xf);
- } else {
- /* EDP_PHY_EDPPHY_GLB_PD_CTL */
- edp_write(ep->base + 0x52c, 0xc0);
- }
-}
-
-void mdss_edp_lane_power_ctrl(struct mdss_edp_drv_pdata *ep, int up)
-{
- int i, off, max_lane;
- u32 data;
-
- max_lane = ep->lane_cnt;
-
- if (up)
- data = 0; /* power up */
- else
- data = 0x7; /* power down */
-
- /* EDP_PHY_EDPPHY_LNn_PD_CTL */
- for (i = 0; i < max_lane; i++) {
- off = 0x40 * i;
- edp_write(ep->base + 0x404 + off , data);
- }
-
- /* power down un used lane */
- data = 0x7; /* power down */
- for (i = max_lane; i < EDP_MAX_LANE; i++) {
- off = 0x40 * i;
- edp_write(ep->base + 0x404 + off , data);
- }
-}
-
-void mdss_edp_clock_synchrous(struct mdss_edp_drv_pdata *ep, int sync)
-{
- u32 data;
- u32 color;
-
- /* EDP_MISC1_MISC0 */
- data = edp_read(ep->base + 0x02c);
-
- if (sync)
- data |= 0x01;
- else
- data &= ~0x01;
-
- /* only legacy rgb mode supported */
- color = 0; /* 6 bits */
- if (ep->edid.color_depth == 8)
- color = 0x01;
- else if (ep->edid.color_depth == 10)
- color = 0x02;
- else if (ep->edid.color_depth == 12)
- color = 0x03;
- else if (ep->edid.color_depth == 16)
- color = 0x04;
-
- color <<= 5; /* bit 5 to bit 7 */
-
- data |= color;
- /* EDP_MISC1_MISC0 */
- edp_write(ep->base + 0x2c, data);
-}
-
-/* voltage mode and pre emphasis cfg */
-void mdss_edp_phy_vm_pe_init(struct mdss_edp_drv_pdata *ep)
-{
- /* EDP_PHY_EDPPHY_GLB_VM_CFG0 */
- edp_write(ep->base + 0x510, 0x3); /* vm only */
- /* EDP_PHY_EDPPHY_GLB_VM_CFG1 */
- edp_write(ep->base + 0x514, 0x64);
- /* EDP_PHY_EDPPHY_GLB_MISC9 */
- edp_write(ep->base + 0x518, 0x6c);
-}
-
-void mdss_edp_config_ctrl(struct mdss_edp_drv_pdata *ep)
-{
- struct dpcd_cap *cap;
- struct display_timing_desc *dp;
- u32 data = 0;
-
- dp = &ep->edid.timing[0];
-
- cap = &ep->dpcd;
-
- data = ep->lane_cnt - 1;
- data <<= 4;
-
- if (cap->enhanced_frame)
- data |= 0x40;
-
- if (ep->edid.color_depth == 8) {
- /* 0 == 6 bits, 1 == 8 bits */
- data |= 0x100; /* bit 8 */
- }
-
- if (!dp->interlaced) /* progressive */
- data |= 0x04;
-
- data |= 0x03; /* sycn clock & static Mvid */
-
- edp_write(ep->base + 0xc, data); /* EDP_CONFIGURATION_CTRL */
-}
-
-static void mdss_edp_sw_mvid_nvid(struct mdss_edp_drv_pdata *ep)
-{
- edp_write(ep->base + 0x14, 0x13b); /* EDP_SOFTWARE_MVID */
- edp_write(ep->base + 0x18, 0x266); /* EDP_SOFTWARE_NVID */
-}
-
-static void mdss_edp_timing_cfg(struct mdss_edp_drv_pdata *ep)
-{
- struct mdss_panel_info *pinfo;
- u32 total_ver, total_hor;
- u32 data;
-
- pinfo = &ep->panel_data.panel_info;
-
- pr_debug("%s: width=%d hporch= %d %d %d\n", __func__,
- pinfo->xres, pinfo->lcdc.h_back_porch,
- pinfo->lcdc.h_front_porch, pinfo->lcdc.h_pulse_width);
-
- pr_debug("%s: height=%d vporch= %d %d %d\n", __func__,
- pinfo->yres, pinfo->lcdc.v_back_porch,
- pinfo->lcdc.v_front_porch, pinfo->lcdc.v_pulse_width);
-
- total_hor = pinfo->xres + pinfo->lcdc.h_back_porch +
- pinfo->lcdc.h_front_porch + pinfo->lcdc.h_pulse_width;
-
- total_ver = pinfo->yres + pinfo->lcdc.v_back_porch +
- pinfo->lcdc.v_front_porch + pinfo->lcdc.v_pulse_width;
-
- data = total_ver;
- data <<= 16;
- data |= total_hor;
- edp_write(ep->base + 0x1c, data); /* EDP_TOTAL_HOR_VER */
-
- data = (pinfo->lcdc.v_back_porch + pinfo->lcdc.v_pulse_width);
- data <<= 16;
- data |= (pinfo->lcdc.h_back_porch + pinfo->lcdc.h_pulse_width);
- edp_write(ep->base + 0x20, data); /* EDP_START_HOR_VER_FROM_SYNC */
-
- data = pinfo->lcdc.v_pulse_width;
- data <<= 16;
- data |= pinfo->lcdc.h_pulse_width;
- edp_write(ep->base + 0x24, data); /* EDP_HSYNC_VSYNC_WIDTH_POLARITY */
-
- data = pinfo->yres;
- data <<= 16;
- data |= pinfo->xres;
- edp_write(ep->base + 0x28, data); /* EDP_ACTIVE_HOR_VER */
-}
-
-int mdss_edp_wait4train(struct mdss_edp_drv_pdata *edp_drv)
-{
- int ret = 0;
-
- if (edp_drv->cont_splash)
- return ret;
-
- ret = wait_for_completion_timeout(&edp_drv->video_comp, 30);
- if (ret <= 0) {
- pr_err("%s: Link Train timedout\n", __func__);
- ret = -EINVAL;
- } else {
- ret = 0;
- }
-
- pr_debug("%s:\n", __func__);
-
- return ret;
-}
-
-static void mdss_edp_irq_enable(struct mdss_edp_drv_pdata *edp_drv);
-static void mdss_edp_irq_disable(struct mdss_edp_drv_pdata *edp_drv);
-
-int mdss_edp_on(struct mdss_panel_data *pdata)
-{
- struct mdss_edp_drv_pdata *edp_drv = NULL;
- int ret = 0;
-
- if (!pdata) {
- pr_err("%s: Invalid input data\n", __func__);
- return -EINVAL;
- }
-
- edp_drv = container_of(pdata, struct mdss_edp_drv_pdata,
- panel_data);
-
- pr_debug("%s:+, cont_splash=%d\n", __func__, edp_drv->cont_splash);
-
- if (!edp_drv->cont_splash) { /* vote for clocks */
- mdss_edp_phy_pll_reset(edp_drv);
- mdss_edp_aux_reset(edp_drv);
- mdss_edp_mainlink_reset(edp_drv);
- mdss_edp_aux_ctrl(edp_drv, 1);
-
- ret = mdss_edp_prepare_clocks(edp_drv);
- if (ret)
- return ret;
-
- mdss_edp_phy_power_ctrl(edp_drv, 1);
-
- ret = mdss_edp_clk_enable(edp_drv);
- if (ret) {
- mdss_edp_unprepare_clocks(edp_drv);
- return ret;
- }
-
- mdss_edp_phy_pll_ready(edp_drv);
-
- mdss_edp_lane_power_ctrl(edp_drv, 1);
-
- mdss_edp_clock_synchrous(edp_drv, 1);
- mdss_edp_phy_vm_pe_init(edp_drv);
- mdss_edp_config_ctrl(edp_drv);
- mdss_edp_sw_mvid_nvid(edp_drv);
- mdss_edp_timing_cfg(edp_drv);
-
- gpio_set_value(edp_drv->gpio_panel_en, 1);
- if (gpio_is_valid(edp_drv->gpio_lvl_en))
- gpio_set_value(edp_drv->gpio_lvl_en, 1);
-
- reinit_completion(&edp_drv->idle_comp);
- mdss_edp_mainlink_ctrl(edp_drv, 1);
- } else {
- mdss_edp_aux_ctrl(edp_drv, 1);
- }
-
- mdss_edp_irq_enable(edp_drv);
-
- if (edp_drv->delay_link_train) {
- mdss_edp_link_train(edp_drv);
- edp_drv->delay_link_train = 0;
- }
-
- mdss_edp_wait4train(edp_drv);
-
- edp_drv->cont_splash = 0;
-
- pr_debug("%s:-\n", __func__);
- return ret;
-}
-
-int mdss_edp_off(struct mdss_panel_data *pdata)
-{
- struct mdss_edp_drv_pdata *edp_drv = NULL;
- int ret = 0;
-
- edp_drv = container_of(pdata, struct mdss_edp_drv_pdata,
- panel_data);
- if (!edp_drv) {
- pr_err("%s: Invalid input data\n", __func__);
- return -EINVAL;
- }
- pr_debug("%s:+, cont_splash=%d\n", __func__, edp_drv->cont_splash);
-
- /* wait until link training is completed */
- mutex_lock(&edp_drv->train_mutex);
-
- reinit_completion(&edp_drv->idle_comp);
- mdss_edp_state_ctrl(edp_drv, ST_PUSH_IDLE);
-
- ret = wait_for_completion_timeout(&edp_drv->idle_comp,
- msecs_to_jiffies(100));
- if (ret == 0)
- pr_err("%s: idle pattern timedout\n", __func__);
-
- mdss_edp_state_ctrl(edp_drv, 0);
-
- mdss_edp_sink_power_state(edp_drv, SINK_POWER_OFF);
-
- mdss_edp_irq_disable(edp_drv);
-
- gpio_set_value(edp_drv->gpio_panel_en, 0);
- if (gpio_is_valid(edp_drv->gpio_lvl_en))
- gpio_set_value(edp_drv->gpio_lvl_en, 0);
- if (edp_drv->bl_pwm != NULL)
- pwm_disable(edp_drv->bl_pwm);
- edp_drv->is_pwm_enabled = 0;
-
- mdss_edp_mainlink_reset(edp_drv);
- mdss_edp_mainlink_ctrl(edp_drv, 0);
-
- mdss_edp_lane_power_ctrl(edp_drv, 0);
- mdss_edp_phy_power_ctrl(edp_drv, 0);
-
- mdss_edp_clk_disable(edp_drv);
- mdss_edp_unprepare_clocks(edp_drv);
-
- mdss_edp_aux_ctrl(edp_drv, 0);
-
- pr_debug("%s-: state_ctrl=%x\n", __func__,
- edp_read(edp_drv->base + 0x8));
-
- mutex_unlock(&edp_drv->train_mutex);
- return 0;
-}
-
-static int mdss_edp_event_handler(struct mdss_panel_data *pdata,
- int event, void *arg)
-{
- int rc = 0;
-
- pr_debug("%s: event=%d\n", __func__, event);
- switch (event) {
- case MDSS_EVENT_UNBLANK:
- rc = mdss_edp_on(pdata);
- break;
- case MDSS_EVENT_PANEL_OFF:
- rc = mdss_edp_off(pdata);
- break;
- }
- return rc;
-}
-
-/*
- * Converts from EDID struct to mdss_panel_info
- */
-static void mdss_edp_edid2pinfo(struct mdss_edp_drv_pdata *edp_drv)
-{
- struct display_timing_desc *dp;
- struct mdss_panel_info *pinfo;
-
- dp = &edp_drv->edid.timing[0];
- pinfo = &edp_drv->panel_data.panel_info;
-
- pinfo->clk_rate = dp->pclk;
- pr_debug("%s: pclk=%d\n", __func__, pinfo->clk_rate);
-
- pinfo->xres = dp->h_addressable + dp->h_border * 2;
- pinfo->yres = dp->v_addressable + dp->v_border * 2;
-
- pr_debug("%s: x=%d y=%d\n", __func__, pinfo->xres, pinfo->yres);
-
- pinfo->lcdc.h_back_porch = dp->h_blank - dp->h_fporch \
- - dp->h_sync_pulse;
- pinfo->lcdc.h_front_porch = dp->h_fporch;
- pinfo->lcdc.h_pulse_width = dp->h_sync_pulse;
-
- pr_debug("%s: hporch= %d %d %d\n", __func__,
- pinfo->lcdc.h_back_porch, pinfo->lcdc.h_front_porch,
- pinfo->lcdc.h_pulse_width);
-
- pinfo->lcdc.v_back_porch = dp->v_blank - dp->v_fporch \
- - dp->v_sync_pulse;
- pinfo->lcdc.v_front_porch = dp->v_fporch;
- pinfo->lcdc.v_pulse_width = dp->v_sync_pulse;
-
- pr_debug("%s: vporch= %d %d %d\n", __func__,
- pinfo->lcdc.v_back_porch, pinfo->lcdc.v_front_porch,
- pinfo->lcdc.v_pulse_width);
-
- pinfo->type = EDP_PANEL;
- pinfo->pdest = DISPLAY_1;
- pinfo->wait_cycle = 0;
- pinfo->bpp = edp_drv->edid.color_depth * RGB_COMPONENTS;
- pinfo->fb_num = 2;
-
- pinfo->lcdc.border_clr = 0; /* black */
- pinfo->lcdc.underflow_clr = 0xff; /* blue */
- pinfo->lcdc.hsync_skew = 0;
-}
-
-static int mdss_edp_remove(struct platform_device *pdev)
-{
- struct mdss_edp_drv_pdata *edp_drv = NULL;
-
- edp_drv = platform_get_drvdata(pdev);
-
- gpio_free(edp_drv->gpio_panel_en);
- if (gpio_is_valid(edp_drv->gpio_lvl_en))
- gpio_free(edp_drv->gpio_lvl_en);
- mdss_edp_regulator_off(edp_drv);
- iounmap(edp_drv->base);
- iounmap(edp_drv->mmss_cc_base);
- edp_drv->base = NULL;
-
- return 0;
-}
-
-static int mdss_edp_device_register(struct mdss_edp_drv_pdata *edp_drv)
-{
- int ret;
- u32 tmp;
-
- mdss_edp_edid2pinfo(edp_drv);
- edp_drv->panel_data.panel_info.bl_min = 1;
- edp_drv->panel_data.panel_info.bl_max = 255;
- ret = of_property_read_u32(edp_drv->pdev->dev.of_node,
- "qcom,mdss-brightness-max-level", &tmp);
- edp_drv->panel_data.panel_info.brightness_max =
- (!ret ? tmp : MDSS_MAX_BL_BRIGHTNESS);
-
- edp_drv->panel_data.panel_info.edp.frame_rate =
- DEFAULT_FRAME_RATE;/* 60 fps */
-
- edp_drv->panel_data.event_handler = mdss_edp_event_handler;
- edp_drv->panel_data.set_backlight = mdss_edp_set_backlight;
-
- edp_drv->panel_data.panel_info.cont_splash_enabled =
- edp_drv->cont_splash;
-
- ret = mdss_register_panel(edp_drv->pdev, &edp_drv->panel_data);
- if (ret) {
- dev_err(&(edp_drv->pdev->dev), "unable to register eDP\n");
- return ret;
- }
-
- pr_info("%s: eDP initialized\n", __func__);
-
- return 0;
-}
-
-/*
- * Retrieve edp base address
- */
-static int mdss_edp_get_base_address(struct mdss_edp_drv_pdata *edp_drv)
-{
- struct resource *res;
-
- res = platform_get_resource_byname(edp_drv->pdev, IORESOURCE_MEM,
- "edp_base");
- if (!res) {
- pr_err("%s: Unable to get the MDSS EDP resources", __func__);
- return -ENOMEM;
- }
-
- edp_drv->base_size = resource_size(res);
- edp_drv->base = ioremap(res->start, resource_size(res));
- if (!edp_drv->base) {
- pr_err("%s: Unable to remap EDP resources", __func__);
- return -ENOMEM;
- }
-
- pr_debug("%s: drv=%x base=%x size=%x\n", __func__,
- (int)edp_drv, (int)edp_drv->base, edp_drv->base_size);
-
- mdss_debug_register_base("edp",
- edp_drv->base, edp_drv->base_size, NULL);
-
- return 0;
-}
-
-static int mdss_edp_get_mmss_cc_base_address(struct mdss_edp_drv_pdata
- *edp_drv)
-{
- struct resource *res;
-
- res = platform_get_resource_byname(edp_drv->pdev, IORESOURCE_MEM,
- "mmss_cc_base");
- if (!res) {
- pr_err("%s: Unable to get the MMSS_CC resources", __func__);
- return -ENOMEM;
- }
-
- edp_drv->mmss_cc_base = ioremap(res->start, resource_size(res));
- if (!edp_drv->mmss_cc_base) {
- pr_err("%s: Unable to remap MMSS_CC resources", __func__);
- return -ENOMEM;
- }
-
- return 0;
-}
-
-static void mdss_edp_video_ready(struct mdss_edp_drv_pdata *ep)
-{
- pr_debug("%s: edp_video_ready\n", __func__);
- complete(&ep->video_comp);
-}
-
-static void mdss_edp_idle_patterns_sent(struct mdss_edp_drv_pdata *ep)
-{
- pr_debug("%s: idle_patterns_sent\n", __func__);
- complete(&ep->idle_comp);
-}
-
-static void mdss_edp_do_link_train(struct mdss_edp_drv_pdata *ep)
-{
- if (ep->cont_splash)
- return;
-
- if (!ep->inited) {
- ep->delay_link_train++;
- return;
- }
-
- mdss_edp_link_train(ep);
-}
-
-static int edp_event_thread(void *data)
-{
- struct mdss_edp_drv_pdata *ep;
- unsigned long flag;
- u32 todo = 0;
-
- ep = (struct mdss_edp_drv_pdata *)data;
-
- while (1) {
- wait_event(ep->event_q, (ep->event_pndx != ep->event_gndx));
- spin_lock_irqsave(&ep->event_lock, flag);
- if (ep->event_pndx == ep->event_gndx) {
- spin_unlock_irqrestore(&ep->event_lock, flag);
- break;
- }
- todo = ep->event_todo_list[ep->event_gndx];
- ep->event_todo_list[ep->event_gndx++] = 0;
- ep->event_gndx %= HPD_EVENT_MAX;
- spin_unlock_irqrestore(&ep->event_lock, flag);
-
- pr_debug("%s: todo=%x\n", __func__, todo);
-
- if (todo == 0)
- continue;
-
- if (todo & EV_EDID_READ)
- mdss_edp_edid_read(ep, 0);
-
- if (todo & EV_DPCD_CAP_READ)
- mdss_edp_dpcd_cap_read(ep);
-
- if (todo & EV_DPCD_STATUS_READ)
- mdss_edp_dpcd_status_read(ep);
-
- if (todo & EV_LINK_TRAIN)
- mdss_edp_do_link_train(ep);
-
- if (todo & EV_VIDEO_READY)
- mdss_edp_video_ready(ep);
-
- if (todo & EV_IDLE_PATTERNS_SENT)
- mdss_edp_idle_patterns_sent(ep);
- }
-
- return 0;
-}
-
-static void edp_send_events(struct mdss_edp_drv_pdata *ep, u32 events)
-{
- spin_lock(&ep->event_lock);
- ep->event_todo_list[ep->event_pndx++] = events;
- ep->event_pndx %= HPD_EVENT_MAX;
- wake_up(&ep->event_q);
- spin_unlock(&ep->event_lock);
-}
-
-irqreturn_t edp_isr(int irq, void *ptr)
-{
- struct mdss_edp_drv_pdata *ep = (struct mdss_edp_drv_pdata *)ptr;
- unsigned char *base = ep->base;
- u32 isr1, isr2, mask1, mask2;
- u32 ack;
-
- spin_lock(&ep->lock);
- isr1 = edp_read(base + 0x308);
- isr2 = edp_read(base + 0x30c);
-
- mask1 = isr1 & ep->mask1;
- mask2 = isr2 & ep->mask2;
-
- isr1 &= ~mask1; /* remove masks bit */
- isr2 &= ~mask2;
-
- pr_debug("%s: isr=%x mask=%x isr2=%x mask2=%x\n",
- __func__, isr1, mask1, isr2, mask2);
-
- ack = isr1 & EDP_INTR_STATUS1;
- ack <<= 1; /* ack bits */
- ack |= mask1;
- edp_write(base + 0x308, ack);
-
- ack = isr2 & EDP_INTR_STATUS2;
- ack <<= 1; /* ack bits */
- ack |= mask2;
- edp_write(base + 0x30c, ack);
- spin_unlock(&ep->lock);
-
- if (isr1 & EDP_INTR_HPD) {
- isr1 &= ~EDP_INTR_HPD; /* clear */
- edp_send_events(ep, EV_LINK_TRAIN);
- }
-
- if (isr2 & EDP_INTR_READY_FOR_VIDEO)
- edp_send_events(ep, EV_VIDEO_READY);
-
- if (isr2 & EDP_INTR_IDLE_PATTERNs_SENT)
- edp_send_events(ep, EV_IDLE_PATTERNS_SENT);
-
- if (isr1 && ep->aux_cmd_busy) {
- /* clear EDP_AUX_TRANS_CTRL */
- edp_write(base + 0x318, 0);
- /* read EDP_INTERRUPT_TRANS_NUM */
- ep->aux_trans_num = edp_read(base + 0x310);
-
- if (ep->aux_cmd_i2c)
- edp_aux_i2c_handler(ep, isr1);
- else
- edp_aux_native_handler(ep, isr1);
- }
-
- return IRQ_HANDLED;
-}
-
-struct mdss_hw mdss_edp_hw = {
- .hw_ndx = MDSS_HW_EDP,
- .ptr = NULL,
- .irq_handler = edp_isr,
-};
-
-static void mdss_edp_irq_enable(struct mdss_edp_drv_pdata *edp_drv)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&edp_drv->lock, flags);
- edp_write(edp_drv->base + 0x308, edp_drv->mask1);
- edp_write(edp_drv->base + 0x30c, edp_drv->mask2);
- spin_unlock_irqrestore(&edp_drv->lock, flags);
-
- edp_drv->mdss_util->enable_irq(&mdss_edp_hw);
-}
-
-static void mdss_edp_irq_disable(struct mdss_edp_drv_pdata *edp_drv)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&edp_drv->lock, flags);
- edp_write(edp_drv->base + 0x308, 0x0);
- edp_write(edp_drv->base + 0x30c, 0x0);
- spin_unlock_irqrestore(&edp_drv->lock, flags);
-
- edp_drv->mdss_util->disable_irq(&mdss_edp_hw);
-}
-
-static int mdss_edp_irq_setup(struct mdss_edp_drv_pdata *edp_drv)
-{
- int ret = 0;
-
- edp_drv->gpio_panel_hpd = of_get_named_gpio_flags(
- edp_drv->pdev->dev.of_node, "gpio-panel-hpd", 0,
- &edp_drv->hpd_flags);
-
- if (!gpio_is_valid(edp_drv->gpio_panel_hpd)) {
- pr_err("%s gpio_panel_hpd %d is not valid ", __func__,
- edp_drv->gpio_panel_hpd);
- return -ENODEV;
- }
-
- ret = gpio_request(edp_drv->gpio_panel_hpd, "edp_hpd_irq_gpio");
- if (ret) {
- pr_err("%s unable to request gpio_panel_hpd %d", __func__,
- edp_drv->gpio_panel_hpd);
- return -ENODEV;
- }
-
- ret = gpio_tlmm_config(GPIO_CFG(
- edp_drv->gpio_panel_hpd,
- 1,
- GPIO_CFG_INPUT,
- GPIO_CFG_NO_PULL,
- GPIO_CFG_2MA),
- GPIO_CFG_ENABLE);
- if (ret) {
- pr_err("%s: unable to config tlmm = %d\n", __func__,
- edp_drv->gpio_panel_hpd);
- gpio_free(edp_drv->gpio_panel_hpd);
- return -ENODEV;
- }
-
- ret = gpio_direction_input(edp_drv->gpio_panel_hpd);
- if (ret) {
- pr_err("%s unable to set direction for gpio_panel_hpd %d",
- __func__, edp_drv->gpio_panel_hpd);
- return -ENODEV;
- }
-
- mdss_edp_hw.ptr = (void *)(edp_drv);
-
- if (edp_drv->mdss_util->register_irq(&mdss_edp_hw))
- pr_err("%s: mdss_register_irq failed.\n", __func__);
-
-
- return 0;
-}
-
-
-static void mdss_edp_event_setup(struct mdss_edp_drv_pdata *ep)
-{
- init_waitqueue_head(&ep->event_q);
- spin_lock_init(&ep->event_lock);
-
- kthread_run(edp_event_thread, (void *)ep, "mdss_edp_hpd");
-}
-
-static int mdss_edp_probe(struct platform_device *pdev)
-{
- int ret;
- struct mdss_edp_drv_pdata *edp_drv;
- struct mdss_panel_cfg *pan_cfg = NULL;
-
- if (!mdss_is_ready()) {
- pr_err("%s: MDP not probed yet!\n", __func__);
- return -EPROBE_DEFER;
- }
-
- pan_cfg = mdss_panel_intf_type(MDSS_PANEL_INTF_EDP);
- if (IS_ERR(pan_cfg)) {
- return PTR_ERR(pan_cfg);
- } else if (!pan_cfg) {
- pr_debug("%s: not configured as prim\n", __func__);
- return -ENODEV;
- }
-
- if (!pdev->dev.of_node) {
- pr_err("%s: Failed\n", __func__);
- return -EPERM;
- }
-
- edp_drv = devm_kzalloc(&pdev->dev, sizeof(*edp_drv), GFP_KERNEL);
- if (edp_drv == NULL) {
- pr_err("%s: Failed, could not allocate edp_drv", __func__);
- return -ENOMEM;
- }
-
- edp_drv->mdss_util = mdss_get_util_intf();
- if (edp_drv->mdss_util == NULL) {
- pr_err("Failed to get mdss utility functions\n");
- return -ENODEV;
- }
- edp_drv->panel_data.panel_info.is_prim_panel = true;
-
- mdss_edp_hw.irq_info = mdss_intr_line();
- if (mdss_edp_hw.irq_info == NULL) {
- pr_err("Failed to get mdss irq information\n");
- return -ENODEV;
- }
-
- edp_drv->pdev = pdev;
- edp_drv->pdev->id = 1;
- edp_drv->clk_on = 0;
- edp_drv->aux_rate = 19200000;
- edp_drv->mask1 = EDP_INTR_MASK1;
- edp_drv->mask2 = EDP_INTR_MASK2;
- mutex_init(&edp_drv->emutex);
- spin_lock_init(&edp_drv->lock);
-
- ret = mdss_edp_get_base_address(edp_drv);
- if (ret)
- goto probe_err;
-
- ret = mdss_edp_get_mmss_cc_base_address(edp_drv);
- if (ret)
- goto edp_base_unmap;
-
- ret = mdss_edp_regulator_init(edp_drv);
- if (ret)
- goto mmss_cc_base_unmap;
-
- ret = mdss_edp_clk_init(edp_drv);
- if (ret)
- goto edp_clk_deinit;
-
- ret = mdss_edp_gpio_panel_en(edp_drv);
- if (ret)
- goto edp_clk_deinit;
-
- ret = mdss_edp_gpio_lvl_en(edp_drv);
- if (ret)
- pr_err("%s: No gpio_lvl_en detected\n", __func__);
-
- ret = mdss_edp_pwm_config(edp_drv);
- if (ret)
- goto edp_free_gpio_panel_en;
-
- mdss_edp_irq_setup(edp_drv);
-
- mdss_edp_aux_init(edp_drv);
-
- mdss_edp_event_setup(edp_drv);
-
- edp_drv->cont_splash = edp_drv->mdss_util->panel_intf_status(DISPLAY_1,
- MDSS_PANEL_INTF_EDP) ? true : false;
-
- /* only need aux and ahb clock for aux channel */
- mdss_edp_prepare_aux_clocks(edp_drv);
- mdss_edp_aux_clk_enable(edp_drv);
-
- if (!edp_drv->cont_splash) {
- mdss_edp_phy_pll_reset(edp_drv);
- mdss_edp_aux_reset(edp_drv);
- mdss_edp_mainlink_reset(edp_drv);
- mdss_edp_phy_power_ctrl(edp_drv, 1);
- mdss_edp_aux_ctrl(edp_drv, 1);
- }
-
- mdss_edp_irq_enable(edp_drv);
-
- mdss_edp_edid_read(edp_drv, 0);
- mdss_edp_dpcd_cap_read(edp_drv);
- mdss_edp_fill_link_cfg(edp_drv);
-
- mdss_edp_irq_disable(edp_drv);
-
- if (!edp_drv->cont_splash) {
- mdss_edp_aux_ctrl(edp_drv, 0);
- mdss_edp_phy_power_ctrl(edp_drv, 0);
- }
-
- mdss_edp_aux_clk_disable(edp_drv);
- mdss_edp_unprepare_aux_clocks(edp_drv);
-
- if (edp_drv->cont_splash) { /* vote for clocks */
- mdss_edp_prepare_clocks(edp_drv);
- mdss_edp_clk_enable(edp_drv);
- }
-
- mdss_edp_device_register(edp_drv);
-
- edp_drv->inited = true;
-
- pr_debug("%s: done\n", __func__);
-
- return 0;
-
-
-edp_free_gpio_panel_en:
- gpio_free(edp_drv->gpio_panel_en);
- if (gpio_is_valid(edp_drv->gpio_lvl_en))
- gpio_free(edp_drv->gpio_lvl_en);
-edp_clk_deinit:
- mdss_edp_clk_deinit(edp_drv);
- mdss_edp_regulator_off(edp_drv);
-mmss_cc_base_unmap:
- iounmap(edp_drv->mmss_cc_base);
-edp_base_unmap:
- iounmap(edp_drv->base);
-probe_err:
- return ret;
-
-}
-
-static const struct of_device_id msm_mdss_edp_dt_match[] = {
- {.compatible = "qcom,mdss-edp"},
- {}
-};
-MODULE_DEVICE_TABLE(of, msm_mdss_edp_dt_match);
-
-static struct platform_driver mdss_edp_driver = {
- .probe = mdss_edp_probe,
- .remove = mdss_edp_remove,
- .shutdown = NULL,
- .driver = {
- .name = "mdss_edp",
- .of_match_table = msm_mdss_edp_dt_match,
- },
-};
-
-static int __init mdss_edp_init(void)
-{
- int ret;
-
- ret = platform_driver_register(&mdss_edp_driver);
- if (ret) {
- pr_err("%s driver register failed", __func__);
- return ret;
- }
-
- return ret;
-}
-module_init(mdss_edp_init);
-
-static void __exit mdss_edp_driver_cleanup(void)
-{
- platform_driver_unregister(&mdss_edp_driver);
-}
-module_exit(mdss_edp_driver_cleanup);
-
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("eDP controller driver");
diff --git a/drivers/video/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/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
index fafc74d79ac1..1f62232e196b 100644
--- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c
+++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
@@ -18,7 +18,7 @@
#include <linux/clk/msm-clk.h>
#include "mdss_dsi.h"
-#include "mdss_edp.h"
+#include "mdss_dp.h"
#include "mdss_dsi_phy.h"
#define MDSS_DSI_DSIPHY_REGULATOR_CTRL_0 0x00
@@ -2423,314 +2423,3 @@ int mdss_dsi_pre_clkon_cb(void *priv,
return rc;
}
-
-void mdss_edp_clk_deinit(struct mdss_edp_drv_pdata *edp_drv)
-{
- if (edp_drv->aux_clk)
- clk_put(edp_drv->aux_clk);
- if (edp_drv->pixel_clk)
- clk_put(edp_drv->pixel_clk);
- if (edp_drv->ahb_clk)
- clk_put(edp_drv->ahb_clk);
- if (edp_drv->link_clk)
- clk_put(edp_drv->link_clk);
- if (edp_drv->mdp_core_clk)
- clk_put(edp_drv->mdp_core_clk);
-}
-
-int mdss_edp_clk_init(struct mdss_edp_drv_pdata *edp_drv)
-{
- struct device *dev = &(edp_drv->pdev->dev);
-
- edp_drv->aux_clk = clk_get(dev, "core_clk");
- if (IS_ERR(edp_drv->aux_clk)) {
- pr_err("%s: Can't find aux_clk", __func__);
- edp_drv->aux_clk = NULL;
- goto mdss_edp_clk_err;
- }
-
- edp_drv->pixel_clk = clk_get(dev, "pixel_clk");
- if (IS_ERR(edp_drv->pixel_clk)) {
- pr_err("%s: Can't find pixel_clk", __func__);
- edp_drv->pixel_clk = NULL;
- goto mdss_edp_clk_err;
- }
-
- edp_drv->ahb_clk = clk_get(dev, "iface_clk");
- if (IS_ERR(edp_drv->ahb_clk)) {
- pr_err("%s: Can't find ahb_clk", __func__);
- edp_drv->ahb_clk = NULL;
- goto mdss_edp_clk_err;
- }
-
- edp_drv->link_clk = clk_get(dev, "link_clk");
- if (IS_ERR(edp_drv->link_clk)) {
- pr_err("%s: Can't find link_clk", __func__);
- edp_drv->link_clk = NULL;
- goto mdss_edp_clk_err;
- }
-
- /* need mdss clock to receive irq */
- edp_drv->mdp_core_clk = clk_get(dev, "mdp_core_clk");
- if (IS_ERR(edp_drv->mdp_core_clk)) {
- pr_err("%s: Can't find mdp_core_clk", __func__);
- edp_drv->mdp_core_clk = NULL;
- goto mdss_edp_clk_err;
- }
-
- return 0;
-
-mdss_edp_clk_err:
- mdss_edp_clk_deinit(edp_drv);
- return -EPERM;
-}
-
-int mdss_edp_aux_clk_enable(struct mdss_edp_drv_pdata *edp_drv)
-{
- int ret;
-
- if (clk_set_rate(edp_drv->aux_clk, 19200000) < 0)
- pr_err("%s: aux_clk - clk_set_rate failed\n",
- __func__);
-
- ret = clk_enable(edp_drv->aux_clk);
- if (ret) {
- pr_err("%s: Failed to enable aux clk\n", __func__);
- goto c2;
- }
-
- ret = clk_enable(edp_drv->ahb_clk);
- if (ret) {
- pr_err("%s: Failed to enable ahb clk\n", __func__);
- goto c1;
- }
-
- /* need mdss clock to receive irq */
- ret = clk_enable(edp_drv->mdp_core_clk);
- if (ret) {
- pr_err("%s: Failed to enable mdp_core_clk\n", __func__);
- goto c0;
- }
-
- return 0;
-c0:
- clk_disable(edp_drv->ahb_clk);
-c1:
- clk_disable(edp_drv->aux_clk);
-c2:
- return ret;
-
-}
-
-void mdss_edp_aux_clk_disable(struct mdss_edp_drv_pdata *edp_drv)
-{
- clk_disable(edp_drv->aux_clk);
- clk_disable(edp_drv->ahb_clk);
- clk_disable(edp_drv->mdp_core_clk);
-}
-
-static void mdss_edp_clk_set_rate(struct mdss_edp_drv_pdata *edp_drv)
-{
- if (clk_set_rate(edp_drv->link_clk, edp_drv->link_rate * 27000000) < 0)
- pr_err("%s: link_clk - clk_set_rate failed\n",
- __func__);
-
- if (clk_set_rate(edp_drv->pixel_clk, edp_drv->pixel_rate) < 0)
- pr_err("%s: pixel_clk - clk_set_rate failed\n",
- __func__);
-}
-
-int mdss_edp_clk_enable(struct mdss_edp_drv_pdata *edp_drv)
-{
- int ret;
-
- if (edp_drv->clk_on) {
- pr_info("%s: edp clks are already ON\n", __func__);
- return 0;
- }
-
- if (clk_set_rate(edp_drv->link_clk, edp_drv->link_rate * 27000000) < 0)
- pr_err("%s: link_clk - clk_set_rate failed\n",
- __func__);
-
- if (clk_set_rate(edp_drv->aux_clk, edp_drv->aux_rate) < 0)
- pr_err("%s: aux_clk - clk_set_rate failed\n",
- __func__);
-
- if (clk_set_rate(edp_drv->pixel_clk, edp_drv->pixel_rate) < 0)
- pr_err("%s: pixel_clk - clk_set_rate failed\n",
- __func__);
-
- ret = clk_enable(edp_drv->aux_clk);
- if (ret) {
- pr_err("%s: Failed to enable aux clk\n", __func__);
- goto c4;
- }
- ret = clk_enable(edp_drv->pixel_clk);
- if (ret) {
- pr_err("%s: Failed to enable pixel clk\n", __func__);
- goto c3;
- }
- ret = clk_enable(edp_drv->ahb_clk);
- if (ret) {
- pr_err("%s: Failed to enable ahb clk\n", __func__);
- goto c2;
- }
- ret = clk_enable(edp_drv->link_clk);
- if (ret) {
- pr_err("%s: Failed to enable link clk\n", __func__);
- goto c1;
- }
- ret = clk_enable(edp_drv->mdp_core_clk);
- if (ret) {
- pr_err("%s: Failed to enable mdp_core_clk\n", __func__);
- goto c0;
- }
-
- edp_drv->clk_on = 1;
-
- return 0;
-
-c0:
- clk_disable(edp_drv->link_clk);
-c1:
- clk_disable(edp_drv->ahb_clk);
-c2:
- clk_disable(edp_drv->pixel_clk);
-c3:
- clk_disable(edp_drv->aux_clk);
-c4:
- return ret;
-}
-
-void mdss_edp_clk_disable(struct mdss_edp_drv_pdata *edp_drv)
-{
- if (edp_drv->clk_on == 0) {
- pr_info("%s: edp clks are already OFF\n", __func__);
- return;
- }
-
- clk_disable(edp_drv->aux_clk);
- clk_disable(edp_drv->pixel_clk);
- clk_disable(edp_drv->ahb_clk);
- clk_disable(edp_drv->link_clk);
- clk_disable(edp_drv->mdp_core_clk);
-
- edp_drv->clk_on = 0;
-}
-
-int mdss_edp_prepare_aux_clocks(struct mdss_edp_drv_pdata *edp_drv)
-{
- int ret;
-
- /* ahb clock should be prepared first */
- ret = clk_prepare(edp_drv->ahb_clk);
- if (ret) {
- pr_err("%s: Failed to prepare ahb clk\n", __func__);
- goto c3;
- }
- ret = clk_prepare(edp_drv->aux_clk);
- if (ret) {
- pr_err("%s: Failed to prepare aux clk\n", __func__);
- goto c2;
- }
-
- /* need mdss clock to receive irq */
- ret = clk_prepare(edp_drv->mdp_core_clk);
- if (ret) {
- pr_err("%s: Failed to prepare mdp_core clk\n", __func__);
- goto c1;
- }
-
- return 0;
-c1:
- clk_unprepare(edp_drv->aux_clk);
-c2:
- clk_unprepare(edp_drv->ahb_clk);
-c3:
- return ret;
-
-}
-
-void mdss_edp_unprepare_aux_clocks(struct mdss_edp_drv_pdata *edp_drv)
-{
- clk_unprepare(edp_drv->mdp_core_clk);
- clk_unprepare(edp_drv->aux_clk);
- clk_unprepare(edp_drv->ahb_clk);
-}
-
-int mdss_edp_prepare_clocks(struct mdss_edp_drv_pdata *edp_drv)
-{
- int ret;
-
- mdss_edp_clk_set_rate(edp_drv);
-
- /* ahb clock should be prepared first */
- ret = clk_prepare(edp_drv->ahb_clk);
- if (ret) {
- pr_err("%s: Failed to prepare ahb clk\n", __func__);
- goto c4;
- }
- ret = clk_prepare(edp_drv->aux_clk);
- if (ret) {
- pr_err("%s: Failed to prepare aux clk\n", __func__);
- goto c3;
- }
- ret = clk_prepare(edp_drv->pixel_clk);
- if (ret) {
- pr_err("%s: Failed to prepare pixel clk\n", __func__);
- goto c2;
- }
- ret = clk_prepare(edp_drv->link_clk);
- if (ret) {
- pr_err("%s: Failed to prepare link clk\n", __func__);
- goto c1;
- }
- ret = clk_prepare(edp_drv->mdp_core_clk);
- if (ret) {
- pr_err("%s: Failed to prepare mdp_core clk\n", __func__);
- goto c0;
- }
-
- return 0;
-c0:
- clk_unprepare(edp_drv->link_clk);
-c1:
- clk_unprepare(edp_drv->pixel_clk);
-c2:
- clk_unprepare(edp_drv->aux_clk);
-c3:
- clk_unprepare(edp_drv->ahb_clk);
-c4:
- return ret;
-}
-
-void mdss_edp_unprepare_clocks(struct mdss_edp_drv_pdata *edp_drv)
-{
- clk_unprepare(edp_drv->mdp_core_clk);
- clk_unprepare(edp_drv->aux_clk);
- clk_unprepare(edp_drv->pixel_clk);
- clk_unprepare(edp_drv->link_clk);
- /* ahb clock should be last one to disable */
- clk_unprepare(edp_drv->ahb_clk);
-}
-
-void mdss_edp_clk_debug(unsigned char *edp_base, unsigned char *mmss_cc_base)
-{
- u32 da4, da0, d32c;
- u32 dc4, dc0, d330;
-
- /* pixel clk */
- da0 = edp_read(mmss_cc_base + 0x0a0);
- da4 = edp_read(mmss_cc_base + 0x0a4);
- d32c = edp_read(mmss_cc_base + 0x32c);
-
- /* main link clk */
- dc0 = edp_read(mmss_cc_base + 0x0c0);
- dc4 = edp_read(mmss_cc_base + 0x0c4);
- d330 = edp_read(mmss_cc_base + 0x330);
-
- pr_err("%s: da0=%x da4=%x d32c=%x dc0=%x dc4=%x d330=%x\n", __func__,
- (int)da0, (int)da4, (int)d32c, (int)dc0, (int)dc4, (int)d330);
-
-}
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..b2fb3c4a56ae
--- /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/ipa_usb.h b/include/linux/ipa_usb.h
index c3885c72e5ea..0fe0e36c551f 100644
--- a/include/linux/ipa_usb.h
+++ b/include/linux/ipa_usb.h
@@ -94,7 +94,7 @@ struct ipa_usb_xdci_connect_params {
* ipa_usb_xdci_chan_scratch - xDCI protocol SW config area of
* channel scratch
*
- * @last_trb_addr: Address (LSB - based on alignment restrictions) of
+ * @last_trb_addr_iova: Address (iova LSB - based on alignment restrictions) of
* last TRB in queue. Used to identify roll over case
* @const_buffer_size: TRB buffer size in KB (similar to IPA aggregation
* configuration). Must be aligned to max USB Packet Size.
@@ -103,7 +103,7 @@ struct ipa_usb_xdci_connect_params {
* @depcmd_hi_addr: Used to generate "Update Transfer" command.
*/
struct ipa_usb_xdci_chan_scratch {
- u16 last_trb_addr;
+ u16 last_trb_addr_iova;
u8 const_buffer_size;
u32 depcmd_low_addr;
u8 depcmd_hi_addr;
@@ -124,6 +124,11 @@ struct ipa_usb_xdci_chan_scratch {
* @xfer_ring_base_addr: physical base address of transfer ring. Address must be
* aligned to xfer_ring_len rounded to power of two
* @xfer_scratch: parameters for xDCI channel scratch
+ * @xfer_ring_base_addr_iova: IO virtual address mapped to xfer_ring_base_addr
+ * @data_buff_base_len: length of data buffer allocated by USB driver
+ * @data_buff_base_addr: physical base address for the data buffer (where TRBs
+ * points)
+ * @data_buff_base_addr_iova: IO virtual address mapped to data_buff_base_addr
*
*/
struct ipa_usb_xdci_chan_params {
@@ -140,6 +145,10 @@ struct ipa_usb_xdci_chan_params {
u16 xfer_ring_len;
u64 xfer_ring_base_addr;
struct ipa_usb_xdci_chan_scratch xfer_scratch;
+ u64 xfer_ring_base_addr_iova;
+ u32 data_buff_base_len;
+ u64 data_buff_base_addr;
+ u64 data_buff_base_addr_iova;
};
/**
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/xfrm.h b/include/net/xfrm.h
index d6f6e5006ee9..d18cbafc3455 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -285,11 +285,10 @@ struct xfrm_policy_afinfo {
unsigned short family;
struct dst_ops *dst_ops;
void (*garbage_collect)(struct net *net);
- struct dst_entry *(*dst_lookup)(struct net *net,
- int tos, int oif,
+ struct dst_entry *(*dst_lookup)(struct net *net, int tos,
const xfrm_address_t *saddr,
const xfrm_address_t *daddr);
- int (*get_saddr)(struct net *net, int oif,
+ int (*get_saddr)(struct net *net,
xfrm_address_t *saddr,
xfrm_address_t *daddr);
void (*decode_session)(struct sk_buff *skb,
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/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 27fe13a534b4..fe19c7596f8c 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -1166,6 +1166,13 @@ enum v4l2_mpeg_vidc_video_lowlatency_mode {
#define V4L2_CID_MPEG_VIDEO_MAX_QP_PACKED \
(V4L2_CID_MPEG_MSM_VIDC_BASE + 92)
+#define V4L2_CID_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8 \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 93)
+enum v4l2_mpeg_vidc_video_h264_transform_8x8 {
+ V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_DISABLE = 0,
+ V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_ENABLE = 1,
+};
+
/* Camera class control IDs */
#define V4L2_CID_CAMERA_CLASS_BASE (V4L2_CTRL_CLASS_CAMERA | 0x900)
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/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/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c
index 7b0edb37a115..ab7ab839b057 100644
--- a/net/ipv4/xfrm4_policy.c
+++ b/net/ipv4/xfrm4_policy.c
@@ -20,7 +20,7 @@
static struct xfrm_policy_afinfo xfrm4_policy_afinfo;
static struct dst_entry *__xfrm4_dst_lookup(struct net *net, struct flowi4 *fl4,
- int tos, int oif,
+ int tos,
const xfrm_address_t *saddr,
const xfrm_address_t *daddr)
{
@@ -29,7 +29,6 @@ static struct dst_entry *__xfrm4_dst_lookup(struct net *net, struct flowi4 *fl4,
memset(fl4, 0, sizeof(*fl4));
fl4->daddr = daddr->a4;
fl4->flowi4_tos = tos;
- fl4->flowi4_oif = oif;
if (saddr)
fl4->saddr = saddr->a4;
@@ -42,22 +41,22 @@ static struct dst_entry *__xfrm4_dst_lookup(struct net *net, struct flowi4 *fl4,
return ERR_CAST(rt);
}
-static struct dst_entry *xfrm4_dst_lookup(struct net *net, int tos, int oif,
+static struct dst_entry *xfrm4_dst_lookup(struct net *net, int tos,
const xfrm_address_t *saddr,
const xfrm_address_t *daddr)
{
struct flowi4 fl4;
- return __xfrm4_dst_lookup(net, &fl4, tos, oif, saddr, daddr);
+ return __xfrm4_dst_lookup(net, &fl4, tos, saddr, daddr);
}
-static int xfrm4_get_saddr(struct net *net, int oif,
+static int xfrm4_get_saddr(struct net *net,
xfrm_address_t *saddr, xfrm_address_t *daddr)
{
struct dst_entry *dst;
struct flowi4 fl4;
- dst = __xfrm4_dst_lookup(net, &fl4, 0, oif, NULL, daddr);
+ dst = __xfrm4_dst_lookup(net, &fl4, 0, NULL, daddr);
if (IS_ERR(dst))
return -EHOSTUNREACH;
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 31144c486c52..c2b523c498d8 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -891,8 +891,7 @@ static struct dst_entry *ip6_sk_dst_check(struct sock *sk,
#ifdef CONFIG_IPV6_SUBTREES
ip6_rt_check(&rt->rt6i_src, &fl6->saddr, np->saddr_cache) ||
#endif
- (!(fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF) &&
- (fl6->flowi6_oif && fl6->flowi6_oif != dst->dev->ifindex))) {
+ (fl6->flowi6_oif && fl6->flowi6_oif != dst->dev->ifindex)) {
dst_release(dst);
dst = NULL;
}
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index dd76806358fe..01d7ee57d937 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -1049,9 +1049,6 @@ static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
saved_fn = fn;
- if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
- oif = 0;
-
redo_rt6_select:
rt = rt6_select(fn, oif, strict);
if (rt->rt6i_nsiblings)
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index c074771a10f7..c23742462f02 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -27,7 +27,7 @@
static struct xfrm_policy_afinfo xfrm6_policy_afinfo;
-static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, int oif,
+static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos,
const xfrm_address_t *saddr,
const xfrm_address_t *daddr)
{
@@ -36,8 +36,6 @@ static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, int oif,
int err;
memset(&fl6, 0, sizeof(fl6));
- fl6.flowi6_oif = oif;
- fl6.flowi6_flags = FLOWI_FLAG_SKIP_NH_OIF;
memcpy(&fl6.daddr, daddr, sizeof(fl6.daddr));
if (saddr)
memcpy(&fl6.saddr, saddr, sizeof(fl6.saddr));
@@ -53,13 +51,13 @@ static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, int oif,
return dst;
}
-static int xfrm6_get_saddr(struct net *net, int oif,
+static int xfrm6_get_saddr(struct net *net,
xfrm_address_t *saddr, xfrm_address_t *daddr)
{
struct dst_entry *dst;
struct net_device *dev;
- dst = xfrm6_dst_lookup(net, 0, oif, NULL, daddr);
+ dst = xfrm6_dst_lookup(net, 0, NULL, daddr);
if (IS_ERR(dst))
return -EHOSTUNREACH;
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index b5e665b3cfb0..cf0193b74ae3 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -115,8 +115,7 @@ static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo)
rcu_read_unlock();
}
-static inline struct dst_entry *__xfrm_dst_lookup(struct net *net,
- int tos, int oif,
+static inline struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos,
const xfrm_address_t *saddr,
const xfrm_address_t *daddr,
int family)
@@ -128,15 +127,14 @@ static inline struct dst_entry *__xfrm_dst_lookup(struct net *net,
if (unlikely(afinfo == NULL))
return ERR_PTR(-EAFNOSUPPORT);
- dst = afinfo->dst_lookup(net, tos, oif, saddr, daddr);
+ dst = afinfo->dst_lookup(net, tos, saddr, daddr);
xfrm_policy_put_afinfo(afinfo);
return dst;
}
-static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x,
- int tos, int oif,
+static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos,
xfrm_address_t *prev_saddr,
xfrm_address_t *prev_daddr,
int family)
@@ -155,7 +153,7 @@ static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x,
daddr = x->coaddr;
}
- dst = __xfrm_dst_lookup(net, tos, oif, saddr, daddr, family);
+ dst = __xfrm_dst_lookup(net, tos, saddr, daddr, family);
if (!IS_ERR(dst)) {
if (prev_saddr != saddr)
@@ -1395,15 +1393,15 @@ int __xfrm_sk_clone_policy(struct sock *sk, const struct sock *osk)
}
static int
-xfrm_get_saddr(struct net *net, int oif, xfrm_address_t *local,
- xfrm_address_t *remote, unsigned short family)
+xfrm_get_saddr(struct net *net, xfrm_address_t *local, xfrm_address_t *remote,
+ unsigned short family)
{
int err;
struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
if (unlikely(afinfo == NULL))
return -EINVAL;
- err = afinfo->get_saddr(net, oif, local, remote);
+ err = afinfo->get_saddr(net, local, remote);
xfrm_policy_put_afinfo(afinfo);
return err;
}
@@ -1432,8 +1430,7 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, const struct flowi *fl,
remote = &tmpl->id.daddr;
local = &tmpl->saddr;
if (xfrm_addr_any(local, tmpl->encap_family)) {
- error = xfrm_get_saddr(net, fl->flowi_oif,
- &tmp, remote,
+ error = xfrm_get_saddr(net, &tmp, remote,
tmpl->encap_family);
if (error)
goto fail;
@@ -1712,8 +1709,8 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) {
family = xfrm[i]->props.family;
- dst = xfrm_dst_lookup(xfrm[i], tos, fl->flowi_oif,
- &saddr, &daddr, family);
+ dst = xfrm_dst_lookup(xfrm[i], tos, &saddr, &daddr,
+ family);
err = PTR_ERR(dst);
if (IS_ERR(dst))
goto put_states;
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/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;