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/power/qcom-charger/qpnp-smb2.txt8
-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-mtp.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-cdp.dts15
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-coresight.dtsi3
-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_defconfig17
-rw-r--r--arch/arm64/configs/msmcortex_defconfig1
-rw-r--r--arch/arm64/kernel/perf_event.c2
-rw-r--r--arch/um/configs/x86_64_defconfig2
-rw-r--r--drivers/clk/msm/Makefile6
-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/arm-smmu.c34
-rw-r--r--drivers/iommu/iommu-debug.c63
-rw-r--r--drivers/media/platform/msm/camera_v2/camera/camera.c4
-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/camera_v2/pproc/cpp/Makefile1
-rw-r--r--drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c183
-rw-r--r--drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h3
-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/qcom/qdsp6v2/q6audio_v2.c7
-rw-r--r--drivers/misc/uid_stat.c105
-rw-r--r--drivers/platform/msm/ipa/ipa_clients/ipa_usb.c6
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa.c26
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_dp.c13
-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.c220
-rw-r--r--drivers/power/qcom-charger/smb-lib.c84
-rw-r--r--drivers/power/qcom-charger/smb-lib.h8
-rw-r--r--drivers/power/qcom-charger/smb-reg.h9
-rw-r--r--drivers/scsi/ufs/ufshcd.c15
-rw-r--r--drivers/slimbus/slim-msm-ngd.c10
-rw-r--r--drivers/slimbus/slim-msm.h3
-rw-r--r--drivers/soc/qcom/Makefile4
-rw-r--r--drivers/soc/qcom/icnss.c14
-rw-r--r--drivers/soc/qcom/service-locator.c215
-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.c8
-rw-r--r--drivers/usb/host/xhci-pci.c1
-rw-r--r--drivers/usb/host/xhci-plat.c1
-rw-r--r--drivers/usb/host/xhci-ring.c3
-rw-r--r--drivers/usb/host/xhci.c8
-rw-r--r--drivers/usb/host/xhci.h1
-rw-r--r--drivers/usb/pd/policy_engine.c483
-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/linux/usb/usbpd.h156
-rw-r--r--include/net/xfrm.h5
-rw-r--r--include/soc/qcom/service-locator.h23
-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--input/touchscreen/gt9xx/goodix_tool.c615
-rw-r--r--input/touchscreen/gt9xx/gt9xx.c1810
-rw-r--r--input/touchscreen/gt9xx/gt9xx.h241
-rw-r--r--input/touchscreen/gt9xx/gt9xx_firmware.h6
-rw-r--r--input/touchscreen/gt9xx/gt9xx_update.c1930
-rw-r--r--kernel/Documentation/firmware_updater/request_firmware.txt22
-rw-r--r--kernel/Documentation/firmware_updater/synaptics_fw_updaterbin0 -> 666266 bytes
-rw-r--r--kernel/Documentation/firmware_updater/synaptics_fw_updater.c753
-rw-r--r--kernel/Documentation/firmware_updater/synaptics_fw_updater_readme.txt41
-rw-r--r--kernel/arch/arm/configs/omap3_beagle_android_defconfig2419
-rw-r--r--kernel/arch/arm/configs/panda_defconfig331
-rw-r--r--kernel/arch/arm/mach-omap2/board-omap3beagle.c1038
-rw-r--r--kernel/arch/arm/mach-omap2/board-omap4panda.c1053
-rw-r--r--kernel/drivers/input/touchscreen/Kconfig721
-rw-r--r--kernel/drivers/input/touchscreen/Makefile68
-rw-r--r--kernel/drivers/input/touchscreen/msg21xx/msg21xx_ts.c1757
-rw-r--r--kernel/drivers/input/touchscreen/synaptics_fw_update.c1587
-rw-r--r--kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.c2110
-rw-r--r--kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.h282
-rw-r--r--kernel/drivers/input/touchscreen/synaptics_rmi_dev.c710
-rw-r--r--kernel/include/linux/input/synaptics_dsx.h59
-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/msm/qdsp6v2/audio_calibration.c10
-rwxr-xr-xsound/soc/msm/qdsp6v2/msm-compress-q6-v2.c3
-rw-r--r--sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c5
-rw-r--r--sound/usb/card.c1
-rw-r--r--sound/usb/usb_audio_qmi_svc.c36
-rw-r--r--sound/usb/usb_audio_qmi_v01.c368
-rw-r--r--sound/usb/usb_audio_qmi_v01.h75
164 files changed, 33179 insertions, 3045 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/power/qcom-charger/qpnp-smb2.txt b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt
index 58db2c2350e4..368f0b8c9525 100644
--- a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt
+++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt
@@ -50,6 +50,12 @@ Charger specific properties:
Value type: <u32>
Definition: Specifies the DC input current limit in micro-amps.
+- qcom,wipower-max-uw
+ Usage: optional
+ Value type: <u32>
+ Definition: Specifies the DC input power limit in micro-watts.
+ If the value is not present, 8W is used as default.
+
=============================================
Second Level Nodes - SMB2 Charger Peripherals
=============================================
@@ -80,9 +86,7 @@ pmicobalt_charger: qcom,qpnp-smb2 {
#address-cells = <1>;
#size-cells = <1>;
- qcom,pmic-revid = <&pmicobalt_revid>;
qcom,suspend-input;
- qcom,disable-charging;
dpdm-supply = <&qusb_phy0>;
qcom,chgr@1000 {
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-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/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 c0777a7e193a..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>;
@@ -75,6 +76,8 @@
coresight-name = "coresight-tmc-etf";
+ arm,default-sink;
+
clocks = <&clock_gcc clk_qdss_clk>,
<&clock_gcc clk_qdss_a_clk>;
clock-names = "apb_pclk", "core_a_clk";
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..ca7ec6d173cd 100644
--- a/arch/arm64/configs/msmcortex-perf_defconfig
+++ b/arch/arm64/configs/msmcortex-perf_defconfig
@@ -379,6 +379,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 +468,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 +550,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..afa39ce02376 100644
--- a/arch/arm64/configs/msmcortex_defconfig
+++ b/arch/arm64/configs/msmcortex_defconfig
@@ -384,6 +384,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/Makefile b/drivers/clk/msm/Makefile
index daf0b81c9663..ecf0b09bb49a 100644
--- a/drivers/clk/msm/Makefile
+++ b/drivers/clk/msm/Makefile
@@ -13,9 +13,9 @@ obj-$(CONFIG_COMMON_CLK_MSM) += clock-debug.o
# MSM 8996
ifeq ($(CONFIG_COMMON_CLK_MSM), y)
- obj-$(CONFIG_ARCH_QCOM) += clock-gcc-8996.o
- obj-$(CONFIG_ARCH_QCOM) += clock-mmss-8996.o
- obj-$(CONFIG_ARCH_QCOM) += clock-cpu-8996.o
+ obj-$(CONFIG_ARCH_MSM8996) += clock-gcc-8996.o
+ obj-$(CONFIG_ARCH_MSM8996) += clock-mmss-8996.o
+ obj-$(CONFIG_ARCH_MSM8996) += clock-cpu-8996.o
endif
# MSM COBALT
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/arm-smmu.c b/drivers/iommu/arm-smmu.c
index ce3f8713df3e..db4b66bb18ed 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -568,6 +568,28 @@ static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu,
return NULL;
}
+static struct arm_smmu_master *find_smmu_master_by_sid(
+ struct arm_smmu_device *smmu, u32 sid)
+{
+ struct rb_node *next;
+ struct arm_smmu_master *master;
+ struct arm_smmu_master_cfg *cfg;
+ int i;
+
+ next = rb_first(&smmu->masters);
+ for (; next; next = rb_next(next)) {
+ master = container_of(next, struct arm_smmu_master, node);
+ cfg = &master->cfg;
+
+ for (i = 0; i < cfg->num_streamids; i++) {
+ if (cfg->streamids[i] == sid)
+ return master;
+ }
+ }
+
+ return NULL;
+}
+
static struct arm_smmu_master_cfg *
find_smmu_master_cfg(struct device *dev)
{
@@ -1175,8 +1197,9 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
bool fatal_asf;
void __iomem *gr1_base;
phys_addr_t phys_soft;
- u32 frsynra;
+ u32 sid;
bool non_fatal_fault = smmu_domain->non_fatal_faults;
+ struct arm_smmu_master *master;
static DEFINE_RATELIMIT_STATE(_rs,
DEFAULT_RATELIMIT_INTERVAL,
@@ -1231,7 +1254,9 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
iova = far;
phys_soft = arm_smmu_iova_to_phys(domain, iova);
- frsynra = readl_relaxed(gr1_base + ARM_SMMU_GR1_CBFRSYNRA(cfg->cbndx));
+ sid = readl_relaxed(gr1_base + ARM_SMMU_GR1_CBFRSYNRA(cfg->cbndx));
+ sid &= 0xffff;
+ master = find_smmu_master_by_sid(smmu, sid);
tmp = report_iommu_fault(domain, smmu->dev, iova, flags);
if (!tmp || (tmp == -EBUSY)) {
dev_dbg(smmu->dev,
@@ -1246,6 +1271,9 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
fsr);
if (__ratelimit(&_rs)) {
+ dev_err(smmu->dev, "Context Fault for %s\n",
+ master ? master->of_node->name : "Unknown SID");
+
dev_err(smmu->dev,
"Unhandled context fault: iova=0x%08lx, fsr=0x%x, fsynr=0x%x, cb=%d\n",
iova, fsr, fsynr, cfg->cbndx);
@@ -1271,7 +1299,7 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
dev_name(smmu->dev));
dev_err(smmu->dev,
"hard iova-to-phys (ATOS)=%pa\n", &phys_atos);
- dev_err(smmu->dev, "SID=0x%x\n", frsynra & 0xffff);
+ dev_err(smmu->dev, "SID=0x%x\n", sid);
}
ret = IRQ_NONE;
resume = RESUME_TERMINATE;
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/camera/camera.c b/drivers/media/platform/msm/camera_v2/camera/camera.c
index c1aeb8c43e81..3985df780216 100644
--- a/drivers/media/platform/msm/camera_v2/camera/camera.c
+++ b/drivers/media/platform/msm/camera_v2/camera/camera.c
@@ -538,7 +538,7 @@ static int camera_v4l2_fh_open(struct file *filep)
{
struct msm_video_device *pvdev = video_drvdata(filep);
struct camera_v4l2_private *sp;
- unsigned int stream_id;
+ unsigned long stream_id;
sp = kzalloc(sizeof(*sp), GFP_KERNEL);
if (!sp) {
@@ -617,7 +617,7 @@ static int camera_v4l2_open(struct file *filep)
int rc = 0;
struct v4l2_event event;
struct msm_video_device *pvdev = video_drvdata(filep);
- unsigned int opn_idx, idx;
+ unsigned long opn_idx, idx;
BUG_ON(!pvdev);
rc = camera_v4l2_fh_open(filep);
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/camera_v2/pproc/cpp/Makefile b/drivers/media/platform/msm/camera_v2/pproc/cpp/Makefile
index 56880f4d5676..2198352143f7 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/Makefile
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/Makefile
@@ -2,4 +2,5 @@ ccflags-y += -Idrivers/media/platform/msm/camera_v2
ccflags-y += -Idrivers/media/platform/msm/camera_v2/isp/
ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/msm_buf_mgr/
obj-$(CONFIG_MSM_CPP) += msm_cpp_soc.o msm_cpp.o
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
index 32eb883ed6c0..5a1e99adc7f3 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
@@ -83,8 +83,27 @@
if (IS_BATCH_BUFFER_ON_PREVIEW(new_frame)) \
iden = swap_iden; \
}
+
+#define SWAP_BUF_INDEX_FOR_BATCH_ON_PREVIEW(new_frame, buff_mgr_info, \
+ cur_index, swap_index) { \
+ if (IS_BATCH_BUFFER_ON_PREVIEW(new_frame)) \
+ buff_mgr_info.index = swap_index; \
+ else \
+ buff_mgr_info.index = cur_index; \
+}
+
+/*
+ * Default value for get buf to be used - 0xFFFFFFFF
+ * 0 is a valid index
+ * no valid index from userspace, use last buffer from queue.
+ */
+#define DEFAULT_OUTPUT_BUF_INDEX 0xFFFFFFFF
+#define IS_DEFAULT_OUTPUT_BUF_INDEX(index) \
+ ((index == DEFAULT_OUTPUT_BUF_INDEX) ? 1 : 0)
+
static int msm_cpp_buffer_ops(struct cpp_device *cpp_dev,
- uint32_t buff_mgr_ops, struct msm_buf_mngr_info *buff_mgr_info);
+ uint32_t buff_mgr_ops, uint32_t ids, void *arg);
+
static int msm_cpp_send_frame_to_hardware(struct cpp_device *cpp_dev,
struct msm_queue_cmd *frame_qcmd);
static int msm_cpp_send_command_to_hardware(struct cpp_device *cpp_dev,
@@ -92,6 +111,9 @@ static int msm_cpp_send_command_to_hardware(struct cpp_device *cpp_dev,
static int msm_cpp_update_gdscr_status(struct cpp_device *cpp_dev,
bool status);
+static int msm_cpp_buffer_private_ops(struct cpp_device *cpp_dev,
+ uint32_t buff_mgr_ops, uint32_t id, void *arg);
+
#if CONFIG_MSM_CPP_DBG
#define CPP_DBG(fmt, args...) pr_err(fmt, ##args)
#else
@@ -802,12 +824,9 @@ static int cpp_init_hardware(struct cpp_device *cpp_dev)
pr_err("%s: irq request fail\n", __func__);
goto req_irq_fail;
}
- cpp_dev->buf_mgr_subdev = msm_buf_mngr_get_subdev();
-
- rc = msm_cpp_buffer_ops(cpp_dev,
- VIDIOC_MSM_BUF_MNGR_INIT, NULL);
+ rc = msm_cam_buf_mgr_register_ops(&cpp_dev->buf_mgr_ops);
if (rc < 0) {
- pr_err("buf mngr init failed\n");
+ pr_err("buf mngr req ops failed\n");
msm_camera_unregister_irq(cpp_dev->pdev,
cpp_dev->irq, cpp_dev);
goto req_irq_fail;
@@ -878,12 +897,6 @@ static void cpp_release_hardware(struct cpp_device *cpp_dev)
{
int32_t rc;
if (cpp_dev->state != CPP_STATE_BOOT) {
- rc = msm_cpp_buffer_ops(cpp_dev,
- VIDIOC_MSM_BUF_MNGR_DEINIT, NULL);
- if (rc < 0) {
- pr_err("error in buf mngr deinit\n");
- rc = -EINVAL;
- }
msm_camera_unregister_irq(cpp_dev->pdev, cpp_dev->irq, cpp_dev);
tasklet_kill(&cpp_dev->cpp_tasklet);
atomic_set(&cpp_dev->irq_cnt, 0);
@@ -1193,12 +1206,28 @@ static const struct v4l2_subdev_internal_ops msm_cpp_internal_ops = {
};
static int msm_cpp_buffer_ops(struct cpp_device *cpp_dev,
- uint32_t buff_mgr_ops, struct msm_buf_mngr_info *buff_mgr_info)
+ uint32_t buff_mgr_ops, uint32_t ids,
+ void *arg)
{
int rc = -EINVAL;
- rc = v4l2_subdev_call(cpp_dev->buf_mgr_subdev, core, ioctl,
- buff_mgr_ops, buff_mgr_info);
+ switch (buff_mgr_ops) {
+ case VIDIOC_MSM_BUF_MNGR_IOCTL_CMD: {
+ rc = msm_cpp_buffer_private_ops(cpp_dev, buff_mgr_ops,
+ ids, arg);
+ break;
+ }
+ case VIDIOC_MSM_BUF_MNGR_PUT_BUF:
+ case VIDIOC_MSM_BUF_MNGR_BUF_DONE:
+ case VIDIOC_MSM_BUF_MNGR_GET_BUF:
+ default: {
+ struct msm_buf_mngr_info *buff_mgr_info =
+ (struct msm_buf_mngr_info *)arg;
+ rc = cpp_dev->buf_mgr_ops.msm_cam_buf_mgr_ops(buff_mgr_ops,
+ buff_mgr_info);
+ break;
+ }
+ }
if (rc < 0)
pr_debug("%s: line %d rc = %d\n", __func__, __LINE__, rc);
return rc;
@@ -1242,9 +1271,9 @@ static int msm_cpp_notify_frame_done(struct cpp_device *cpp_dev,
SWAP_IDENTITY_FOR_BATCH_ON_PREVIEW(processed_frame,
iden, processed_frame->duplicate_identity);
-
memset(&buff_mgr_info, 0 ,
sizeof(struct msm_buf_mngr_info));
+
buff_mgr_info.session_id = ((iden >> 16) & 0xFFFF);
buff_mgr_info.stream_id = (iden & 0xFFFF);
buff_mgr_info.frame_id = processed_frame->frame_id;
@@ -1262,7 +1291,7 @@ static int msm_cpp_notify_frame_done(struct cpp_device *cpp_dev,
if (put_buf) {
rc = msm_cpp_buffer_ops(cpp_dev,
VIDIOC_MSM_BUF_MNGR_PUT_BUF,
- &buff_mgr_info);
+ 0x0, &buff_mgr_info);
if (rc < 0) {
pr_err("error putting buffer\n");
rc = -EINVAL;
@@ -1270,7 +1299,7 @@ static int msm_cpp_notify_frame_done(struct cpp_device *cpp_dev,
} else {
rc = msm_cpp_buffer_ops(cpp_dev,
VIDIOC_MSM_BUF_MNGR_BUF_DONE,
- &buff_mgr_info);
+ 0x0, &buff_mgr_info);
if (rc < 0) {
pr_err("error putting buffer\n");
rc = -EINVAL;
@@ -1289,6 +1318,7 @@ static int msm_cpp_notify_frame_done(struct cpp_device *cpp_dev,
memset(&buff_mgr_info, 0 ,
sizeof(struct msm_buf_mngr_info));
+
buff_mgr_info.session_id = ((iden >> 16) & 0xFFFF);
buff_mgr_info.stream_id = (iden & 0xFFFF);
buff_mgr_info.frame_id = processed_frame->frame_id;
@@ -1298,7 +1328,7 @@ static int msm_cpp_notify_frame_done(struct cpp_device *cpp_dev,
if (put_buf) {
rc = msm_cpp_buffer_ops(cpp_dev,
VIDIOC_MSM_BUF_MNGR_PUT_BUF,
- &buff_mgr_info);
+ 0x0, &buff_mgr_info);
if (rc < 0) {
pr_err("error putting buffer\n");
rc = -EINVAL;
@@ -1306,7 +1336,7 @@ static int msm_cpp_notify_frame_done(struct cpp_device *cpp_dev,
} else {
rc = msm_cpp_buffer_ops(cpp_dev,
VIDIOC_MSM_BUF_MNGR_BUF_DONE,
- &buff_mgr_info);
+ 0x0, &buff_mgr_info);
if (rc < 0) {
pr_err("error putting buffer\n");
rc = -EINVAL;
@@ -2158,6 +2188,8 @@ static int msm_cpp_cfg_frame(struct cpp_device *cpp_dev,
uint8_t tnr_enabled;
enum msm_camera_buf_mngr_buf_type buf_type =
MSM_CAMERA_BUF_MNGR_BUF_PLANAR;
+ uint32_t ioctl_cmd, idx;
+ uint32_t op_index, dup_index;
stripe_base = cpp_dev->payload_params.stripe_base;
stripe_size = cpp_dev->payload_params.stripe_size;
@@ -2212,8 +2244,12 @@ static int msm_cpp_cfg_frame(struct cpp_device *cpp_dev,
goto frame_msg_err;
}
+ op_index = new_frame->output_buffer_info[0].index;
+ dup_index = new_frame->duplicate_buffer_info.index;
+
if (new_frame->we_disable == 0) {
int32_t iden = new_frame->identity;
+
if ((new_frame->output_buffer_info[0].native_buff == 0) &&
(new_frame->first_payload)) {
memset(&buff_mgr_info, 0,
@@ -2226,16 +2262,32 @@ static int msm_cpp_cfg_frame(struct cpp_device *cpp_dev,
SWAP_IDENTITY_FOR_BATCH_ON_PREVIEW(new_frame,
iden, new_frame->duplicate_identity);
+ /*
+ * Swap the input buffer index for batch mode with
+ * buffer on preview
+ */
+ SWAP_BUF_INDEX_FOR_BATCH_ON_PREVIEW(new_frame,
+ buff_mgr_info, op_index, dup_index);
+
buff_mgr_info.session_id = ((iden >> 16) & 0xFFFF);
buff_mgr_info.stream_id = (iden & 0xFFFF);
buff_mgr_info.type = buf_type;
+
+ if (IS_DEFAULT_OUTPUT_BUF_INDEX(buff_mgr_info.index)) {
+ ioctl_cmd = VIDIOC_MSM_BUF_MNGR_GET_BUF;
+ idx = 0x0;
+ } else {
+ ioctl_cmd = VIDIOC_MSM_BUF_MNGR_IOCTL_CMD;
+ idx =
+ MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX;
+ }
rc = msm_cpp_buffer_ops(cpp_dev,
- VIDIOC_MSM_BUF_MNGR_GET_BUF,
- &buff_mgr_info);
+ ioctl_cmd, idx, &buff_mgr_info);
if (rc < 0) {
rc = -EAGAIN;
- pr_debug("%s: error getting buffer rc:%d\n",
- __func__, rc);
+ pr_debug("%s:get_buf err rc:%d, index %d\n",
+ __func__, rc,
+ new_frame->output_buffer_info[0].index);
goto frame_msg_err;
}
num_output_bufs =
@@ -2273,16 +2325,32 @@ static int msm_cpp_cfg_frame(struct cpp_device *cpp_dev,
iden, new_frame->identity);
memset(&dup_buff_mgr_info, 0, sizeof(struct msm_buf_mngr_info));
+
+ /*
+ * Swap the input buffer index for batch mode with
+ * buffer on preview
+ */
+ SWAP_BUF_INDEX_FOR_BATCH_ON_PREVIEW(new_frame,
+ dup_buff_mgr_info, dup_index, op_index);
+
dup_buff_mgr_info.session_id = ((iden >> 16) & 0xFFFF);
dup_buff_mgr_info.stream_id = (iden & 0xFFFF);
dup_buff_mgr_info.type =
MSM_CAMERA_BUF_MNGR_BUF_PLANAR;
- rc = msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_GET_BUF,
+ if (IS_DEFAULT_OUTPUT_BUF_INDEX(dup_buff_mgr_info.index)) {
+ ioctl_cmd = VIDIOC_MSM_BUF_MNGR_GET_BUF;
+ idx = 0x0;
+ } else {
+ ioctl_cmd = VIDIOC_MSM_BUF_MNGR_IOCTL_CMD;
+ idx = MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX;
+ }
+ rc = msm_cpp_buffer_ops(cpp_dev, ioctl_cmd, idx,
&dup_buff_mgr_info);
if (rc < 0) {
rc = -EAGAIN;
- pr_debug("%s: error getting buffer rc:%d\n",
- __func__, rc);
+ pr_debug("%s: get_buf err rc:%d, index %d\n",
+ __func__, rc,
+ new_frame->duplicate_buffer_info.index);
goto phyaddr_err;
}
new_frame->duplicate_buffer_info.index =
@@ -2296,7 +2364,7 @@ static int msm_cpp_cfg_frame(struct cpp_device *cpp_dev,
pr_err("error gettting output physical address\n");
rc = -EINVAL;
msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_PUT_BUF,
- &dup_buff_mgr_info);
+ 0x0, &dup_buff_mgr_info);
goto phyaddr_err;
}
/* set duplicate enable bit */
@@ -2374,7 +2442,7 @@ qcmd_err:
phyaddr_err:
if (new_frame->output_buffer_info[0].native_buff == 0)
msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_PUT_BUF,
- &buff_mgr_info);
+ 0x0, &buff_mgr_info);
frame_msg_err:
kfree(cpp_frame_msg);
kfree(new_frame);
@@ -2985,11 +3053,11 @@ STREAM_BUFF_END:
if (queue_buf_info.is_buf_dirty) {
rc = msm_cpp_buffer_ops(cpp_dev,
VIDIOC_MSM_BUF_MNGR_PUT_BUF,
- &queue_buf_info.buff_mgr_info);
+ 0x0, &queue_buf_info.buff_mgr_info);
} else {
rc = msm_cpp_buffer_ops(cpp_dev,
VIDIOC_MSM_BUF_MNGR_BUF_DONE,
- &queue_buf_info.buff_mgr_info);
+ 0x0, &queue_buf_info.buff_mgr_info);
}
if (rc < 0) {
pr_err("error in buf done\n");
@@ -3001,6 +3069,7 @@ STREAM_BUFF_END:
case VIDIOC_MSM_CPP_POP_STREAM_BUFFER: {
struct msm_buf_mngr_info buff_mgr_info;
struct msm_cpp_frame_info_t frame_info;
+ uint32_t ioctl_cmd, idx;
if (ioctl_ptr->ioctl_ptr == NULL ||
(ioctl_ptr->len !=
sizeof(struct msm_cpp_frame_info_t))) {
@@ -3020,16 +3089,25 @@ STREAM_BUFF_END:
buff_mgr_info.stream_id = (frame_info.identity & 0xFFFF);
buff_mgr_info.type =
MSM_CAMERA_BUF_MNGR_BUF_PLANAR;
- rc = msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_GET_BUF,
+ if (IS_DEFAULT_OUTPUT_BUF_INDEX(
+ frame_info.output_buffer_info[0].index)) {
+ ioctl_cmd = VIDIOC_MSM_BUF_MNGR_GET_BUF;
+ idx = 0x0;
+ } else {
+ ioctl_cmd = VIDIOC_MSM_BUF_MNGR_IOCTL_CMD;
+ idx = MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX;
+ }
+ rc = msm_cpp_buffer_ops(cpp_dev, ioctl_cmd, idx,
&buff_mgr_info);
if (rc < 0) {
rc = -EAGAIN;
- pr_err_ratelimited("error getting buffer rc:%d\n", rc);
+ pr_err_ratelimited("POP: get_buf err rc:%d, index %d\n",
+ rc, frame_info.output_buffer_info[0].index);
break;
}
buff_mgr_info.frame_id = frame_info.frame_id;
rc = msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_BUF_DONE,
- &buff_mgr_info);
+ 0x0, &buff_mgr_info);
if (rc < 0) {
pr_err("error in buf done\n");
rc = -EAGAIN;
@@ -3813,6 +3891,43 @@ static void msm_cpp_set_vbif_reg_values(struct cpp_device *cpp_dev)
}
}
+static int msm_cpp_buffer_private_ops(struct cpp_device *cpp_dev,
+ uint32_t buff_mgr_ops, uint32_t id, void *arg) {
+
+ int32_t rc = 0;
+
+ switch (id) {
+ case MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX: {
+ struct msm_camera_private_ioctl_arg ioctl_arg;
+ struct msm_buf_mngr_info *buff_mgr_info =
+ (struct msm_buf_mngr_info *)arg;
+
+ ioctl_arg.id = MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX;
+ ioctl_arg.size = sizeof(struct msm_buf_mngr_info);
+ ioctl_arg.result = 0;
+ ioctl_arg.reserved = 0x0;
+ ioctl_arg.ioctl_ptr = 0x0;
+ MSM_CAM_GET_IOCTL_ARG_PTR(&ioctl_arg.ioctl_ptr, &buff_mgr_info,
+ sizeof(void *));
+ rc = cpp_dev->buf_mgr_ops.msm_cam_buf_mgr_ops(buff_mgr_ops,
+ &ioctl_arg);
+ /* Use VIDIOC_MSM_BUF_MNGR_GET_BUF if getbuf with indx fails */
+ if (rc < 0) {
+ pr_err_ratelimited("get_buf_by_idx for %d err %d,use get_buf\n",
+ buff_mgr_info->index, rc);
+ rc = cpp_dev->buf_mgr_ops.msm_cam_buf_mgr_ops(
+ VIDIOC_MSM_BUF_MNGR_GET_BUF, buff_mgr_info);
+ }
+ break;
+ }
+ default: {
+ pr_err("unsupported buffer manager ioctl\n");
+ break;
+ }
+ }
+ return rc;
+}
+
static int cpp_probe(struct platform_device *pdev)
{
struct cpp_device *cpp_dev;
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
index 753c6ffd810a..1784e27b1e37 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
@@ -19,6 +19,7 @@
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <media/v4l2-subdev.h>
+#include "msm_generic_buf_mgr.h"
#include "msm_sd.h"
#include "cam_soc_api.h"
#include "cam_hw_ops.h"
@@ -254,7 +255,7 @@ struct cpp_device {
struct msm_cpp_buff_queue_info_t *buff_queue;
uint32_t num_buffq;
- struct v4l2_subdev *buf_mgr_subdev;
+ struct msm_cam_buf_mgr_req_ops buf_mgr_ops;
uint32_t bus_client;
uint32_t bus_idx;
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/qcom/qdsp6v2/q6audio_v2.c b/drivers/misc/qcom/qdsp6v2/q6audio_v2.c
index 7eb6629b1e57..51ba23da1270 100644
--- a/drivers/misc/qcom/qdsp6v2/q6audio_v2.c
+++ b/drivers/misc/qcom/qdsp6v2/q6audio_v2.c
@@ -85,6 +85,13 @@ void audio_in_get_dsp_frames(void *priv,
pr_debug("%s:session id %d: enc_framesotal_size=0x%8x\n", __func__,
audio->ac->session, payload[4]);
+ /* Ensure the index is within max array size: FRAME_NUM */
+ if (index >= FRAME_NUM) {
+ pr_err("%s: Invalid index %d\n",
+ __func__, index);
+ return;
+ }
+
audio->out_frame_info[index][0] = payload[9];
audio->out_frame_info[index][1] = payload[5];
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..11daafe43d7d 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
@@ -1061,8 +1061,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",
@@ -1227,7 +1227,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 =
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_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/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..fe00dadc3f38
--- /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, j;
+ 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 (j = 0; i < items_to_log; ++i, ++j) {
+ cnt = print_to_log(log, "%2.2X ", data[j]);
+ 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 (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 7810ecb9b15b..541e40aeb91a 100644
--- a/drivers/power/qcom-charger/qpnp-smb2.c
+++ b/drivers/power/qcom-charger/qpnp-smb2.c
@@ -25,54 +25,105 @@
#include "smb-lib.h"
#include "pmic-voter.h"
-#define SMB2_DEFAULT_FCC_UA 3000000
-#define SMB2_DEFAULT_FV_UV 4350000
-#define SMB2_DEFAULT_ICL_UA 3000000
+#define SMB2_DEFAULT_FCC_UA 3000000
+#define SMB2_DEFAULT_FV_UV 4350000
+#define SMB2_DEFAULT_ICL_UA 3000000
+#define SMB2_DEFAULT_WPWR_UW 8000000
static struct smb_params v1_params = {
- .fcc = {
+ .fcc = {
.name = "fast charge current",
.reg = FAST_CHARGE_CURRENT_CFG_REG,
.min_u = 0,
.max_u = 4500000,
.step_u = 25000,
},
- .fv = {
+ .fv = {
.name = "float voltage",
.reg = FLOAT_VOLTAGE_CFG_REG,
- .min_u = 3487500,
- .max_u = 4920000,
- .step_u = 7500,
+ .min_u = 2500000,
+ .max_u = 5000000,
+ .step_u = 10000,
},
- .usb_icl = {
+ .usb_icl = {
.name = "usb input current limit",
.reg = USBIN_CURRENT_LIMIT_CFG_REG,
.min_u = 0,
- .max_u = 4800000,
+ .max_u = 6000000,
.step_u = 25000,
},
- .icl_stat = {
+ .icl_stat = {
.name = "input current limit status",
.reg = ICL_STATUS_REG,
.min_u = 0,
.max_u = 4800000,
.step_u = 25000,
},
- .dc_icl = {
+ .dc_icl = {
.name = "dc input current limit",
.reg = DCIN_CURRENT_LIMIT_CFG_REG,
.min_u = 0,
+ .max_u = 6000000,
+ .step_u = 25000,
+ },
+ .dc_icl_pt_lv = {
+ .name = "dc icl PT <8V",
+ .reg = ZIN_ICL_PT_REG,
+ .min_u = 0,
+ .max_u = 3000000,
+ .step_u = 25000,
+ },
+ .dc_icl_pt_hv = {
+ .name = "dc icl PT >8V",
+ .reg = ZIN_ICL_PT_HV_REG,
+ .min_u = 0,
+ .max_u = 3000000,
+ .step_u = 25000,
+ },
+ .dc_icl_div2_lv = {
+ .name = "dc icl div2 <5.5V",
+ .reg = ZIN_ICL_LV_REG,
+ .min_u = 0,
+ .max_u = 3000000,
+ .step_u = 25000,
+ },
+ .dc_icl_div2_mid_lv = {
+ .name = "dc icl div2 5.5-6.5V",
+ .reg = ZIN_ICL_MID_LV_REG,
+ .min_u = 0,
.max_u = 3000000,
.step_u = 25000,
},
+ .dc_icl_div2_mid_hv = {
+ .name = "dc icl div2 6.5-8.0V",
+ .reg = ZIN_ICL_MID_HV_REG,
+ .min_u = 0,
+ .max_u = 3000000,
+ .step_u = 25000,
+ },
+ .dc_icl_div2_hv = {
+ .name = "dc icl div2 >8.0V",
+ .reg = ZIN_ICL_HV_REG,
+ .min_u = 0,
+ .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 {
- bool suspend_input;
- int fcc_ua;
- int usb_icl_ua;
- int dc_icl_ua;
- int fv_uv;
+ bool suspend_input;
+ int fcc_ua;
+ int usb_icl_ua;
+ int dc_icl_ua;
+ int fv_uv;
+ int wipower_max_uw;
};
struct smb2 {
@@ -125,6 +176,11 @@ static int smb2_parse_dt(struct smb2 *chip)
if (rc < 0)
chip->dt.dc_icl_ua = SMB2_DEFAULT_ICL_UA;
+ rc = of_property_read_u32(node,
+ "qcom,wipower-max-uw", &chip->dt.wipower_max_uw);
+ if (rc < 0)
+ chip->dt.wipower_max_uw = SMB2_DEFAULT_WPWR_UW;
+
return 0;
}
@@ -486,6 +542,57 @@ static int smb2_init_vconn_regulator(struct smb2 *chip)
/***************************
* HARDWARE INITIALIZATION *
***************************/
+static int smb2_config_wipower_input_power(struct smb2 *chip, int uw)
+{
+ int rc;
+ int ua;
+ struct smb_charger *chg = &chip->chg;
+ s64 nw = (s64)uw * 1000;
+
+ ua = div_s64(nw, ZIN_ICL_PT_MAX_MV);
+ rc = smblib_set_charge_param(chg, &chg->param.dc_icl_pt_lv, ua);
+ if (rc < 0) {
+ pr_err("Couldn't configure dc_icl_pt_lv rc = %d\n", rc);
+ return rc;
+ }
+
+ ua = div_s64(nw, ZIN_ICL_PT_HV_MAX_MV);
+ rc = smblib_set_charge_param(chg, &chg->param.dc_icl_pt_hv, ua);
+ if (rc < 0) {
+ pr_err("Couldn't configure dc_icl_pt_hv rc = %d\n", rc);
+ return rc;
+ }
+
+ ua = div_s64(nw, ZIN_ICL_LV_MAX_MV);
+ rc = smblib_set_charge_param(chg, &chg->param.dc_icl_div2_lv, ua);
+ if (rc < 0) {
+ pr_err("Couldn't configure dc_icl_div2_lv rc = %d\n", rc);
+ return rc;
+ }
+
+ ua = div_s64(nw, ZIN_ICL_MID_LV_MAX_MV);
+ rc = smblib_set_charge_param(chg, &chg->param.dc_icl_div2_mid_lv, ua);
+ if (rc < 0) {
+ pr_err("Couldn't configure dc_icl_div2_mid_lv rc = %d\n", rc);
+ return rc;
+ }
+
+ ua = div_s64(nw, ZIN_ICL_MID_HV_MAX_MV);
+ rc = smblib_set_charge_param(chg, &chg->param.dc_icl_div2_mid_hv, ua);
+ if (rc < 0) {
+ pr_err("Couldn't configure dc_icl_div2_mid_hv rc = %d\n", rc);
+ return rc;
+ }
+
+ ua = div_s64(nw, ZIN_ICL_HV_MAX_MV);
+ rc = smblib_set_charge_param(chg, &chg->param.dc_icl_div2_hv, ua);
+ if (rc < 0) {
+ pr_err("Couldn't configure dc_icl_div2_hv rc = %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
static int smb2_init_hw(struct smb2 *chip)
{
@@ -582,6 +689,13 @@ static int smb2_init_hw(struct smb2 *chip)
return rc;
}
+ /* configure wipower watts */
+ rc = smb2_config_wipower_input_power(chip, chip->dt.wipower_max_uw);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't configure wipower rc=%d\n", rc);
+ return rc;
+ }
+
return rc;
}
@@ -615,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 8b3d00b6a5c1..2e35e1e3b174 100644
--- a/drivers/power/qcom-charger/smb-lib.h
+++ b/drivers/power/qcom-charger/smb-lib.h
@@ -62,6 +62,13 @@ struct smb_params {
struct smb_chg_param usb_icl;
struct smb_chg_param icl_stat;
struct smb_chg_param dc_icl;
+ struct smb_chg_param dc_icl_pt_lv;
+ struct smb_chg_param dc_icl_pt_hv;
+ struct smb_chg_param dc_icl_div2_lv;
+ 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 {
@@ -144,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/power/qcom-charger/smb-reg.h b/drivers/power/qcom-charger/smb-reg.h
index f63e983c595c..5af01c229f01 100644
--- a/drivers/power/qcom-charger/smb-reg.h
+++ b/drivers/power/qcom-charger/smb-reg.h
@@ -719,6 +719,15 @@ enum {
#define ZIN_ICL_MID_HV_REG (DCIN_BASE + 0x98)
#define ZIN_ICL_MID_HV_MASK GENMASK(7, 0)
+enum {
+ ZIN_ICL_PT_MAX_MV = 8000,
+ ZIN_ICL_PT_HV_MAX_MV = 9000,
+ ZIN_ICL_LV_MAX_MV = 5500,
+ ZIN_ICL_MID_LV_MAX_MV = 6500,
+ ZIN_ICL_MID_HV_MAX_MV = 8000,
+ ZIN_ICL_HV_MAX_MV = 11000,
+};
+
/* MISC Peripheral Registers */
#define REVISION1_REG (MISC_BASE + 0x00)
#define DIG_MINOR_MASK GENMASK(7, 0)
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 9135415a5a51..3e167f4c0f42 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -4067,8 +4067,11 @@ int ufshcd_uic_hibern8_enter(struct ufs_hba *hba)
for (retries = UIC_HIBERN8_ENTER_RETRIES; retries > 0; retries--) {
ret = __ufshcd_uic_hibern8_enter(hba);
- if (!ret || ret == -ENOLINK)
+ if (!ret)
goto out;
+ /* Unable to recover the link, so no point proceeding */
+ if (ret == -ENOLINK)
+ BUG();
}
out:
return ret;
@@ -4089,7 +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))
+ BUG();
} else {
dev_dbg(hba->dev, "%s: Hibern8 Exit at %lld us", __func__,
ktime_to_us(ktime_get()));
@@ -6316,6 +6321,12 @@ static int ufshcd_reset_and_restore(struct ufs_hba *hba)
} while (err && --retries);
/*
+ * There is no point proceeding even after failing
+ * to recover after multiple retries.
+ */
+ if (err)
+ BUG();
+ /*
* After reset the door-bell might be cleared, complete
* outstanding requests in s/w here.
*/
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 bb3f092f6de9..adbf2dc7a166 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -13,7 +13,7 @@ obj-$(CONFIG_MSM_GLINK_SMD_XPRT) += glink_smd_xprt.o
obj-$(CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT)+= glink_smem_native_xprt.o
obj-$(CONFIG_MSM_SMEM_LOGGING) += smem_log.o
obj-$(CONFIG_MSM_SYSMON_GLINK_COMM) += sysmon-glink.o sysmon-qmi.o
-obj-$(CONFIG_ARCH_QCOM) += kryo-l2-accessors.o
+obj-$(CONFIG_ARCH_MSM8996) += kryo-l2-accessors.o
obj-$(CONFIG_MSM_SMP2P) += smp2p.o smp2p_debug.o smp2p_sleepstate.o
obj-$(CONFIG_MSM_SMP2P_TEST) += smp2p_loopback.o smp2p_test.o smp2p_spinlock_test.o
obj-$(CONFIG_MSM_QMI_INTERFACE) += qmi_interface.o
@@ -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_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..58b847a4a5f4 100644
--- a/drivers/usb/gadget/function/f_gsi.c
+++ b/drivers/usb/gadget/function/f_gsi.c
@@ -256,7 +256,7 @@ 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 =
+ in_params->xfer_scratch.last_trb_addr_iova =
d_port->in_last_trb_addr = gsi_channel_info.last_trb_addr;
in_params->xfer_scratch.const_buffer_size =
gsi_channel_info.const_buffer_size;
@@ -290,7 +290,7 @@ static int ipa_connect_channels(struct gsi_data_port *d_port)
gsi_channel_info.xfer_ring_len;
out_params->xfer_ring_base_addr =
gsi_channel_info.xfer_ring_base_addr;
- out_params->xfer_scratch.last_trb_addr =
+ 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 +2787,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 +2821,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/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index c2d65206ec6c..6c47c26b5df7 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -299,6 +299,7 @@ static void xhci_pci_remove(struct pci_dev *dev)
struct xhci_hcd *xhci;
xhci = hcd_to_xhci(pci_get_drvdata(dev));
+ xhci->xhc_state |= XHCI_STATE_REMOVING;
if (xhci->shared_hcd) {
usb_remove_hcd(xhci->shared_hcd);
usb_put_hcd(xhci->shared_hcd);
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 8125a8f96311..3e49861a09a2 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -284,6 +284,7 @@ static int xhci_plat_remove(struct platform_device *dev)
pm_runtime_disable(&dev->dev);
device_remove_file(&dev->dev, &dev_attr_config_imod);
+ xhci->xhc_state |= XHCI_STATE_REMOVING;
usb_remove_hcd(xhci->shared_hcd);
usb_phy_shutdown(hcd->usb_phy);
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index db0f0831b94f..2b63969c2bbf 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -4008,7 +4008,8 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd,
int reserved_trbs = xhci->cmd_ring_reserved_trbs;
int ret;
- if (xhci->xhc_state) {
+ if ((xhci->xhc_state & XHCI_STATE_DYING) ||
+ (xhci->xhc_state & XHCI_STATE_HALTED)) {
xhci_dbg(xhci, "xHCI dying or halted, can't queue_command\n");
return -ESHUTDOWN;
}
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 42f74d55e3bd..dd7669331d00 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -155,7 +155,8 @@ static int xhci_start(struct xhci_hcd *xhci)
"waited %u microseconds.\n",
XHCI_MAX_HALT_USEC);
if (!ret)
- xhci->xhc_state &= ~(XHCI_STATE_HALTED | XHCI_STATE_DYING);
+ /* clear state flags. Including dying, halted or removing */
+ xhci->xhc_state = 0;
return ret;
}
@@ -2762,7 +2763,8 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
if (ret <= 0)
return ret;
xhci = hcd_to_xhci(hcd);
- if (xhci->xhc_state & XHCI_STATE_DYING)
+ if ((xhci->xhc_state & XHCI_STATE_DYING) ||
+ (xhci->xhc_state & XHCI_STATE_REMOVING))
return -ENODEV;
xhci_dbg(xhci, "%s called for udev %p\n", __func__, udev);
@@ -3809,7 +3811,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
mutex_lock(&xhci->mutex);
- if (xhci->xhc_state) /* dying or halted */
+ if (xhci->xhc_state) /* dying, removing or halted */
goto out;
if (!udev->slot_id) {
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index a6e94e029a10..dc03aac4f88a 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1604,6 +1604,7 @@ struct xhci_hcd {
*/
#define XHCI_STATE_DYING (1 << 0)
#define XHCI_STATE_HALTED (1 << 1)
+#define XHCI_STATE_REMOVING (1 << 2)
/* Statistics */
int error_bitmask;
unsigned int quirks;
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index b7b978b8d306..f29c275c490b 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -16,12 +16,13 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/platform_device.h>
+#include <linux/of_platform.h>
#include <linux/power_supply.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/extcon.h>
+#include <linux/usb/usbpd.h>
#include "usbpd.h"
enum usbpd_state {
@@ -119,10 +120,13 @@ enum usbpd_data_msg_type {
MSG_VDM = 0xF,
};
-enum plug_orientation {
- ORIENTATION_NONE,
- ORIENTATION_CC1,
- ORIENTATION_CC2,
+enum vdm_state {
+ VDM_NONE,
+ DISCOVERED_ID,
+ DISCOVERED_SVIDS,
+ DISCOVERED_MODES,
+ MODE_ENTERED,
+ MODE_EXITED,
};
static void *usbpd_ipc_log;
@@ -162,6 +166,7 @@ static void *usbpd_ipc_log;
#define PS_HARD_RESET_TIME 35
#define PS_SOURCE_ON 400
#define PS_SOURCE_OFF 900
+#define VDM_BUSY_TIME 50
#define PD_CAPS_COUNT 50
@@ -205,6 +210,33 @@ static void *usbpd_ipc_log;
#define PD_SRC_PDO_VAR_BATT_MIN_VOLT(pdo) (((pdo) >> 10) & 0x3FF)
#define PD_SRC_PDO_VAR_BATT_MAX(pdo) ((pdo) & 0x3FF)
+/* Vendor Defined Messages */
+#define MAX_CRC_RECEIVE_TIME 9 /* ~(2 * tReceive_max(1.1ms) * # retry 4) */
+#define MAX_VDM_RESPONSE_TIME 60 /* 2 * tVDMSenderResponse_max(30ms) */
+#define MAX_VDM_BUSY_TIME 100 /* 2 * tVDMBusy (50ms) */
+
+/* VDM header is the first 32-bit object following the 16-bit PD header */
+#define VDM_HDR_SVID(hdr) ((hdr) >> 16)
+#define VDM_HDR_TYPE(hdr) ((hdr) & 0x8000)
+#define VDM_HDR_CMD_TYPE(hdr) (((hdr) >> 6) & 0x3)
+#define VDM_HDR_CMD(hdr) ((hdr) & 0x1f)
+
+#define SVDM_HDR(svid, ver, obj, cmd_type, cmd) \
+ (((svid) << 16) | (1 << 15) | ((ver) << 13) \
+ | ((obj) << 8) | ((cmd_type) << 6) | (cmd))
+
+/* discover id response vdo bit fields */
+#define ID_HDR_USB_HOST BIT(31)
+#define ID_HDR_USB_DEVICE BIT(30)
+#define ID_HDR_MODAL_OPR BIT(26)
+#define ID_HDR_PRODUCT_TYPE(n) ((n) >> 27)
+#define ID_HDR_PRODUCT_PER_MASK (2 << 27)
+#define ID_HDR_PRODUCT_HUB 1
+#define ID_HDR_PRODUCT_PER 2
+#define ID_HDR_PRODUCT_AMA 5
+#define ID_HDR_VID 0x05c6 /* qcom */
+#define PROD_VDO_PID 0x0a00 /* TBD */
+
static int min_sink_current = 900;
module_param(min_sink_current, int, S_IRUSR | S_IWUSR);
@@ -217,6 +249,12 @@ static const u32 default_snk_caps[] = { 0x2601905A, /* 5V @ 900mA */
0x0002D096, /* 9V @ 1.5A */
0x0003C064 }; /* 12V @ 1A */
+struct vdm_tx {
+ u32 data[7];
+ int size;
+ struct list_head entry;
+};
+
struct usbpd {
struct device dev;
struct workqueue_struct *wq;
@@ -265,6 +303,12 @@ struct usbpd {
int caps_count;
int hard_reset_count;
+ enum vdm_state vdm_state;
+ u16 *discovered_svids;
+ struct vdm_tx *vdm_tx_retry;
+ struct list_head vdm_tx_queue;
+ struct list_head svid_handlers;
+
struct list_head instance;
};
@@ -280,7 +324,7 @@ static const unsigned int usbpd_extcon_cable[] = {
/* EXTCON_USB and EXTCON_USB_HOST are mutually exclusive */
static const u32 usbpd_extcon_exclusive[] = {0x3, 0};
-static enum plug_orientation usbpd_get_plug_orientation(struct usbpd *pd)
+enum plug_orientation usbpd_get_plug_orientation(struct usbpd *pd)
{
int ret;
union power_supply_propval val;
@@ -292,6 +336,7 @@ static enum plug_orientation usbpd_get_plug_orientation(struct usbpd *pd)
return val.intval;
}
+EXPORT_SYMBOL(usbpd_get_plug_orientation);
static bool is_cable_flipped(struct usbpd *pd)
{
@@ -329,6 +374,17 @@ static int set_power_role(struct usbpd *pd, enum power_role pr)
POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val);
}
+static struct usbpd_svid_handler *find_svid_handler(struct usbpd *pd, u16 svid)
+{
+ struct usbpd_svid_handler *handler;
+
+ list_for_each_entry(handler, &pd->svid_handlers, entry)
+ if (svid == handler->svid)
+ return handler;
+
+ return NULL;
+}
+
static int pd_send_msg(struct usbpd *pd, u8 hdr_type, const u32 *data,
size_t num_data, enum pd_msg_type type)
{
@@ -604,9 +660,17 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
break;
case PE_SRC_READY:
- if (pd->current_dr == DR_DFP)
- extcon_set_cable_state_(pd->extcon, EXTCON_USB_HOST, 1);
pd->in_explicit_contract = true;
+ if (pd->current_dr == DR_DFP) {
+ if (pd->vdm_state == VDM_NONE)
+ usbpd_send_svdm(pd, USBPD_SID,
+ USBPD_SVDM_DISCOVER_IDENTITY,
+ SVDM_CMD_TYPE_INITIATOR, 0,
+ NULL, 0);
+
+ extcon_set_cable_state_(pd->extcon, EXTCON_USB_HOST, 1);
+ }
+
kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE);
break;
@@ -669,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;
@@ -799,6 +864,315 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
}
}
+int usbpd_register_svid(struct usbpd *pd, struct usbpd_svid_handler *hdlr)
+{
+ if (find_svid_handler(pd, hdlr->svid)) {
+ usbpd_err(&pd->dev, "SVID 0x%04x already registered\n",
+ hdlr->svid);
+ return -EINVAL;
+ }
+
+ usbpd_dbg(&pd->dev, "registered handler for SVID 0x%04x\n", hdlr->svid);
+
+ list_add_tail(&hdlr->entry, &pd->svid_handlers);
+
+ /* already connected with this SVID discovered? */
+ if (pd->vdm_state >= DISCOVERED_SVIDS) {
+ u16 *psvid;
+
+ for (psvid = pd->discovered_svids; *psvid; psvid++) {
+ if (*psvid == hdlr->svid) {
+ if (hdlr->connect)
+ hdlr->connect(hdlr);
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(usbpd_register_svid);
+
+void usbpd_unregister_svid(struct usbpd *pd, struct usbpd_svid_handler *hdlr)
+{
+ list_del_init(&hdlr->entry);
+}
+EXPORT_SYMBOL(usbpd_unregister_svid);
+
+int usbpd_send_vdm(struct usbpd *pd, u32 vdm_hdr, const u32 *vdos, int num_vdos)
+{
+ struct vdm_tx *vdm_tx;
+
+ if (!pd->in_explicit_contract)
+ return -EBUSY;
+
+ vdm_tx = kzalloc(sizeof(*vdm_tx), GFP_KERNEL);
+ if (!vdm_tx)
+ return -ENOMEM;
+
+ vdm_tx->data[0] = vdm_hdr;
+ memcpy(&vdm_tx->data[1], vdos, num_vdos * sizeof(u32));
+ vdm_tx->size = num_vdos + 1; /* include the header */
+
+ /* VDM will get sent in PE_SRC/SNK_READY state handling */
+ list_add_tail(&vdm_tx->entry, &pd->vdm_tx_queue);
+ queue_delayed_work(pd->wq, &pd->sm_work, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL(usbpd_send_vdm);
+
+int usbpd_send_svdm(struct usbpd *pd, u16 svid, u8 cmd,
+ enum usbpd_svdm_cmd_type cmd_type, int obj_pos,
+ const u32 *vdos, int num_vdos)
+{
+ u32 svdm_hdr = SVDM_HDR(svid, 0, obj_pos, cmd_type, cmd);
+
+ usbpd_dbg(&pd->dev, "VDM tx: svid:%x cmd:%x cmd_type:%x svdm_hdr:%x\n",
+ svid, cmd, cmd_type, svdm_hdr);
+
+ return usbpd_send_vdm(pd, svdm_hdr, vdos, num_vdos);
+}
+EXPORT_SYMBOL(usbpd_send_svdm);
+
+static void handle_vdm_rx(struct usbpd *pd)
+{
+ u32 vdm_hdr = pd->rx_payload[0];
+ u32 *vdos = &pd->rx_payload[1];
+ u16 svid = VDM_HDR_SVID(vdm_hdr);
+ u16 *psvid;
+ u8 i, num_vdos = pd->rx_msg_len - 1; /* num objects minus header */
+ u8 cmd = VDM_HDR_CMD(vdm_hdr);
+ u8 cmd_type = VDM_HDR_CMD_TYPE(vdm_hdr);
+ struct usbpd_svid_handler *handler;
+
+ usbpd_dbg(&pd->dev, "VDM rx: svid:%x cmd:%x cmd_type:%x vdm_hdr:%x\n",
+ svid, cmd, cmd_type, vdm_hdr);
+
+ /* if it's a supported SVID, pass the message to the handler */
+ handler = find_svid_handler(pd, svid);
+
+ /* Unstructured VDM */
+ if (!VDM_HDR_TYPE(vdm_hdr)) {
+ if (handler && handler->vdm_received)
+ handler->vdm_received(handler, vdm_hdr, vdos, num_vdos);
+ return;
+ }
+
+ if (handler && handler->svdm_received)
+ handler->svdm_received(handler, cmd, cmd_type, vdos, num_vdos);
+
+ switch (cmd_type) {
+ case SVDM_CMD_TYPE_INITIATOR:
+ if (cmd == USBPD_SVDM_DISCOVER_IDENTITY) {
+ u32 tx_vdos[3] = {
+ ID_HDR_USB_HOST | ID_HDR_USB_DEVICE |
+ ID_HDR_PRODUCT_PER_MASK | ID_HDR_VID,
+ 0x0, /* TBD: Cert Stat VDO */
+ (PROD_VDO_PID << 16),
+ /* TBD: Get these from gadget */
+ };
+
+ usbpd_send_svdm(pd, USBPD_SID, cmd,
+ SVDM_CMD_TYPE_RESP_ACK, 0, tx_vdos, 3);
+ } else {
+ usbpd_send_svdm(pd, USBPD_SID, cmd,
+ SVDM_CMD_TYPE_RESP_NAK, 0, NULL, 0);
+ }
+ break;
+
+ case SVDM_CMD_TYPE_RESP_ACK:
+ switch (cmd) {
+ case USBPD_SVDM_DISCOVER_IDENTITY:
+ if (svid != USBPD_SID) {
+ usbpd_err(&pd->dev, "invalid VID:0x%x\n", svid);
+ break;
+ }
+
+ kfree(pd->vdm_tx_retry);
+ pd->vdm_tx_retry = NULL;
+
+ pd->vdm_state = DISCOVERED_ID;
+ usbpd_send_svdm(pd, USBPD_SID,
+ USBPD_SVDM_DISCOVER_SVIDS,
+ SVDM_CMD_TYPE_INITIATOR, 0, NULL, 0);
+ break;
+
+ case USBPD_SVDM_DISCOVER_SVIDS:
+ if (svid != USBPD_SID) {
+ usbpd_err(&pd->dev, "invalid VID:0x%x\n", svid);
+ break;
+ }
+
+ pd->vdm_state = DISCOVERED_SVIDS;
+
+ kfree(pd->vdm_tx_retry);
+ pd->vdm_tx_retry = NULL;
+
+ kfree(pd->discovered_svids);
+
+ /* TODO: handle > 12 SVIDs */
+ pd->discovered_svids = kzalloc((2 * num_vdos + 1) *
+ sizeof(u16),
+ GFP_KERNEL);
+ if (!pd->discovered_svids) {
+ usbpd_err(&pd->dev, "unable to allocate SVIDs\n");
+ break;
+ }
+
+ /* convert 32-bit VDOs to list of 16-bit SVIDs */
+ psvid = pd->discovered_svids;
+ for (i = 0; i < num_vdos * 2; i++) {
+ /*
+ * Within each 32-bit VDO,
+ * SVID[i]: upper 16-bits
+ * SVID[i+1]: lower 16-bits
+ * where i is even.
+ */
+ if (!(i & 1))
+ svid = vdos[i >> 1] >> 16;
+ else
+ svid = vdos[i >> 1] & 0xFFFF;
+
+ /*
+ * There are some devices that incorrectly
+ * swap the order of SVIDs within a VDO. So in
+ * case of an odd-number of SVIDs it could end
+ * up with SVID[i] as 0 while SVID[i+1] is
+ * non-zero. Just skip over the zero ones.
+ */
+ if (svid) {
+ usbpd_dbg(&pd->dev, "Discovered SVID: 0x%04x\n",
+ svid);
+ *psvid++ = svid;
+
+ /* if SVID supported notify handler */
+ handler = find_svid_handler(pd, svid);
+ if (handler && handler->connect)
+ handler->connect(handler);
+ }
+ }
+
+ break;
+
+ case USBPD_SVDM_DISCOVER_MODES:
+ usbpd_info(&pd->dev, "SVID:0x%04x VDM Modes discovered\n",
+ svid);
+ pd->vdm_state = DISCOVERED_MODES;
+ break;
+
+ case USBPD_SVDM_ENTER_MODE:
+ usbpd_info(&pd->dev, "SVID:0x%04x VDM Mode entered\n",
+ svid);
+ pd->vdm_state = MODE_ENTERED;
+ kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE);
+ break;
+
+ case USBPD_SVDM_EXIT_MODE:
+ usbpd_info(&pd->dev, "SVID:0x%04x VDM Mode exited\n",
+ svid);
+ pd->vdm_state = MODE_EXITED;
+ kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SVDM_CMD_TYPE_RESP_NAK:
+ usbpd_info(&pd->dev, "VDM NAK received for SVID:0x%04x command:%d\n",
+ svid, cmd);
+ break;
+
+ case SVDM_CMD_TYPE_RESP_BUSY:
+ switch (cmd) {
+ case USBPD_SVDM_DISCOVER_IDENTITY:
+ case USBPD_SVDM_DISCOVER_SVIDS:
+ if (!pd->vdm_tx_retry) {
+ usbpd_err(&pd->dev, "Discover command %d VDM was unexpectedly freed\n",
+ cmd);
+ break;
+ }
+
+ /* wait tVDMBusy, then retry */
+ list_move(&pd->vdm_tx_retry->entry, &pd->vdm_tx_queue);
+ pd->vdm_tx_retry = NULL;
+ queue_delayed_work(pd->wq, &pd->sm_work,
+ msecs_to_jiffies(VDM_BUSY_TIME));
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+}
+
+static void handle_vdm_tx(struct usbpd *pd)
+{
+ int ret;
+
+ /* only send one VDM at a time */
+ if (!list_empty(&pd->vdm_tx_queue)) {
+ struct vdm_tx *vdm_tx = list_first_entry(&pd->vdm_tx_queue,
+ struct vdm_tx, entry);
+ u32 vdm_hdr = vdm_tx->data[0];
+
+ ret = pd_send_msg(pd, MSG_VDM, vdm_tx->data, vdm_tx->size,
+ SOP_MSG);
+ if (ret) {
+ usbpd_err(&pd->dev, "Error sending VDM command %d\n",
+ VDM_HDR_CMD(vdm_tx->data[0]));
+ usbpd_set_state(pd, pd->current_pr == PR_SRC ?
+ PE_SRC_SEND_SOFT_RESET :
+ PE_SNK_SEND_SOFT_RESET);
+
+ /* retry when hitting PE_SRC/SNK_Ready again */
+ return;
+ }
+
+ list_del(&vdm_tx->entry);
+
+ /*
+ * special case: keep initiated Discover ID/SVIDs
+ * around in case we need to re-try when receiving BUSY
+ */
+ if (VDM_HDR_TYPE(vdm_hdr) &&
+ VDM_HDR_CMD_TYPE(vdm_hdr) == SVDM_CMD_TYPE_INITIATOR &&
+ VDM_HDR_CMD(vdm_hdr) <= USBPD_SVDM_DISCOVER_SVIDS) {
+ if (pd->vdm_tx_retry) {
+ usbpd_err(&pd->dev, "Previous Discover VDM command %d not ACKed/NAKed\n",
+ VDM_HDR_CMD(pd->vdm_tx_retry->data[0]));
+ kfree(pd->vdm_tx_retry);
+ }
+ pd->vdm_tx_retry = vdm_tx;
+ } else {
+ kfree(vdm_tx);
+ }
+ }
+}
+
+static void reset_vdm_state(struct usbpd *pd)
+{
+ struct usbpd_svid_handler *handler;
+
+ pd->vdm_state = VDM_NONE;
+ list_for_each_entry(handler, &pd->svid_handlers, entry)
+ if (handler->disconnect)
+ handler->disconnect(handler);
+ kfree(pd->vdm_tx_retry);
+ pd->vdm_tx_retry = NULL;
+ kfree(pd->discovered_svids);
+ pd->discovered_svids = NULL;
+ while (!list_empty(&pd->vdm_tx_queue)) {
+ struct vdm_tx *vdm_tx =
+ list_first_entry(&pd->vdm_tx_queue,
+ struct vdm_tx, entry);
+ list_del(&vdm_tx->entry);
+ kfree(vdm_tx);
+ }
+}
+
static void dr_swap(struct usbpd *pd)
{
if (pd->current_dr == DR_DFP) {
@@ -813,6 +1187,12 @@ static void dr_swap(struct usbpd *pd)
is_cable_flipped(pd));
extcon_set_cable_state_(pd->extcon, EXTCON_USB_HOST, 1);
pd->current_dr = DR_DFP;
+
+ if (pd->vdm_state == VDM_NONE)
+ usbpd_send_svdm(pd, USBPD_SID,
+ USBPD_SVDM_DISCOVER_IDENTITY,
+ SVDM_CMD_TYPE_INITIATOR, 0,
+ NULL, 0);
}
pd_phy_update_roles(pd->current_dr, pd->current_pr);
@@ -873,6 +1253,8 @@ static void usbpd_sm(struct work_struct *w)
pd->current_pr = PR_NONE;
pd->current_dr = DR_NONE;
+ reset_vdm_state(pd);
+
/* Set CC back to DRP toggle */
val.intval = POWER_SUPPLY_TYPEC_PR_DUAL;
power_supply_set_property(pd->usb_psy,
@@ -884,6 +1266,8 @@ static void usbpd_sm(struct work_struct *w)
/* Hard reset? */
if (pd->hard_reset) {
+ reset_vdm_state(pd);
+
if (pd->current_pr == PR_SINK)
usbpd_set_state(pd, PE_SNK_TRANSITION_TO_DEFAULT);
else
@@ -1001,6 +1385,11 @@ static void usbpd_sm(struct work_struct *w)
pd->rdo = pd->rx_payload[0];
usbpd_set_state(pd, PE_SRC_NEGOTIATE_CAPABILITY);
} else if (ctrl_recvd == MSG_DR_SWAP) {
+ if (pd->vdm_state == MODE_ENTERED) {
+ usbpd_set_state(pd, PE_SRC_HARD_RESET);
+ break;
+ }
+
ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG);
if (ret) {
usbpd_err(&pd->dev, "Error sending Accept\n");
@@ -1022,12 +1411,18 @@ static void usbpd_sm(struct work_struct *w)
pd->current_state = PE_PRS_SRC_SNK_TRANSITION_TO_OFF;
queue_delayed_work(pd->wq, &pd->sm_work, 0);
break;
+ } else {
+ if (data_recvd == MSG_VDM)
+ handle_vdm_rx(pd);
+ else
+ handle_vdm_tx(pd);
}
break;
case PE_SRC_HARD_RESET:
pd_send_hard_reset(pd);
pd->in_explicit_contract = false;
+ reset_vdm_state(pd);
msleep(PS_HARD_RESET_TIME);
usbpd_set_state(pd, PE_SRC_TRANSITION_TO_DEFAULT);
@@ -1133,6 +1528,11 @@ static void usbpd_sm(struct work_struct *w)
usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET);
}
} else if (ctrl_recvd == MSG_DR_SWAP) {
+ if (pd->vdm_state == MODE_ENTERED) {
+ usbpd_set_state(pd, PE_SNK_HARD_RESET);
+ break;
+ }
+
ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG);
if (ret) {
usbpd_err(&pd->dev, "Error sending Accept\n");
@@ -1166,6 +1566,11 @@ static void usbpd_sm(struct work_struct *w)
queue_delayed_work(pd->wq, &pd->sm_work,
msecs_to_jiffies(PS_SOURCE_OFF));
break;
+ } else {
+ if (data_recvd == MSG_VDM)
+ handle_vdm_rx(pd);
+ else
+ handle_vdm_tx(pd);
}
break;
@@ -1218,6 +1623,7 @@ static void usbpd_sm(struct work_struct *w)
pd_send_hard_reset(pd);
pd->in_explicit_contract = false;
+ reset_vdm_state(pd);
usbpd_set_state(pd, PE_SNK_TRANSITION_TO_DEFAULT);
break;
@@ -1480,6 +1886,7 @@ static int usbpd_uevent(struct device *dev, struct kobj_uevent_env *env)
add_uevent_var(env, "RDO=%08x", pd->rdo);
add_uevent_var(env, "CONTRACT=%s", pd->in_explicit_contract ?
"explicit" : "implicit");
+ add_uevent_var(env, "ALT_MODE=%d", pd->vdm_state == MODE_ENTERED);
return 0;
}
@@ -1782,6 +2189,61 @@ static struct class usbpd_class = {
.dev_groups = usbpd_groups,
};
+static int match_usbpd_device(struct device *dev, const void *data)
+{
+ return dev->parent == data;
+}
+
+static void devm_usbpd_put(struct device *dev, void *res)
+{
+ struct usbpd **ppd = res;
+
+ put_device(&(*ppd)->dev);
+}
+
+struct usbpd *devm_usbpd_get_by_phandle(struct device *dev, const char *phandle)
+{
+ struct usbpd **ptr, *pd = NULL;
+ struct device_node *pd_np;
+ struct platform_device *pdev;
+ struct device *pd_dev;
+
+ if (!dev->of_node)
+ return ERR_PTR(-ENODEV);
+
+ pd_np = of_parse_phandle(dev->of_node, phandle, 0);
+ if (!pd_np)
+ return ERR_PTR(-ENODEV);
+
+ pdev = of_find_device_by_node(pd_np);
+ if (!pdev)
+ return ERR_PTR(-ENODEV);
+
+ pd_dev = class_find_device(&usbpd_class, NULL, &pdev->dev,
+ match_usbpd_device);
+ if (!pd_dev) {
+ platform_device_put(pdev);
+ return ERR_PTR(-ENODEV);
+ }
+
+ ptr = devres_alloc(devm_usbpd_put, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr) {
+ put_device(pd_dev);
+ platform_device_put(pdev);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ pd = dev_get_drvdata(pd_dev);
+ if (!pd)
+ return ERR_PTR(-ENODEV);
+
+ *ptr = pd;
+ devres_add(dev, ptr);
+
+ return pd;
+}
+EXPORT_SYMBOL(devm_usbpd_get_by_phandle);
+
static int num_pd_instances;
/**
@@ -1869,6 +2331,9 @@ struct usbpd *usbpd_create(struct device *parent)
pd->current_dr = DR_NONE;
list_add_tail(&pd->instance, &_usbpd);
+ INIT_LIST_HEAD(&pd->vdm_tx_queue);
+ INIT_LIST_HEAD(&pd->svid_handlers);
+
/* force read initial power_supply values */
psy_changed(&pd->psy_nb, PSY_EVENT_PROP_CHANGED, pd->usb_psy);
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/linux/usb/usbpd.h b/include/linux/usb/usbpd.h
new file mode 100644
index 000000000000..c2c1025feb8e
--- /dev/null
+++ b/include/linux/usb/usbpd.h
@@ -0,0 +1,156 @@
+/* Copyright (c) 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
+ * 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 __LINUX_USB_USBPD_H
+#define __LINUX_USB_USBPD_H
+
+#include <linux/list.h>
+
+struct usbpd;
+
+/* Standard IDs */
+#define USBPD_SID 0xff00
+
+/* Structured VDM Command Type */
+enum usbpd_svdm_cmd_type {
+ SVDM_CMD_TYPE_INITIATOR,
+ SVDM_CMD_TYPE_RESP_ACK,
+ SVDM_CMD_TYPE_RESP_NAK,
+ SVDM_CMD_TYPE_RESP_BUSY,
+};
+
+/* Structured VDM Commands */
+#define USBPD_SVDM_DISCOVER_IDENTITY 0x1
+#define USBPD_SVDM_DISCOVER_SVIDS 0x2
+#define USBPD_SVDM_DISCOVER_MODES 0x3
+#define USBPD_SVDM_ENTER_MODE 0x4
+#define USBPD_SVDM_EXIT_MODE 0x5
+#define USBPD_SVDM_ATTENTION 0x6
+
+/*
+ * Implemented by client
+ */
+struct usbpd_svid_handler {
+ u16 svid;
+
+ void (*connect)(struct usbpd_svid_handler *hdlr);
+ void (*disconnect)(struct usbpd_svid_handler *hdlr);
+
+ /* Unstructured VDM */
+ void (*vdm_received)(struct usbpd_svid_handler *hdlr, u32 vdm_hdr,
+ const u32 *vdos, int num_vdos);
+
+ /* Structured VDM */
+ void (*svdm_received)(struct usbpd_svid_handler *hdlr, u8 cmd,
+ enum usbpd_svdm_cmd_type cmd_type, const u32 *vdos,
+ int num_vdos);
+
+ struct list_head entry;
+};
+
+enum plug_orientation {
+ ORIENTATION_NONE,
+ ORIENTATION_CC1,
+ ORIENTATION_CC2,
+};
+
+#if IS_ENABLED(CONFIG_USB_PD_POLICY)
+/*
+ * Obtains an instance of usbpd from a DT phandle
+ */
+struct usbpd *devm_usbpd_get_by_phandle(struct device *dev,
+ const char *phandle);
+
+/*
+ * Called by client to handle specific SVID messages.
+ * Specify callback functions in the usbpd_svid_handler argument
+ */
+int usbpd_register_svid(struct usbpd *pd, struct usbpd_svid_handler *hdlr);
+
+void usbpd_unregister_svid(struct usbpd *pd, struct usbpd_svid_handler *hdlr);
+
+/*
+ * Transmit a VDM message.
+ */
+int usbpd_send_vdm(struct usbpd *pd, u32 vdm_hdr, const u32 *vdos,
+ int num_vdos);
+
+/*
+ * Transmit a Structured VDM message.
+ */
+int usbpd_send_svdm(struct usbpd *pd, u16 svid, u8 cmd,
+ enum usbpd_svdm_cmd_type cmd_type, int obj_pos,
+ const u32 *vdos, int num_vdos);
+
+/*
+ * Get current status of CC pin orientation.
+ *
+ * Return: ORIENTATION_CC1 or ORIENTATION_CC2 if attached,
+ * otherwise ORIENTATION_NONE if not attached
+ */
+enum plug_orientation usbpd_get_plug_orientation(struct usbpd *pd);
+#else
+static inline struct usbpd *devm_usbpd_get_by_phandle(struct device *dev,
+ const char *phandle)
+{
+ return ERR_PTR(-ENODEV);
+}
+
+static inline int usbpd_register_svid(struct usbpd *pd,
+ struct usbpd_svid_handler *hdlr)
+{
+ return -EINVAL;
+}
+
+static inline void usbpd_unregister_svid(struct usbpd *pd,
+ struct usbpd_svid_handler *hdlr)
+{
+}
+
+static inline int usbpd_send_vdm(struct usbpd *pd, u32 vdm_hdr, const u32 *vdos,
+ int num_vdos)
+{
+ return -EINVAL;
+}
+
+static inline int usbpd_send_svdm(struct usbpd *pd, u16 svid, u8 cmd,
+ enum usbpd_svdm_cmd_type cmd_type, int obj_pos,
+ const u32 *vdos, int num_vdos)
+{
+ return -EINVAL;
+}
+
+static inline enum plug_orientation usbpd_get_plug_orientation(struct usbpd *pd)
+{
+ return ORIENTATION_NONE;
+}
+#endif /* IS_ENABLED(CONFIG_USB_PD_POLICY) */
+
+/*
+ * Additional helpers for Enter/Exit Mode commands
+ */
+
+static inline int usbpd_enter_mode(struct usbpd *pd, u16 svid, int mode,
+ const u32 *vdo)
+{
+ return usbpd_send_svdm(pd, svid, USBPD_SVDM_ENTER_MODE,
+ SVDM_CMD_TYPE_INITIATOR, mode, vdo, vdo ? 1 : 0);
+}
+
+static inline int usbpd_exit_mode(struct usbpd *pd, u16 svid, int mode,
+ const u32 *vdo)
+{
+ return usbpd_send_svdm(pd, svid, USBPD_SVDM_EXIT_MODE,
+ SVDM_CMD_TYPE_INITIATOR, mode, vdo, vdo ? 1 : 0);
+}
+
+#endif /* __LINUX_USB_USBPD_H */
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/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/input/touchscreen/gt9xx/goodix_tool.c b/input/touchscreen/gt9xx/goodix_tool.c
new file mode 100644
index 000000000000..3dfe4e1d334e
--- /dev/null
+++ b/input/touchscreen/gt9xx/goodix_tool.c
@@ -0,0 +1,615 @@
+/* drivers/input/touchscreen/goodix_tool.c
+ *
+ * 2010 - 2012 Goodix Technology.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be a reference
+ * to you, when you are integrating the GOODiX's CTP IC into your system,
+ * 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.
+ *
+ * Version:1.6
+ * V1.0:2012/05/01,create file.
+ * V1.2:2012/06/08,modify some warning.
+ * V1.4:2012/08/28,modified to support GT9XX
+ * V1.6:new proc name
+ */
+
+#include "gt9xx.h"
+
+#define DATA_LENGTH_UINT 512
+#define CMD_HEAD_LENGTH (sizeof(st_cmd_head) - sizeof(u8*))
+static char procname[20] = {0};
+
+#define UPDATE_FUNCTIONS
+
+#ifdef UPDATE_FUNCTIONS
+extern s32 gup_enter_update_mode(struct i2c_client *client);
+extern void gup_leave_update_mode(void);
+extern s32 gup_update_proc(void *dir);
+#endif
+
+extern void gtp_irq_disable(struct goodix_ts_data *);
+extern void gtp_irq_enable(struct goodix_ts_data *);
+
+#pragma pack(1)
+typedef struct{
+ u8 wr; //write read flag£¬0:R 1:W 2:PID 3:
+ u8 flag; //0:no need flag/int 1: need flag 2:need int
+ u8 flag_addr[2]; //flag address
+ u8 flag_val; //flag val
+ u8 flag_relation; //flag_val:flag 0:not equal 1:equal 2:> 3:<
+ u16 circle; //polling cycle
+ u8 times; //plling times
+ u8 retry; //I2C retry times
+ u16 delay; //delay befor read or after write
+ u16 data_len; //data length
+ u8 addr_len; //address length
+ u8 addr[2]; //address
+ u8 res[3]; //reserved
+ u8* data; //data pointer
+}st_cmd_head;
+#pragma pack()
+st_cmd_head cmd_head;
+
+static struct i2c_client *gt_client = NULL;
+
+static struct proc_dir_entry *goodix_proc_entry;
+
+static s32 goodix_tool_write(struct file *filp, const char __user *buff, unsigned long len, void *data);
+static s32 goodix_tool_read( char *page, char **start, off_t off, int count, int *eof, void *data );
+static s32 (*tool_i2c_read)(u8 *, u16);
+static s32 (*tool_i2c_write)(u8 *, u16);
+
+#if GTP_ESD_PROTECT
+extern void gtp_esd_switch(struct i2c_client *, s32);
+#endif
+s32 DATA_LENGTH = 0;
+s8 IC_TYPE[16] = {0};
+
+static void tool_set_proc_name(char * procname)
+{
+ char *months[12] = {"Jan", "Feb", "Mar", "Apr", "May",
+ "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+ char date[20] = {0};
+ char month[4] = {0};
+ int i = 0, n_month = 1, n_day = 0, n_year = 0;
+
+ sprintf(date, "%s", __DATE__);
+
+ //GTP_DEBUG("compile date: %s", date);
+
+ sscanf(date, "%s %d %d", month, &n_day, &n_year);
+
+ for (i = 0; i < 12; ++i)
+ {
+ if (!memcmp(months[i], month, 3))
+ {
+ n_month = i+1;
+ break;
+ }
+ }
+
+ sprintf(procname, "gmnode%04d%02d%02d", n_year, n_month, n_day);
+
+ //GTP_DEBUG("procname = %s", procname);
+}
+
+
+static s32 tool_i2c_read_no_extra(u8* buf, u16 len)
+{
+ s32 ret = -1;
+ s32 i = 0;
+ struct i2c_msg msgs[2];
+
+ msgs[0].flags = !I2C_M_RD;
+ msgs[0].addr = gt_client->addr;
+ msgs[0].len = cmd_head.addr_len;
+ msgs[0].buf = &buf[0];
+
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].addr = gt_client->addr;
+ msgs[1].len = len;
+ msgs[1].buf = &buf[GTP_ADDR_LENGTH];
+
+ for (i = 0; i < cmd_head.retry; i++)
+ {
+ ret=i2c_transfer(gt_client->adapter, msgs, 2);
+ if (ret > 0)
+ {
+ break;
+ }
+ }
+ return ret;
+}
+
+static s32 tool_i2c_write_no_extra(u8* buf, u16 len)
+{
+ s32 ret = -1;
+ s32 i = 0;
+ struct i2c_msg msg;
+
+ msg.flags = !I2C_M_RD;
+ msg.addr = gt_client->addr;
+ msg.len = len;
+ msg.buf = buf;
+
+ for (i = 0; i < cmd_head.retry; i++)
+ {
+ ret=i2c_transfer(gt_client->adapter, &msg, 1);
+ if (ret > 0)
+ {
+ break;
+ }
+ }
+ return ret;
+}
+
+static s32 tool_i2c_read_with_extra(u8* buf, u16 len)
+{
+ s32 ret = -1;
+ u8 pre[2] = {0x0f, 0xff};
+ u8 end[2] = {0x80, 0x00};
+
+ tool_i2c_write_no_extra(pre, 2);
+ ret = tool_i2c_read_no_extra(buf, len);
+ tool_i2c_write_no_extra(end, 2);
+
+ return ret;
+}
+
+static s32 tool_i2c_write_with_extra(u8* buf, u16 len)
+{
+ s32 ret = -1;
+ u8 pre[2] = {0x0f, 0xff};
+ u8 end[2] = {0x80, 0x00};
+
+ tool_i2c_write_no_extra(pre, 2);
+ ret = tool_i2c_write_no_extra(buf, len);
+ tool_i2c_write_no_extra(end, 2);
+
+ return ret;
+}
+
+static void register_i2c_func(void)
+{
+// if (!strncmp(IC_TYPE, "GT818", 5) || !strncmp(IC_TYPE, "GT816", 5)
+// || !strncmp(IC_TYPE, "GT811", 5) || !strncmp(IC_TYPE, "GT818F", 6)
+// || !strncmp(IC_TYPE, "GT827", 5) || !strncmp(IC_TYPE,"GT828", 5)
+// || !strncmp(IC_TYPE, "GT813", 5))
+ if (strncmp(IC_TYPE, "GT8110", 6) && strncmp(IC_TYPE, "GT8105", 6)
+ && strncmp(IC_TYPE, "GT801", 5) && strncmp(IC_TYPE, "GT800", 5)
+ && strncmp(IC_TYPE, "GT801PLUS", 9) && strncmp(IC_TYPE, "GT811", 5)
+ && strncmp(IC_TYPE, "GTxxx", 5))
+ {
+ tool_i2c_read = tool_i2c_read_with_extra;
+ tool_i2c_write = tool_i2c_write_with_extra;
+ GTP_DEBUG("I2C function: with pre and end cmd!");
+ }
+ else
+ {
+ tool_i2c_read = tool_i2c_read_no_extra;
+ tool_i2c_write = tool_i2c_write_no_extra;
+ GTP_INFO("I2C function: without pre and end cmd!");
+ }
+}
+
+static void unregister_i2c_func(void)
+{
+ tool_i2c_read = NULL;
+ tool_i2c_write = NULL;
+ GTP_INFO("I2C function: unregister i2c transfer function!");
+}
+
+
+s32 init_wr_node(struct i2c_client *client)
+{
+ s32 i;
+
+ gt_client = client;
+ memset(&cmd_head, 0, sizeof(cmd_head));
+ cmd_head.data = NULL;
+
+ i = 5;
+ while ((!cmd_head.data) && i)
+ {
+ cmd_head.data = kzalloc(i * DATA_LENGTH_UINT, GFP_KERNEL);
+ if (NULL != cmd_head.data)
+ {
+ break;
+ }
+ i--;
+ }
+ if (i)
+ {
+ DATA_LENGTH = i * DATA_LENGTH_UINT + GTP_ADDR_LENGTH;
+ GTP_INFO("Applied memory size:%d.", DATA_LENGTH);
+ }
+ else
+ {
+ GTP_ERROR("Apply for memory failed.");
+ return FAIL;
+ }
+
+ cmd_head.addr_len = 2;
+ cmd_head.retry = 5;
+
+ register_i2c_func();
+
+ tool_set_proc_name(procname);
+ goodix_proc_entry = create_proc_entry(procname, 0666, NULL);
+ if (goodix_proc_entry == NULL)
+ {
+ GTP_ERROR("Couldn't create proc entry!");
+ return FAIL;
+ }
+ else
+ {
+ GTP_INFO("Create proc entry success!");
+ goodix_proc_entry->write_proc = goodix_tool_write;
+ goodix_proc_entry->read_proc = goodix_tool_read;
+ }
+
+ return SUCCESS;
+}
+
+void uninit_wr_node(void)
+{
+ kfree(cmd_head.data);
+ cmd_head.data = NULL;
+ unregister_i2c_func();
+ remove_proc_entry(procname, NULL);
+}
+
+static u8 relation(u8 src, u8 dst, u8 rlt)
+{
+ u8 ret = 0;
+
+ switch (rlt)
+ {
+ case 0:
+ ret = (src != dst) ? true : false;
+ break;
+
+ case 1:
+ ret = (src == dst) ? true : false;
+ GTP_DEBUG("equal:src:0x%02x dst:0x%02x ret:%d.", src, dst, (s32)ret);
+ break;
+
+ case 2:
+ ret = (src > dst) ? true : false;
+ break;
+
+ case 3:
+ ret = (src < dst) ? true : false;
+ break;
+
+ case 4:
+ ret = (src & dst) ? true : false;
+ break;
+
+ case 5:
+ ret = (!(src | dst)) ? true : false;
+ break;
+
+ default:
+ ret = false;
+ break;
+ }
+
+ return ret;
+}
+
+/*******************************************************
+Function:
+ Comfirm function.
+Input:
+ None.
+Output:
+ Return write length.
+********************************************************/
+static u8 comfirm(void)
+{
+ s32 i = 0;
+ u8 buf[32];
+
+// memcpy(&buf[GTP_ADDR_LENGTH - cmd_head.addr_len], &cmd_head.flag_addr, cmd_head.addr_len);
+// memcpy(buf, &cmd_head.flag_addr, cmd_head.addr_len);//Modified by Scott, 2012-02-17
+ memcpy(buf, cmd_head.flag_addr, cmd_head.addr_len);
+
+ for (i = 0; i < cmd_head.times; i++)
+ {
+ if (tool_i2c_read(buf, 1) <= 0)
+ {
+ GTP_ERROR("Read flag data failed!");
+ return FAIL;
+ }
+ if (true == relation(buf[GTP_ADDR_LENGTH], cmd_head.flag_val, cmd_head.flag_relation))
+ {
+ GTP_DEBUG("value at flag addr:0x%02x.", buf[GTP_ADDR_LENGTH]);
+ GTP_DEBUG("flag value:0x%02x.", cmd_head.flag_val);
+ break;
+ }
+
+ msleep(cmd_head.circle);
+ }
+
+ if (i >= cmd_head.times)
+ {
+ GTP_ERROR("Didn't get the flag to continue!");
+ return FAIL;
+ }
+
+ return SUCCESS;
+}
+
+/*******************************************************
+Function:
+ Goodix tool write function.
+Input:
+ standard proc write function param.
+Output:
+ Return write length.
+********************************************************/
+static s32 goodix_tool_write(struct file *filp, const char __user *buff, unsigned long len, void *data)
+{
+ s32 ret = 0;
+ GTP_DEBUG_FUNC();
+ GTP_DEBUG_ARRAY((u8*)buff, len);
+
+ ret = copy_from_user(&cmd_head, buff, CMD_HEAD_LENGTH);
+ if(ret)
+ {
+ GTP_ERROR("copy_from_user failed.");
+ }
+
+ GTP_DEBUG("wr :0x%02x.", cmd_head.wr);
+ GTP_DEBUG("flag:0x%02x.", cmd_head.flag);
+ GTP_DEBUG("flag addr:0x%02x%02x.", cmd_head.flag_addr[0], cmd_head.flag_addr[1]);
+ GTP_DEBUG("flag val:0x%02x.", cmd_head.flag_val);
+ GTP_DEBUG("flag rel:0x%02x.", cmd_head.flag_relation);
+ GTP_DEBUG("circle :%d.", (s32)cmd_head.circle);
+ GTP_DEBUG("times :%d.", (s32)cmd_head.times);
+ GTP_DEBUG("retry :%d.", (s32)cmd_head.retry);
+ GTP_DEBUG("delay :%d.", (s32)cmd_head.delay);
+ GTP_DEBUG("data len:%d.", (s32)cmd_head.data_len);
+ GTP_DEBUG("addr len:%d.", (s32)cmd_head.addr_len);
+ GTP_DEBUG("addr:0x%02x%02x.", cmd_head.addr[0], cmd_head.addr[1]);
+ GTP_DEBUG("len:%d.", (s32)len);
+ GTP_DEBUG("buf[20]:0x%02x.", buff[CMD_HEAD_LENGTH]);
+
+ if (1 == cmd_head.wr)
+ {
+ // copy_from_user(&cmd_head.data[cmd_head.addr_len], &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
+ ret = copy_from_user(&cmd_head.data[GTP_ADDR_LENGTH], &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
+ if(ret)
+ {
+ GTP_ERROR("copy_from_user failed.");
+ }
+ memcpy(&cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len], cmd_head.addr, cmd_head.addr_len);
+
+ GTP_DEBUG_ARRAY(cmd_head.data, cmd_head.data_len + cmd_head.addr_len);
+ GTP_DEBUG_ARRAY((u8*)&buff[CMD_HEAD_LENGTH], cmd_head.data_len);
+
+ if (1 == cmd_head.flag)
+ {
+ if (FAIL == comfirm())
+ {
+ GTP_ERROR("[WRITE]Comfirm fail!");
+ return FAIL;
+ }
+ }
+ else if (2 == cmd_head.flag)
+ {
+ //Need interrupt!
+ }
+ if (tool_i2c_write(&cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len],
+ cmd_head.data_len + cmd_head.addr_len) <= 0)
+ {
+ GTP_ERROR("[WRITE]Write data failed!");
+ return FAIL;
+ }
+
+ GTP_DEBUG_ARRAY(&cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len],cmd_head.data_len + cmd_head.addr_len);
+ if (cmd_head.delay)
+ {
+ msleep(cmd_head.delay);
+ }
+
+ return cmd_head.data_len + CMD_HEAD_LENGTH;
+ }
+ else if (3 == cmd_head.wr) //Write ic type
+ {
+ ret = copy_from_user(&cmd_head.data[0], &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
+ if(ret)
+ {
+ GTP_ERROR("copy_from_user failed.");
+ }
+ memcpy(IC_TYPE, cmd_head.data, cmd_head.data_len);
+
+ register_i2c_func();
+
+ return cmd_head.data_len + CMD_HEAD_LENGTH;
+ }
+ else if (5 == cmd_head.wr)
+ {
+ //memcpy(IC_TYPE, cmd_head.data, cmd_head.data_len);
+
+ return cmd_head.data_len + CMD_HEAD_LENGTH;
+ }
+ else if (7 == cmd_head.wr)//disable irq!
+ {
+ gtp_irq_disable(i2c_get_clientdata(gt_client));
+
+ #if GTP_ESD_PROTECT
+ gtp_esd_switch(gt_client, SWITCH_OFF);
+ #endif
+ return CMD_HEAD_LENGTH;
+ }
+ else if (9 == cmd_head.wr) //enable irq!
+ {
+ gtp_irq_enable(i2c_get_clientdata(gt_client));
+
+ #if GTP_ESD_PROTECT
+ gtp_esd_switch(gt_client, SWITCH_ON);
+ #endif
+ return CMD_HEAD_LENGTH;
+ }
+ else if(17 == cmd_head.wr)
+ {
+ struct goodix_ts_data *ts = i2c_get_clientdata(gt_client);
+ ret = copy_from_user(&cmd_head.data[GTP_ADDR_LENGTH], &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
+ if(ret)
+ {
+ GTP_DEBUG("copy_from_user failed.");
+ }
+ if(cmd_head.data[GTP_ADDR_LENGTH])
+ {
+ GTP_DEBUG("gtp enter rawdiff.");
+ ts->gtp_rawdiff_mode = true;
+ }
+ else
+ {
+ ts->gtp_rawdiff_mode = false;
+ GTP_DEBUG("gtp leave rawdiff.");
+ }
+ return CMD_HEAD_LENGTH;
+ }
+#ifdef UPDATE_FUNCTIONS
+ else if (11 == cmd_head.wr)//Enter update mode!
+ {
+ if (FAIL == gup_enter_update_mode(gt_client))
+ {
+ return FAIL;
+ }
+ }
+ else if (13 == cmd_head.wr)//Leave update mode!
+ {
+ gup_leave_update_mode();
+ }
+ else if (15 == cmd_head.wr) //Update firmware!
+ {
+ show_len = 0;
+ total_len = 0;
+ memset(cmd_head.data, 0, cmd_head.data_len + 1);
+ memcpy(cmd_head.data, &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
+
+ if (FAIL == gup_update_proc((void*)cmd_head.data))
+ {
+ return FAIL;
+ }
+ }
+#endif
+
+ return CMD_HEAD_LENGTH;
+}
+
+/*******************************************************
+Function:
+ Goodix tool read function.
+Input:
+ standard proc read function param.
+Output:
+ Return read length.
+********************************************************/
+static s32 goodix_tool_read( char *page, char **start, off_t off, int count, int *eof, void *data )
+{
+ GTP_DEBUG_FUNC();
+
+ if (cmd_head.wr % 2)
+ {
+ return FAIL;
+ }
+ else if (!cmd_head.wr)
+ {
+ u16 len = 0;
+ s16 data_len = 0;
+ u16 loc = 0;
+
+ if (1 == cmd_head.flag)
+ {
+ if (FAIL == comfirm())
+ {
+ GTP_ERROR("[READ]Comfirm fail!");
+ return FAIL;
+ }
+ }
+ else if (2 == cmd_head.flag)
+ {
+ //Need interrupt!
+ }
+
+ memcpy(cmd_head.data, cmd_head.addr, cmd_head.addr_len);
+
+ GTP_DEBUG("[CMD HEAD DATA] ADDR:0x%02x%02x.", cmd_head.data[0], cmd_head.data[1]);
+ GTP_DEBUG("[CMD HEAD ADDR] ADDR:0x%02x%02x.", cmd_head.addr[0], cmd_head.addr[1]);
+
+ if (cmd_head.delay)
+ {
+ msleep(cmd_head.delay);
+ }
+
+ data_len = cmd_head.data_len;
+ while(data_len > 0)
+ {
+ if (data_len > DATA_LENGTH)
+ {
+ len = DATA_LENGTH;
+ }
+ else
+ {
+ len = data_len;
+ }
+ data_len -= DATA_LENGTH;
+
+ if (tool_i2c_read(cmd_head.data, len) <= 0)
+ {
+ GTP_ERROR("[READ]Read data failed!");
+ return FAIL;
+ }
+ memcpy(&page[loc], &cmd_head.data[GTP_ADDR_LENGTH], len);
+ loc += len;
+
+ GTP_DEBUG_ARRAY(&cmd_head.data[GTP_ADDR_LENGTH], len);
+ GTP_DEBUG_ARRAY(page, len);
+ }
+ }
+ else if (2 == cmd_head.wr)
+ {
+ // memcpy(page, "gt8", cmd_head.data_len);
+ // memcpy(page, "GT818", 5);
+ // page[5] = 0;
+
+ GTP_DEBUG("Return ic type:%s len:%d.", page, (s32)cmd_head.data_len);
+ return cmd_head.data_len;
+ //return sizeof(IC_TYPE_NAME);
+ }
+ else if (4 == cmd_head.wr)
+ {
+ page[0] = show_len >> 8;
+ page[1] = show_len & 0xff;
+ page[2] = total_len >> 8;
+ page[3] = total_len & 0xff;
+
+ return cmd_head.data_len;
+ }
+ else if (6 == cmd_head.wr)
+ {
+ //Read error code!
+ }
+ else if (8 == cmd_head.wr) //Read driver version
+ {
+ // memcpy(page, GTP_DRIVER_VERSION, strlen(GTP_DRIVER_VERSION));
+ s32 tmp_len;
+ tmp_len = strlen(GTP_DRIVER_VERSION);
+ memcpy(page, GTP_DRIVER_VERSION, tmp_len);
+ page[tmp_len] = 0;
+ }
+
+ return cmd_head.data_len;
+}
diff --git a/input/touchscreen/gt9xx/gt9xx.c b/input/touchscreen/gt9xx/gt9xx.c
new file mode 100644
index 000000000000..b1dc08bdd54f
--- /dev/null
+++ b/input/touchscreen/gt9xx/gt9xx.c
@@ -0,0 +1,1810 @@
+/* drivers/input/touchscreen/gt9xx.c
+ *
+ * 2010 - 2013 Goodix Technology.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be a reference
+ * to you, when you are integrating the GOODiX's CTP IC into your system,
+ * 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.
+ *
+ * Version: 1.8
+ * Authors: andrew@goodix.com, meta@goodix.com
+ * Release Date: 2013/04/25
+ * Revision record:
+ * V1.0:
+ * first Release. By Andrew, 2012/08/31
+ * V1.2:
+ * modify gtp_reset_guitar,slot report,tracking_id & 0x0F. By Andrew, 2012/10/15
+ * V1.4:
+ * modify gt9xx_update.c. By Andrew, 2012/12/12
+ * V1.6:
+ * 1. new heartbeat/esd_protect mechanism(add external watchdog)
+ * 2. doze mode, sliding wakeup
+ * 3. 3 more cfg_group(GT9 Sensor_ID: 0~5)
+ * 3. config length verification
+ * 4. names & comments
+ * By Meta, 2013/03/11
+ * V1.8:
+ * 1. pen/stylus identification
+ * 2. read double check & fixed config support
+ * 2. new esd & slide wakeup optimization
+ * By Meta, 2013/06/08
+ */
+
+#include <linux/irq.h>
+#include "gt9xx.h"
+
+#if GTP_ICS_SLOT_REPORT
+ #include <linux/input/mt.h>
+#endif
+
+static const char *goodix_ts_name = "Goodix Capacitive TouchScreen";
+static struct workqueue_struct *goodix_wq;
+struct i2c_client * i2c_connect_client = NULL;
+u8 config[GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH]
+ = {GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff};
+
+#if GTP_HAVE_TOUCH_KEY
+ static const u16 touch_key_array[] = GTP_KEY_TAB;
+ #define GTP_MAX_KEY_NUM (sizeof(touch_key_array)/sizeof(touch_key_array[0]))
+
+#if GTP_DEBUG_ON
+ static const int key_codes[] = {KEY_HOME, KEY_BACK, KEY_MENU, KEY_SEARCH};
+ static const char *key_names[] = {"Key_Home", "Key_Back", "Key_Menu", "Key_Search"};
+#endif
+
+#endif
+
+static s8 gtp_i2c_test(struct i2c_client *client);
+void gtp_reset_guitar(struct i2c_client *client, s32 ms);
+void gtp_int_sync(s32 ms);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void goodix_ts_early_suspend(struct early_suspend *h);
+static void goodix_ts_late_resume(struct early_suspend *h);
+#endif
+
+#if GTP_CREATE_WR_NODE
+extern s32 init_wr_node(struct i2c_client*);
+extern void uninit_wr_node(void);
+#endif
+
+#if GTP_AUTO_UPDATE
+extern u8 gup_init_update_proc(struct goodix_ts_data *);
+#endif
+
+#if GTP_ESD_PROTECT
+static struct delayed_work gtp_esd_check_work;
+static struct workqueue_struct * gtp_esd_check_workqueue = NULL;
+static void gtp_esd_check_func(struct work_struct *);
+static s32 gtp_init_ext_watchdog(struct i2c_client *client);
+void gtp_esd_switch(struct i2c_client *, s32);
+#endif
+
+
+#if GTP_SLIDE_WAKEUP
+typedef enum
+{
+ DOZE_DISABLED = 0,
+ DOZE_ENABLED = 1,
+ DOZE_WAKEUP = 2,
+}DOZE_T;
+static DOZE_T doze_status = DOZE_DISABLED;
+static s8 gtp_enter_doze(struct goodix_ts_data *ts);
+#endif
+
+static u8 chip_gt9xxs = 0; // true if ic is gt9xxs, like gt915s
+u8 grp_cfg_version = 0;
+
+/*******************************************************
+Function:
+ Read data from the i2c slave device.
+Input:
+ client: i2c device.
+ buf[0~1]: read start address.
+ buf[2~len-1]: read data buffer.
+ len: GTP_ADDR_LENGTH + read bytes count
+Output:
+ numbers of i2c_msgs to transfer:
+ 2: succeed, otherwise: failed
+*********************************************************/
+s32 gtp_i2c_read(struct i2c_client *client, u8 *buf, s32 len)
+{
+ struct i2c_msg msgs[2];
+ s32 ret=-1;
+ s32 retries = 0;
+
+ GTP_DEBUG_FUNC();
+
+ msgs[0].flags = !I2C_M_RD;
+ msgs[0].addr = client->addr;
+ msgs[0].len = GTP_ADDR_LENGTH;
+ msgs[0].buf = &buf[0];
+ //msgs[0].scl_rate = 300 * 1000; // for Rockchip
+
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].addr = client->addr;
+ msgs[1].len = len - GTP_ADDR_LENGTH;
+ msgs[1].buf = &buf[GTP_ADDR_LENGTH];
+ //msgs[1].scl_rate = 300 * 1000;
+
+ while(retries < 5)
+ {
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ if(ret == 2)break;
+ retries++;
+ }
+ if((retries >= 5))
+ {
+ #if GTP_SLIDE_WAKEUP
+ // reset chip would quit doze mode
+ if (DOZE_ENABLED == doze_status)
+ {
+ return ret;
+ }
+ #endif
+ GTP_DEBUG("I2C communication timeout, resetting chip...");
+ gtp_reset_guitar(client, 10);
+ }
+ return ret;
+}
+
+/*******************************************************
+Function:
+ Write data to the i2c slave device.
+Input:
+ client: i2c device.
+ buf[0~1]: write start address.
+ buf[2~len-1]: data buffer
+ len: GTP_ADDR_LENGTH + write bytes count
+Output:
+ numbers of i2c_msgs to transfer:
+ 1: succeed, otherwise: failed
+*********************************************************/
+s32 gtp_i2c_write(struct i2c_client *client,u8 *buf,s32 len)
+{
+ struct i2c_msg msg;
+ s32 ret = -1;
+ s32 retries = 0;
+
+ GTP_DEBUG_FUNC();
+
+ msg.flags = !I2C_M_RD;
+ msg.addr = client->addr;
+ msg.len = len;
+ msg.buf = buf;
+ //msg.scl_rate = 300 * 1000; // for Rockchip
+
+ while(retries < 5)
+ {
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret == 1)break;
+ retries++;
+ }
+ if((retries >= 5))
+ {
+ #if GTP_SLIDE_WAKEUP
+ if (DOZE_ENABLED == doze_status)
+ {
+ return ret;
+ }
+ #endif
+ GTP_DEBUG("I2C communication timeout, resetting chip...");
+ gtp_reset_guitar(client, 10);
+ }
+ return ret;
+}
+/*******************************************************
+Function:
+ i2c read twice, compare the results
+Input:
+ client: i2c device
+ addr: operate address
+ rxbuf: read data to store, if compare successful
+ len: bytes to read
+Output:
+ FAIL: read failed
+ SUCCESS: read successful
+*********************************************************/
+s32 gtp_i2c_read_dbl_check(struct i2c_client *client, u16 addr, u8 *rxbuf, int len)
+{
+ u8 buf[16] = {0};
+ u8 confirm_buf[16] = {0};
+ u8 retry = 0;
+
+ while (retry++ < 3)
+ {
+ memset(buf, 0xAA, 16);
+ buf[0] = (u8)(addr >> 8);
+ buf[1] = (u8)(addr & 0xFF);
+ gtp_i2c_read(client, buf, len + 2);
+
+ memset(confirm_buf, 0xAB, 16);
+ confirm_buf[0] = (u8)(addr >> 8);
+ confirm_buf[1] = (u8)(addr & 0xFF);
+ gtp_i2c_read(client, confirm_buf, len + 2);
+
+ if (!memcmp(buf, confirm_buf, len+2))
+ {
+ break;
+ }
+ }
+ if (retry < 3)
+ {
+ memcpy(rxbuf, confirm_buf+2, len);
+ return SUCCESS;
+ }
+ else
+ {
+ GTP_ERROR("i2c read 0x%04X, %d bytes, double check failed!", addr, len);
+ return FAIL;
+ }
+}
+
+/*******************************************************
+Function:
+ Send config.
+Input:
+ client: i2c device.
+Output:
+ result of i2c write operation.
+ 1: succeed, otherwise: failed
+*********************************************************/
+s32 gtp_send_cfg(struct i2c_client *client)
+{
+ s32 ret = 2;
+
+#if GTP_DRIVER_SEND_CFG
+ s32 retry = 0;
+ struct goodix_ts_data *ts = i2c_get_clientdata(client);
+
+ if (ts->fixed_cfg)
+ {
+ GTP_INFO("Ic fixed config, no config sent!");
+ return 2;
+ }
+ GTP_INFO("driver send config");
+ for (retry = 0; retry < 5; retry++)
+ {
+ ret = gtp_i2c_write(client, config , GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH);
+ if (ret > 0)
+ {
+ break;
+ }
+ }
+#endif
+
+ return ret;
+}
+
+/*******************************************************
+Function:
+ Disable irq function
+Input:
+ ts: goodix i2c_client private data
+Output:
+ None.
+*********************************************************/
+void gtp_irq_disable(struct goodix_ts_data *ts)
+{
+ unsigned long irqflags;
+
+ GTP_DEBUG_FUNC();
+
+ spin_lock_irqsave(&ts->irq_lock, irqflags);
+ if (!ts->irq_is_disable)
+ {
+ ts->irq_is_disable = 1;
+ disable_irq_nosync(ts->client->irq);
+ }
+ spin_unlock_irqrestore(&ts->irq_lock, irqflags);
+}
+
+/*******************************************************
+Function:
+ Enable irq function
+Input:
+ ts: goodix i2c_client private data
+Output:
+ None.
+*********************************************************/
+void gtp_irq_enable(struct goodix_ts_data *ts)
+{
+ unsigned long irqflags = 0;
+
+ GTP_DEBUG_FUNC();
+
+ spin_lock_irqsave(&ts->irq_lock, irqflags);
+ if (ts->irq_is_disable)
+ {
+ enable_irq(ts->client->irq);
+ ts->irq_is_disable = 0;
+ }
+ spin_unlock_irqrestore(&ts->irq_lock, irqflags);
+}
+
+
+/*******************************************************
+Function:
+ Report touch point event
+Input:
+ ts: goodix i2c_client private data
+ id: trackId
+ x: input x coordinate
+ y: input y coordinate
+ w: input pressure
+Output:
+ None.
+*********************************************************/
+static void gtp_touch_down(struct goodix_ts_data* ts,s32 id,s32 x,s32 y,s32 w)
+{
+#if GTP_CHANGE_X2Y
+ GTP_SWAP(x, y);
+#endif
+
+#if GTP_ICS_SLOT_REPORT
+ input_mt_slot(ts->input_dev, id);
+ input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w);
+ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w);
+#else
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w);
+ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w);
+ input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);
+ input_mt_sync(ts->input_dev);
+#endif
+
+ GTP_DEBUG("ID:%d, X:%d, Y:%d, W:%d", id, x, y, w);
+}
+
+/*******************************************************
+Function:
+ Report touch release event
+Input:
+ ts: goodix i2c_client private data
+Output:
+ None.
+*********************************************************/
+static void gtp_touch_up(struct goodix_ts_data* ts, s32 id)
+{
+#if GTP_ICS_SLOT_REPORT
+ input_mt_slot(ts->input_dev, id);
+ input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1);
+ GTP_DEBUG("Touch id[%2d] release!", id);
+#else
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);
+ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0);
+ input_mt_sync(ts->input_dev);
+#endif
+}
+
+
+/*******************************************************
+Function:
+ Goodix touchscreen work function
+Input:
+ work: work struct of goodix_workqueue
+Output:
+ None.
+*********************************************************/
+static void goodix_ts_work_func(struct work_struct *work)
+{
+ u8 end_cmd[3] = {GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF, 0};
+ u8 point_data[2 + 1 + 8 * GTP_MAX_TOUCH + 1]={GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF};
+ u8 touch_num = 0;
+ u8 finger = 0;
+ static u16 pre_touch = 0;
+ static u8 pre_key = 0;
+#if GTP_WITH_PEN
+ static u8 pre_pen = 0;
+#endif
+ u8 key_value = 0;
+ u8* coor_data = NULL;
+ s32 input_x = 0;
+ s32 input_y = 0;
+ s32 input_w = 0;
+ s32 id = 0;
+ s32 i = 0;
+ s32 ret = -1;
+ struct goodix_ts_data *ts = NULL;
+
+#if GTP_SLIDE_WAKEUP
+ u8 doze_buf[3] = {0x81, 0x4B};
+#endif
+
+ GTP_DEBUG_FUNC();
+ ts = container_of(work, struct goodix_ts_data, work);
+ if (ts->enter_update)
+ {
+ return;
+ }
+#if GTP_SLIDE_WAKEUP
+ if (DOZE_ENABLED == doze_status)
+ {
+ ret = gtp_i2c_read(i2c_connect_client, doze_buf, 3);
+ GTP_DEBUG("0x814B = 0x%02X", doze_buf[2]);
+ if (ret > 0)
+ {
+ if (doze_buf[2] == 0xAA)
+ {
+ GTP_INFO("Slide(0xAA) To Light up the screen!");
+ doze_status = DOZE_WAKEUP;
+ input_report_key(ts->input_dev, KEY_POWER, 1);
+ input_sync(ts->input_dev);
+ input_report_key(ts->input_dev, KEY_POWER, 0);
+ input_sync(ts->input_dev);
+ // clear 0x814B
+ doze_buf[2] = 0x00;
+ gtp_i2c_write(i2c_connect_client, doze_buf, 3);
+ }
+ else if (doze_buf[2] == 0xBB)
+ {
+ GTP_INFO("Slide(0xBB) To Light up the screen!");
+ doze_status = DOZE_WAKEUP;
+ input_report_key(ts->input_dev, KEY_POWER, 1);
+ input_sync(ts->input_dev);
+ input_report_key(ts->input_dev, KEY_POWER, 0);
+ input_sync(ts->input_dev);
+ // clear 0x814B
+ doze_buf[2] = 0x00;
+ gtp_i2c_write(i2c_connect_client, doze_buf, 3);
+ }
+ else if (0xC0 == (doze_buf[2] & 0xC0))
+ {
+ GTP_INFO("double click to light up the screen!");
+ doze_status = DOZE_WAKEUP;
+ input_report_key(ts->input_dev, KEY_POWER, 1);
+ input_sync(ts->input_dev);
+ input_report_key(ts->input_dev, KEY_POWER, 0);
+ input_sync(ts->input_dev);
+ // clear 0x814B
+ doze_buf[2] = 0x00;
+ gtp_i2c_write(i2c_connect_client, doze_buf, 3);
+ }
+ else
+ {
+ gtp_enter_doze(ts);
+ }
+ }
+ if (ts->use_irq)
+ {
+ gtp_irq_enable(ts);
+ }
+ return;
+ }
+#endif
+
+ ret = gtp_i2c_read(ts->client, point_data, 12);
+ if (ret < 0)
+ {
+ GTP_ERROR("I2C transfer error. errno:%d\n ", ret);
+ goto exit_work_func;
+ }
+
+ finger = point_data[GTP_ADDR_LENGTH];
+ if((finger & 0x80) == 0)
+ {
+ goto exit_work_func;
+ }
+
+ touch_num = finger & 0x0f;
+ if (touch_num > GTP_MAX_TOUCH)
+ {
+ goto exit_work_func;
+ }
+
+ if (touch_num > 1)
+ {
+ u8 buf[8 * GTP_MAX_TOUCH] = {(GTP_READ_COOR_ADDR + 10) >> 8, (GTP_READ_COOR_ADDR + 10) & 0xff};
+
+ ret = gtp_i2c_read(ts->client, buf, 2 + 8 * (touch_num - 1));
+ memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1));
+ }
+
+#if GTP_HAVE_TOUCH_KEY
+ key_value = point_data[3 + 8 * touch_num];
+
+ if(key_value || pre_key)
+ {
+ for (i = 0; i < GTP_MAX_KEY_NUM; i++)
+ {
+ #if GTP_DEBUG_ON
+ for (ret = 0; ret < 4; ++ret)
+ {
+ if (key_codes[ret] == touch_key_array[i])
+ {
+ GTP_DEBUG("Key: %s %s", key_names[ret], (key_value & (0x01 << i)) ? "Down" : "Up");
+ break;
+ }
+ }
+ #endif
+ input_report_key(ts->input_dev, touch_key_array[i], key_value & (0x01<<i));
+ }
+ touch_num = 0;
+ pre_touch = 0;
+ }
+#endif
+ pre_key = key_value;
+
+ GTP_DEBUG("pre_touch:%02x, finger:%02x.", pre_touch, finger);
+
+#if GTP_ICS_SLOT_REPORT
+
+#if GTP_WITH_PEN
+ if (pre_pen && (touch_num == 0))
+ {
+ GTP_DEBUG("Pen touch UP(Slot)!");
+ input_report_key(ts->input_dev, BTN_TOOL_PEN, 0);
+ input_mt_slot(ts->input_dev, 5);
+ input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1);
+ pre_pen = 0;
+ }
+#endif
+ if (pre_touch || touch_num)
+ {
+ s32 pos = 0;
+ u16 touch_index = 0;
+
+ coor_data = &point_data[3];
+
+ if(touch_num)
+ {
+ id = coor_data[pos] & 0x0F;
+
+ #if GTP_WITH_PEN
+ id = coor_data[pos];
+ if ((id == 128))
+ {
+ GTP_DEBUG("Pen touch DOWN(Slot)!");
+ input_x = coor_data[pos + 1] | (coor_data[pos + 2] << 8);
+ input_y = coor_data[pos + 3] | (coor_data[pos + 4] << 8);
+ input_w = coor_data[pos + 5] | (coor_data[pos + 6] << 8);
+
+ input_report_key(ts->input_dev, BTN_TOOL_PEN, 1);
+ input_mt_slot(ts->input_dev, 5);
+ input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, 5);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y);
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w);
+ GTP_DEBUG("Pen/Stylus: (%d, %d)[%d]", input_x, input_y, input_w);
+ pre_pen = 1;
+ pre_touch = 0;
+ }
+ #endif
+
+ touch_index |= (0x01<<id);
+ }
+
+ GTP_DEBUG("id = %d,touch_index = 0x%x, pre_touch = 0x%x\n",id, touch_index,pre_touch);
+ for (i = 0; i < GTP_MAX_TOUCH; i++)
+ {
+ #if GTP_WITH_PEN
+ if (pre_pen == 1)
+ {
+ break;
+ }
+ #endif
+
+ if (touch_index & (0x01<<i))
+ {
+ input_x = coor_data[pos + 1] | (coor_data[pos + 2] << 8);
+ input_y = coor_data[pos + 3] | (coor_data[pos + 4] << 8);
+ input_w = coor_data[pos + 5] | (coor_data[pos + 6] << 8);
+
+ gtp_touch_down(ts, id, input_x, input_y, input_w);
+ pre_touch |= 0x01 << i;
+
+ pos += 8;
+ id = coor_data[pos] & 0x0F;
+ touch_index |= (0x01<<id);
+ }
+ else
+ {
+ gtp_touch_up(ts, i);
+ pre_touch &= ~(0x01 << i);
+ }
+ }
+ }
+#else
+ input_report_key(ts->input_dev, BTN_TOUCH, (touch_num || key_value));
+ if (touch_num)
+ {
+ for (i = 0; i < touch_num; i++)
+ {
+ coor_data = &point_data[i * 8 + 3];
+
+ id = coor_data[0]; // & 0x0F;
+ input_x = coor_data[1] | (coor_data[2] << 8);
+ input_y = coor_data[3] | (coor_data[4] << 8);
+ input_w = coor_data[5] | (coor_data[6] << 8);
+
+ #if GTP_WITH_PEN
+ if (id == 128)
+ {
+ GTP_DEBUG("Pen touch DOWN!");
+ input_report_key(ts->input_dev, BTN_TOOL_PEN, 1);
+ pre_pen = 1;
+ id = 0;
+ }
+ #endif
+
+ gtp_touch_down(ts, id, input_x, input_y, input_w);
+ }
+ }
+ else if (pre_touch)
+ {
+
+ #if GTP_WITH_PEN
+ if (pre_pen == 1)
+ {
+ GTP_DEBUG("Pen touch UP!");
+ input_report_key(ts->input_dev, BTN_TOOL_PEN, 0);
+ pre_pen = 0;
+ }
+ #endif
+
+ GTP_DEBUG("Touch Release!");
+ gtp_touch_up(ts, 0);
+ }
+
+ pre_touch = touch_num;
+#endif
+
+ input_sync(ts->input_dev);
+
+exit_work_func:
+ if(!ts->gtp_rawdiff_mode)
+ {
+ ret = gtp_i2c_write(ts->client, end_cmd, 3);
+ if (ret < 0)
+ {
+ GTP_INFO("I2C write end_cmd error!");
+ }
+ }
+ if (ts->use_irq)
+ {
+ gtp_irq_enable(ts);
+ }
+}
+
+/*******************************************************
+Function:
+ Timer interrupt service routine for polling mode.
+Input:
+ timer: timer struct pointer
+Output:
+ Timer work mode.
+ HRTIMER_NORESTART: no restart mode
+*********************************************************/
+static enum hrtimer_restart goodix_ts_timer_handler(struct hrtimer *timer)
+{
+ struct goodix_ts_data *ts = container_of(timer, struct goodix_ts_data, timer);
+
+ GTP_DEBUG_FUNC();
+
+ queue_work(goodix_wq, &ts->work);
+ hrtimer_start(&ts->timer, ktime_set(0, (GTP_POLL_TIME+6)*1000000), HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+}
+
+/*******************************************************
+Function:
+ External interrupt service routine for interrupt mode.
+Input:
+ irq: interrupt number.
+ dev_id: private data pointer
+Output:
+ Handle Result.
+ IRQ_HANDLED: interrupt handled successfully
+*********************************************************/
+static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
+{
+ struct goodix_ts_data *ts = dev_id;
+
+ GTP_DEBUG_FUNC();
+
+ gtp_irq_disable(ts);
+
+ queue_work(goodix_wq, &ts->work);
+
+ return IRQ_HANDLED;
+}
+/*******************************************************
+Function:
+ Synchronization.
+Input:
+ ms: synchronization time in millisecond.
+Output:
+ None.
+*******************************************************/
+void gtp_int_sync(s32 ms)
+{
+ GTP_GPIO_OUTPUT(GTP_INT_PORT, 0);
+ msleep(ms);
+ GTP_GPIO_AS_INT(GTP_INT_PORT);
+}
+
+/*******************************************************
+Function:
+ Reset chip.
+Input:
+ ms: reset time in millisecond
+Output:
+ None.
+*******************************************************/
+void gtp_reset_guitar(struct i2c_client *client, s32 ms)
+{
+ GTP_DEBUG_FUNC();
+
+ GTP_GPIO_OUTPUT(GTP_RST_PORT, 0); // begin select I2C slave addr
+ msleep(ms); // T2: > 10ms
+ // HIGH: 0x28/0x29, LOW: 0xBA/0xBB
+ GTP_GPIO_OUTPUT(GTP_INT_PORT, client->addr == 0x14);
+
+ msleep(2); // T3: > 100us
+ GTP_GPIO_OUTPUT(GTP_RST_PORT, 1);
+
+ msleep(6); // T4: > 5ms
+
+ GTP_GPIO_AS_INPUT(GTP_RST_PORT); // end select I2C slave addr
+
+ gtp_int_sync(50);
+
+#if GTP_ESD_PROTECT
+ gtp_init_ext_watchdog(client);
+#endif
+}
+
+#if GTP_SLIDE_WAKEUP
+/*******************************************************
+Function:
+ Enter doze mode for sliding wakeup.
+Input:
+ ts: goodix tp private data
+Output:
+ 1: succeed, otherwise failed
+*******************************************************/
+static s8 gtp_enter_doze(struct goodix_ts_data *ts)
+{
+ s8 ret = -1;
+ s8 retry = 0;
+ u8 i2c_control_buf[3] = {(u8)(GTP_REG_SLEEP >> 8), (u8)GTP_REG_SLEEP, 8};
+
+ GTP_DEBUG_FUNC();
+
+#if GTP_DBL_CLK_WAKEUP
+ i2c_control_buf[2] = 0x09;
+#endif
+
+ gtp_irq_disable(ts);
+
+ GTP_DEBUG("entering doze mode...");
+ while(retry++ < 5)
+ {
+ i2c_control_buf[0] = 0x80;
+ i2c_control_buf[1] = 0x46;
+ ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
+ if (ret < 0)
+ {
+ GTP_DEBUG("failed to set doze flag into 0x8046, %d", retry);
+ continue;
+ }
+ i2c_control_buf[0] = 0x80;
+ i2c_control_buf[1] = 0x40;
+ ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
+ if (ret > 0)
+ {
+ doze_status = DOZE_ENABLED;
+ GTP_INFO("GTP has been working in doze mode!");
+ gtp_irq_enable(ts);
+ return ret;
+ }
+ msleep(10);
+ }
+ GTP_ERROR("GTP send doze cmd failed.");
+ gtp_irq_enable(ts);
+ return ret;
+}
+#else
+/*******************************************************
+Function:
+ Enter sleep mode.
+Input:
+ ts: private data.
+Output:
+ Executive outcomes.
+ 1: succeed, otherwise failed.
+*******************************************************/
+static s8 gtp_enter_sleep(struct goodix_ts_data * ts)
+{
+ s8 ret = -1;
+ s8 retry = 0;
+ u8 i2c_control_buf[3] = {(u8)(GTP_REG_SLEEP >> 8), (u8)GTP_REG_SLEEP, 5};
+
+ GTP_DEBUG_FUNC();
+
+ GTP_GPIO_OUTPUT(GTP_INT_PORT, 0);
+ msleep(5);
+
+ while(retry++ < 5)
+ {
+ ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
+ if (ret > 0)
+ {
+ GTP_INFO("GTP enter sleep!");
+
+ return ret;
+ }
+ msleep(10);
+ }
+ GTP_ERROR("GTP send sleep cmd failed.");
+ return ret;
+}
+#endif
+/*******************************************************
+Function:
+ Wakeup from sleep.
+Input:
+ ts: private data.
+Output:
+ Executive outcomes.
+ >0: succeed, otherwise: failed.
+*******************************************************/
+static s8 gtp_wakeup_sleep(struct goodix_ts_data * ts)
+{
+ u8 retry = 0;
+ s8 ret = -1;
+
+ GTP_DEBUG_FUNC();
+
+#if GTP_POWER_CTRL_SLEEP
+ while(retry++ < 5)
+ {
+ gtp_reset_guitar(ts->client, 20);
+
+ ret = gtp_send_cfg(ts->client);
+ if (ret < 0)
+ {
+ GTP_INFO("Wakeup sleep send config failed!");
+ continue;
+ }
+ GTP_INFO("GTP wakeup sleep");
+ return 1;
+ }
+#else
+ while(retry++ < 10)
+ {
+ #if GTP_SLIDE_WAKEUP
+ if (DOZE_WAKEUP != doze_status) // wakeup not by slide
+ {
+ gtp_reset_guitar(ts->client, 10);
+ }
+ else // wakeup by slide
+ {
+ doze_status = DOZE_DISABLED;
+ }
+ #else
+ if (chip_gt9xxs == 1)
+ {
+ gtp_reset_guitar(ts->client, 10);
+ }
+ else
+ {
+ GTP_GPIO_OUTPUT(GTP_INT_PORT, 1);
+ msleep(5);
+ }
+ #endif
+ ret = gtp_i2c_test(ts->client);
+ if (ret > 0)
+ {
+ GTP_INFO("GTP wakeup sleep.");
+
+ #if (!GTP_SLIDE_WAKEUP)
+ if (chip_gt9xxs == 0)
+ {
+ gtp_int_sync(25);
+ msleep(20);
+ #if GTP_ESD_PROTECT
+ gtp_init_ext_watchdog(ts->client);
+ #endif
+ }
+ #endif
+ return ret;
+ }
+ gtp_reset_guitar(ts->client, 20);
+ }
+#endif
+
+ GTP_ERROR("GTP wakeup sleep failed.");
+ return ret;
+}
+
+/*******************************************************
+Function:
+ Initialize gtp.
+Input:
+ ts: goodix private data
+Output:
+ Executive outcomes.
+ 0: succeed, otherwise: failed
+*******************************************************/
+static s32 gtp_init_panel(struct goodix_ts_data *ts)
+{
+ s32 ret = -1;
+
+#if GTP_DRIVER_SEND_CFG
+ s32 i;
+ u8 check_sum = 0;
+ u8 opr_buf[16];
+ u8 sensor_id = 0;
+
+ u8 cfg_info_group1[] = CTP_CFG_GROUP1;
+ u8 cfg_info_group2[] = CTP_CFG_GROUP2;
+ u8 cfg_info_group3[] = CTP_CFG_GROUP3;
+ u8 cfg_info_group4[] = CTP_CFG_GROUP4;
+ u8 cfg_info_group5[] = CTP_CFG_GROUP5;
+ u8 cfg_info_group6[] = CTP_CFG_GROUP6;
+ u8 *send_cfg_buf[] = {cfg_info_group1, cfg_info_group2, cfg_info_group3,
+ cfg_info_group4, cfg_info_group5, cfg_info_group6};
+ u8 cfg_info_len[] = { CFG_GROUP_LEN(cfg_info_group1),
+ CFG_GROUP_LEN(cfg_info_group2),
+ CFG_GROUP_LEN(cfg_info_group3),
+ CFG_GROUP_LEN(cfg_info_group4),
+ CFG_GROUP_LEN(cfg_info_group5),
+ CFG_GROUP_LEN(cfg_info_group6)};
+
+ GTP_DEBUG("Config Groups\' Lengths: %d, %d, %d, %d, %d, %d",
+ cfg_info_len[0], cfg_info_len[1], cfg_info_len[2], cfg_info_len[3],
+ cfg_info_len[4], cfg_info_len[5]);
+
+ ret = gtp_i2c_read_dbl_check(ts->client, 0x41E4, opr_buf, 1);
+ if (SUCCESS == ret)
+ {
+ if (opr_buf[0] != 0xBE)
+ {
+ ts->fw_error = 1;
+ GTP_ERROR("Firmware error, no config sent!");
+ return -1;
+ }
+ }
+
+ if ((!cfg_info_len[1]) && (!cfg_info_len[2]) &&
+ (!cfg_info_len[3]) && (!cfg_info_len[4]) &&
+ (!cfg_info_len[5]))
+ {
+ sensor_id = 0;
+ }
+ else
+ {
+ ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_SENSOR_ID, &sensor_id, 1);
+ if (SUCCESS == ret)
+ {
+ if (sensor_id >= 0x06)
+ {
+ GTP_ERROR("Invalid sensor_id(0x%02X), No Config Sent!", sensor_id);
+ return -1;
+ }
+ }
+ else
+ {
+ GTP_ERROR("Failed to get sensor_id, No config sent!");
+ return -1;
+ }
+ }
+ GTP_DEBUG("Sensor_ID: %d", sensor_id);
+
+ ts->gtp_cfg_len = cfg_info_len[sensor_id];
+
+ if (ts->gtp_cfg_len < GTP_CONFIG_MIN_LENGTH)
+ {
+ GTP_ERROR("Sensor_ID(%d) matches with NULL or INVALID CONFIG GROUP! NO Config Sent! You need to check you header file CFG_GROUP section!", sensor_id);
+ return -1;
+ }
+
+ ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA, &opr_buf[0], 1);
+
+ if (ret == SUCCESS)
+ {
+ GTP_DEBUG("CFG_GROUP%d Config Version: %d, 0x%02X; IC Config Version: %d, 0x%02X", sensor_id+1,
+ send_cfg_buf[sensor_id][0], send_cfg_buf[sensor_id][0], opr_buf[0], opr_buf[0]);
+
+ if (opr_buf[0] < 90)
+ {
+ grp_cfg_version = send_cfg_buf[sensor_id][0]; // backup group config version
+ send_cfg_buf[sensor_id][0] = 0x00;
+ ts->fixed_cfg = 0;
+ }
+ else // treated as fixed config, not send config
+ {
+ GTP_INFO("Ic fixed config with config version(%d, 0x%02X)", opr_buf[0], opr_buf[0]);
+ ts->fixed_cfg = 1;
+ }
+ }
+ else
+ {
+ GTP_ERROR("Failed to get ic config version!No config sent!");
+ return -1;
+ }
+
+ memset(&config[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH);
+ memcpy(&config[GTP_ADDR_LENGTH], send_cfg_buf[sensor_id], ts->gtp_cfg_len);
+
+#if GTP_CUSTOM_CFG
+ config[RESOLUTION_LOC] = (u8)GTP_MAX_WIDTH;
+ config[RESOLUTION_LOC + 1] = (u8)(GTP_MAX_WIDTH>>8);
+ config[RESOLUTION_LOC + 2] = (u8)GTP_MAX_HEIGHT;
+ config[RESOLUTION_LOC + 3] = (u8)(GTP_MAX_HEIGHT>>8);
+
+ if (GTP_INT_TRIGGER == 0) //RISING
+ {
+ config[TRIGGER_LOC] &= 0xfe;
+ }
+ else if (GTP_INT_TRIGGER == 1) //FALLING
+ {
+ config[TRIGGER_LOC] |= 0x01;
+ }
+#endif // GTP_CUSTOM_CFG
+
+ check_sum = 0;
+ for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++)
+ {
+ check_sum += config[i];
+ }
+ config[ts->gtp_cfg_len] = (~check_sum) + 1;
+
+#else // DRIVER NOT SEND CONFIG
+ ts->gtp_cfg_len = GTP_CONFIG_MAX_LENGTH;
+ ret = gtp_i2c_read(ts->client, config, ts->gtp_cfg_len + GTP_ADDR_LENGTH);
+ if (ret < 0)
+ {
+ GTP_ERROR("Read Config Failed, Using Default Resolution & INT Trigger!");
+ ts->abs_x_max = GTP_MAX_WIDTH;
+ ts->abs_y_max = GTP_MAX_HEIGHT;
+ ts->int_trigger_type = GTP_INT_TRIGGER;
+ }
+#endif // GTP_DRIVER_SEND_CFG
+
+ GTP_DEBUG_FUNC();
+ if ((ts->abs_x_max == 0) && (ts->abs_y_max == 0))
+ {
+ ts->abs_x_max = (config[RESOLUTION_LOC + 1] << 8) + config[RESOLUTION_LOC];
+ ts->abs_y_max = (config[RESOLUTION_LOC + 3] << 8) + config[RESOLUTION_LOC + 2];
+ ts->int_trigger_type = (config[TRIGGER_LOC]) & 0x03;
+ }
+ ret = gtp_send_cfg(ts->client);
+ if (ret < 0)
+ {
+ GTP_ERROR("Send config error.");
+ }
+ GTP_DEBUG("X_MAX = %d, Y_MAX = %d, TRIGGER = 0x%02x",
+ ts->abs_x_max,ts->abs_y_max,ts->int_trigger_type);
+
+ msleep(10);
+ return 0;
+}
+
+/*******************************************************
+Function:
+ Read chip version.
+Input:
+ client: i2c device
+ version: buffer to keep ic firmware version
+Output:
+ read operation return.
+ 2: succeed, otherwise: failed
+*******************************************************/
+s32 gtp_read_version(struct i2c_client *client, u16* version)
+{
+ s32 ret = -1;
+ u8 buf[8] = {GTP_REG_VERSION >> 8, GTP_REG_VERSION & 0xff};
+
+ GTP_DEBUG_FUNC();
+
+ ret = gtp_i2c_read(client, buf, sizeof(buf));
+ if (ret < 0)
+ {
+ GTP_ERROR("GTP read version failed");
+ return ret;
+ }
+
+ if (version)
+ {
+ *version = (buf[7] << 8) | buf[6];
+ }
+
+ if (buf[5] == 0x00)
+ {
+ GTP_INFO("IC Version: %c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[7], buf[6]);
+ }
+ else
+ {
+ if (buf[5] == 'S' || buf[5] == 's')
+ {
+ chip_gt9xxs = 1;
+ }
+ GTP_INFO("IC Version: %c%c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[5], buf[7], buf[6]);
+ }
+ return ret;
+}
+
+/*******************************************************
+Function:
+ I2c test Function.
+Input:
+ client:i2c client.
+Output:
+ Executive outcomes.
+ 2: succeed, otherwise failed.
+*******************************************************/
+static s8 gtp_i2c_test(struct i2c_client *client)
+{
+ u8 test[3] = {GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff};
+ u8 retry = 0;
+ s8 ret = -1;
+
+ GTP_DEBUG_FUNC();
+
+ while(retry++ < 5)
+ {
+ ret = gtp_i2c_read(client, test, 3);
+ if (ret > 0)
+ {
+ return ret;
+ }
+ GTP_ERROR("GTP i2c test failed time %d.",retry);
+ msleep(10);
+ }
+ return ret;
+}
+
+/*******************************************************
+Function:
+ Request gpio(INT & RST) ports.
+Input:
+ ts: private data.
+Output:
+ Executive outcomes.
+ >= 0: succeed, < 0: failed
+*******************************************************/
+static s8 gtp_request_io_port(struct goodix_ts_data *ts)
+{
+ s32 ret = 0;
+
+ ret = GTP_GPIO_REQUEST(GTP_INT_PORT, "GTP_INT_IRQ");
+ if (ret < 0)
+ {
+ GTP_ERROR("Failed to request GPIO:%d, ERRNO:%d", (s32)GTP_INT_PORT, ret);
+ ret = -ENODEV;
+ }
+ else
+ {
+ GTP_GPIO_AS_INT(GTP_INT_PORT);
+ ts->client->irq = GTP_INT_IRQ;
+ }
+
+ ret = GTP_GPIO_REQUEST(GTP_RST_PORT, "GTP_RST_PORT");
+ if (ret < 0)
+ {
+ GTP_ERROR("Failed to request GPIO:%d, ERRNO:%d",(s32)GTP_RST_PORT,ret);
+ ret = -ENODEV;
+ }
+
+ GTP_GPIO_AS_INPUT(GTP_RST_PORT);
+ gtp_reset_guitar(ts->client, 20);
+
+
+ if(ret < 0)
+ {
+ GTP_GPIO_FREE(GTP_RST_PORT);
+ GTP_GPIO_FREE(GTP_INT_PORT);
+ }
+
+ return ret;
+}
+
+/*******************************************************
+Function:
+ Request interrupt.
+Input:
+ ts: private data.
+Output:
+ Executive outcomes.
+ 0: succeed, -1: failed.
+*******************************************************/
+static s8 gtp_request_irq(struct goodix_ts_data *ts)
+{
+ s32 ret = -1;
+ const u8 irq_table[] = GTP_IRQ_TAB;
+
+ GTP_DEBUG("INT trigger type:%x", ts->int_trigger_type);
+
+ ret = request_irq(ts->client->irq,
+ goodix_ts_irq_handler,
+ irq_table[ts->int_trigger_type],
+ ts->client->name,
+ ts);
+ if (ret)
+ {
+ GTP_ERROR("Request IRQ failed!ERRNO:%d.", ret);
+ GTP_GPIO_AS_INPUT(GTP_INT_PORT);
+ GTP_GPIO_FREE(GTP_INT_PORT);
+
+ hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ts->timer.function = goodix_ts_timer_handler;
+ hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+ return -1;
+ }
+ else
+ {
+ gtp_irq_disable(ts);
+ ts->use_irq = 1;
+ return 0;
+ }
+}
+
+/*******************************************************
+Function:
+ Request input device Function.
+Input:
+ ts:private data.
+Output:
+ Executive outcomes.
+ 0: succeed, otherwise: failed.
+*******************************************************/
+static s8 gtp_request_input_dev(struct goodix_ts_data *ts)
+{
+ s8 ret = -1;
+ s8 phys[32];
+#if GTP_HAVE_TOUCH_KEY
+ u8 index = 0;
+#endif
+
+ GTP_DEBUG_FUNC();
+
+ ts->input_dev = input_allocate_device();
+ if (ts->input_dev == NULL)
+ {
+ GTP_ERROR("Failed to allocate input device.");
+ return -ENOMEM;
+ }
+
+ ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) ;
+#if GTP_ICS_SLOT_REPORT
+ __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
+ input_mt_init_slots(ts->input_dev, 10); // in case of "out of memory"
+#else
+ ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+#endif
+
+#if GTP_HAVE_TOUCH_KEY
+ for (index = 0; index < GTP_MAX_KEY_NUM; index++)
+ {
+ input_set_capability(ts->input_dev, EV_KEY, touch_key_array[index]);
+ }
+#endif
+
+#if GTP_SLIDE_WAKEUP
+ input_set_capability(ts->input_dev, EV_KEY, KEY_POWER);
+#endif
+
+#if GTP_WITH_PEN
+ // pen support
+ __set_bit(BTN_TOOL_PEN, ts->input_dev->keybit);
+ __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
+ __set_bit(INPUT_PROP_POINTER, ts->input_dev->propbit);
+#endif
+
+#if GTP_CHANGE_X2Y
+ GTP_SWAP(ts->abs_x_max, ts->abs_y_max);
+#endif
+
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, ts->abs_x_max, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, ts->abs_y_max, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, 255, 0, 0);
+
+ sprintf(phys, "input/ts");
+ ts->input_dev->name = goodix_ts_name;
+ ts->input_dev->phys = phys;
+ ts->input_dev->id.bustype = BUS_I2C;
+ ts->input_dev->id.vendor = 0xDEAD;
+ ts->input_dev->id.product = 0xBEEF;
+ ts->input_dev->id.version = 10427;
+
+ ret = input_register_device(ts->input_dev);
+ if (ret)
+ {
+ GTP_ERROR("Register %s input device failed", ts->input_dev->name);
+ return -ENODEV;
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ts->early_suspend.suspend = goodix_ts_early_suspend;
+ ts->early_suspend.resume = goodix_ts_late_resume;
+ register_early_suspend(&ts->early_suspend);
+#endif
+
+ return 0;
+}
+
+/*******************************************************
+Function:
+ I2c probe.
+Input:
+ client: i2c device struct.
+ id: device id.
+Output:
+ Executive outcomes.
+ 0: succeed.
+*******************************************************/
+static int goodix_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ s32 ret = -1;
+ struct goodix_ts_data *ts;
+ u16 version_info;
+
+ GTP_DEBUG_FUNC();
+
+ //do NOT remove these logs
+ GTP_INFO("GTP Driver Version: %s", GTP_DRIVER_VERSION);
+ GTP_INFO("GTP Driver Built@%s, %s", __TIME__, __DATE__);
+ GTP_INFO("GTP I2C Address: 0x%02x", client->addr);
+
+ i2c_connect_client = client;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ {
+ GTP_ERROR("I2C check functionality failed.");
+ return -ENODEV;
+ }
+ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+ if (ts == NULL)
+ {
+ GTP_ERROR("Alloc GFP_KERNEL memory failed.");
+ return -ENOMEM;
+ }
+
+ memset(ts, 0, sizeof(*ts));
+ INIT_WORK(&ts->work, goodix_ts_work_func);
+ ts->client = client;
+ spin_lock_init(&ts->irq_lock); // 2.6.39 later
+ // ts->irq_lock = SPIN_LOCK_UNLOCKED; // 2.6.39 & before
+ i2c_set_clientdata(client, ts);
+
+ ts->gtp_rawdiff_mode = 0;
+
+ ret = gtp_request_io_port(ts);
+ if (ret < 0)
+ {
+ GTP_ERROR("GTP request IO port failed.");
+ kfree(ts);
+ return ret;
+ }
+
+ ret = gtp_i2c_test(client);
+ if (ret < 0)
+ {
+ GTP_ERROR("I2C communication ERROR!");
+ }
+
+#if GTP_AUTO_UPDATE
+ ret = gup_init_update_proc(ts);
+ if (ret < 0)
+ {
+ GTP_ERROR("Create update thread error.");
+ }
+#endif
+
+ ret = gtp_init_panel(ts);
+ if (ret < 0)
+ {
+ GTP_ERROR("GTP init panel failed.");
+ ts->abs_x_max = GTP_MAX_WIDTH;
+ ts->abs_y_max = GTP_MAX_HEIGHT;
+ ts->int_trigger_type = GTP_INT_TRIGGER;
+ }
+
+ ret = gtp_request_input_dev(ts);
+ if (ret < 0)
+ {
+ GTP_ERROR("GTP request input dev failed");
+ }
+
+ ret = gtp_request_irq(ts);
+ if (ret < 0)
+ {
+ GTP_INFO("GTP works in polling mode.");
+ }
+ else
+ {
+ GTP_INFO("GTP works in interrupt mode.");
+ }
+
+ ret = gtp_read_version(client, &version_info);
+ if (ret < 0)
+ {
+ GTP_ERROR("Read version failed.");
+ }
+ if (ts->use_irq)
+ {
+ gtp_irq_enable(ts);
+ }
+
+#if GTP_CREATE_WR_NODE
+ init_wr_node(client);
+#endif
+
+#if GTP_ESD_PROTECT
+ gtp_esd_switch(client, SWITCH_ON);
+#endif
+ return 0;
+}
+
+
+/*******************************************************
+Function:
+ Goodix touchscreen driver release function.
+Input:
+ client: i2c device struct.
+Output:
+ Executive outcomes. 0---succeed.
+*******************************************************/
+static int goodix_ts_remove(struct i2c_client *client)
+{
+ struct goodix_ts_data *ts = i2c_get_clientdata(client);
+
+ GTP_DEBUG_FUNC();
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&ts->early_suspend);
+#endif
+
+#if GTP_CREATE_WR_NODE
+ uninit_wr_node();
+#endif
+
+#if GTP_ESD_PROTECT
+ destroy_workqueue(gtp_esd_check_workqueue);
+#endif
+
+ if (ts)
+ {
+ if (ts->use_irq)
+ {
+ GTP_GPIO_AS_INPUT(GTP_INT_PORT);
+ GTP_GPIO_FREE(GTP_INT_PORT);
+ free_irq(client->irq, ts);
+ }
+ else
+ {
+ hrtimer_cancel(&ts->timer);
+ }
+ }
+
+ GTP_INFO("GTP driver removing...");
+ i2c_set_clientdata(client, NULL);
+ input_unregister_device(ts->input_dev);
+ kfree(ts);
+
+ return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+/*******************************************************
+Function:
+ Early suspend function.
+Input:
+ h: early_suspend struct.
+Output:
+ None.
+*******************************************************/
+static void goodix_ts_early_suspend(struct early_suspend *h)
+{
+ struct goodix_ts_data *ts;
+ s8 ret = -1;
+ ts = container_of(h, struct goodix_ts_data, early_suspend);
+
+ GTP_DEBUG_FUNC();
+
+#if GTP_ESD_PROTECT
+ ts->gtp_is_suspend = 1;
+ gtp_esd_switch(ts->client, SWITCH_OFF);
+#endif
+
+#if GTP_SLIDE_WAKEUP
+ ret = gtp_enter_doze(ts);
+#else
+ if (ts->use_irq)
+ {
+ gtp_irq_disable(ts);
+ }
+ else
+ {
+ hrtimer_cancel(&ts->timer);
+ }
+ ret = gtp_enter_sleep(ts);
+#endif
+ if (ret < 0)
+ {
+ GTP_ERROR("GTP early suspend failed.");
+ }
+ // to avoid waking up while not sleeping
+ // delay 48 + 10ms to ensure reliability
+ msleep(58);
+}
+
+/*******************************************************
+Function:
+ Late resume function.
+Input:
+ h: early_suspend struct.
+Output:
+ None.
+*******************************************************/
+static void goodix_ts_late_resume(struct early_suspend *h)
+{
+ struct goodix_ts_data *ts;
+ s8 ret = -1;
+ ts = container_of(h, struct goodix_ts_data, early_suspend);
+
+ GTP_DEBUG_FUNC();
+
+ ret = gtp_wakeup_sleep(ts);
+
+#if GTP_SLIDE_WAKEUP
+ doze_status = DOZE_DISABLED;
+#endif
+
+ if (ret < 0)
+ {
+ GTP_ERROR("GTP later resume failed.");
+ }
+
+ if (ts->use_irq)
+ {
+ gtp_irq_enable(ts);
+ }
+ else
+ {
+ hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+ }
+
+#if GTP_ESD_PROTECT
+ ts->gtp_is_suspend = 0;
+ gtp_esd_switch(ts->client, SWITCH_ON);
+#endif
+}
+#endif
+
+#if GTP_ESD_PROTECT
+/*******************************************************
+Function:
+ switch on & off esd delayed work
+Input:
+ client: i2c device
+ on: SWITCH_ON / SWITCH_OFF
+Output:
+ void
+*********************************************************/
+void gtp_esd_switch(struct i2c_client *client, s32 on)
+{
+ struct goodix_ts_data *ts;
+
+ ts = i2c_get_clientdata(client);
+ if (SWITCH_ON == on) // switch on esd
+ {
+ if (!ts->esd_running)
+ {
+ ts->esd_running = 1;
+ GTP_INFO("Esd started");
+ queue_delayed_work(gtp_esd_check_workqueue, &gtp_esd_check_work, GTP_ESD_CHECK_CIRCLE);
+ }
+ }
+ else // switch off esd
+ {
+ if (ts->esd_running)
+ {
+ ts->esd_running = 0;
+ GTP_INFO("Esd cancelled");
+ cancel_delayed_work_sync(&gtp_esd_check_work);
+ }
+ }
+}
+
+/*******************************************************
+Function:
+ Initialize external watchdog for esd protect
+Input:
+ client: i2c device.
+Output:
+ result of i2c write operation.
+ 1: succeed, otherwise: failed
+*********************************************************/
+static s32 gtp_init_ext_watchdog(struct i2c_client *client)
+{
+ u8 opr_buffer[4] = {0x80, 0x40, 0xAA, 0xAA};
+
+ struct i2c_msg msg; // in case of recursively reset by calling gtp_i2c_write
+ s32 ret = -1;
+ s32 retries = 0;
+
+ GTP_DEBUG("Init external watchdog...");
+ GTP_DEBUG_FUNC();
+
+ msg.flags = !I2C_M_RD;
+ msg.addr = client->addr;
+ msg.len = 4;
+ msg.buf = opr_buffer;
+
+ while(retries < 5)
+ {
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret == 1)
+ {
+ return 1;
+ }
+ retries++;
+ }
+ if (retries >= 5)
+ {
+ GTP_ERROR("init external watchdog failed!");
+ }
+ return 0;
+}
+
+/*******************************************************
+Function:
+ Esd protect function.
+ Added external watchdog by meta, 2013/03/07
+Input:
+ work: delayed work
+Output:
+ None.
+*******************************************************/
+static void gtp_esd_check_func(struct work_struct *work)
+{
+ s32 i;
+ s32 ret = -1;
+ struct goodix_ts_data *ts = NULL;
+ u8 test[4] = {0x80, 0x40};
+
+ GTP_DEBUG_FUNC();
+
+ ts = i2c_get_clientdata(i2c_connect_client);
+
+ if (ts->gtp_is_suspend)
+ {
+ ts->esd_running = 0;
+ GTP_INFO("Esd terminated!");
+ return;
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ ret = gtp_i2c_read(ts->client, test, 4);
+
+ GTP_DEBUG("0x8040 = 0x%02X, 0x8041 = 0x%02X", test[2], test[3]);
+ if ((ret < 0))
+ {
+ // IIC communication problem
+ continue;
+ }
+ else
+ {
+ if ((test[2] == 0xAA) || (test[3] != 0xAA))
+ {
+ // IC works abnormally..
+ i = 3;
+ break;
+ }
+ else
+ {
+ // IC works normally, Write 0x8040 0xAA, feed the dog
+ test[2] = 0xAA;
+ gtp_i2c_write(ts->client, test, 3);
+ break;
+ }
+ }
+ }
+ if (i >= 3)
+ {
+ GTP_ERROR("IC Working ABNORMALLY, Resetting Guitar...");
+ gtp_reset_guitar(ts->client, 50);
+ }
+
+ if(!ts->gtp_is_suspend)
+ {
+ queue_delayed_work(gtp_esd_check_workqueue, &gtp_esd_check_work, GTP_ESD_CHECK_CIRCLE);
+ }
+ else
+ {
+ GTP_INFO("Esd terminated!");
+ ts->esd_running = 0;
+ }
+ return;
+}
+#endif
+
+static const struct i2c_device_id goodix_ts_id[] = {
+ { GTP_I2C_NAME, 0 },
+ { }
+};
+
+static struct i2c_driver goodix_ts_driver = {
+ .probe = goodix_ts_probe,
+ .remove = goodix_ts_remove,
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ .suspend = goodix_ts_early_suspend,
+ .resume = goodix_ts_late_resume,
+#endif
+ .id_table = goodix_ts_id,
+ .driver = {
+ .name = GTP_I2C_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+/*******************************************************
+Function:
+ Driver Install function.
+Input:
+ None.
+Output:
+ Executive Outcomes. 0---succeed.
+********************************************************/
+static int __devinit goodix_ts_init(void)
+{
+ s32 ret;
+
+ GTP_DEBUG_FUNC();
+ GTP_INFO("GTP driver installing...");
+ goodix_wq = create_singlethread_workqueue("goodix_wq");
+ if (!goodix_wq)
+ {
+ GTP_ERROR("Creat workqueue failed.");
+ return -ENOMEM;
+ }
+#if GTP_ESD_PROTECT
+ INIT_DELAYED_WORK(&gtp_esd_check_work, gtp_esd_check_func);
+ gtp_esd_check_workqueue = create_workqueue("gtp_esd_check");
+#endif
+ ret = i2c_add_driver(&goodix_ts_driver);
+ return ret;
+}
+
+/*******************************************************
+Function:
+ Driver uninstall function.
+Input:
+ None.
+Output:
+ Executive Outcomes. 0---succeed.
+********************************************************/
+static void __exit goodix_ts_exit(void)
+{
+ GTP_DEBUG_FUNC();
+ GTP_INFO("GTP driver exited.");
+ i2c_del_driver(&goodix_ts_driver);
+ if (goodix_wq)
+ {
+ destroy_workqueue(goodix_wq);
+ }
+}
+
+late_initcall(goodix_ts_init);
+module_exit(goodix_ts_exit);
+
+MODULE_DESCRIPTION("GTP Series Driver");
+MODULE_LICENSE("GPL");
diff --git a/input/touchscreen/gt9xx/gt9xx.h b/input/touchscreen/gt9xx/gt9xx.h
new file mode 100644
index 000000000000..e375af530d57
--- /dev/null
+++ b/input/touchscreen/gt9xx/gt9xx.h
@@ -0,0 +1,241 @@
+/* drivers/input/touchscreen/gt9xx.h
+ *
+ * 2010 - 2013 Goodix Technology.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be a reference
+ * to you, when you are integrating the GOODiX's CTP IC into your system,
+ * 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 _GOODIX_GT9XX_H_
+#define _GOODIX_GT9XX_H_
+
+#include <linux/kernel.h>
+#include <linux/hrtimer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <mach/gpio.h>
+#include <linux/earlysuspend.h>
+
+struct goodix_ts_data {
+ spinlock_t irq_lock;
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct hrtimer timer;
+ struct work_struct work;
+ struct early_suspend early_suspend;
+ s32 irq_is_disable;
+ s32 use_irq;
+ u16 abs_x_max;
+ u16 abs_y_max;
+ u8 max_touch_num;
+ u8 int_trigger_type;
+ u8 green_wake_mode;
+ u8 chip_type;
+ u8 enter_update;
+ u8 gtp_is_suspend;
+ u8 gtp_rawdiff_mode;
+ u8 gtp_cfg_len;
+ u8 fixed_cfg;
+ u8 esd_running;
+ u8 fw_error;
+};
+
+extern u16 show_len;
+extern u16 total_len;
+
+//***************************PART1:ON/OFF define*******************************
+#define GTP_CUSTOM_CFG 0
+#define GTP_CHANGE_X2Y 0
+#define GTP_DRIVER_SEND_CFG 1
+#define GTP_HAVE_TOUCH_KEY 0
+#define GTP_POWER_CTRL_SLEEP 0
+#define GTP_ICS_SLOT_REPORT 0
+
+#define GTP_AUTO_UPDATE 1 // auto updated by .bin file as default
+#define GTP_HEADER_FW_UPDATE 0 // auto updated by head_fw_array in gt9xx_firmware.h, function together with GTP_AUTO_UPDATE
+
+#define GTP_CREATE_WR_NODE 1
+#define GTP_ESD_PROTECT 0
+#define GTP_WITH_PEN 0
+
+#define GTP_SLIDE_WAKEUP 0
+#define GTP_DBL_CLK_WAKEUP 0 // double-click wakeup, function together with GTP_SLIDE_WAKEUP
+
+#define GTP_DEBUG_ON 1
+#define GTP_DEBUG_ARRAY_ON 0
+#define GTP_DEBUG_FUNC_ON 0
+
+//*************************** PART2:TODO define **********************************
+// STEP_1(REQUIRED): Define Configuration Information Group(s)
+// Sensor_ID Map:
+/* sensor_opt1 sensor_opt2 Sensor_ID
+ GND GND 0
+ VDDIO GND 1
+ NC GND 2
+ GND NC/300K 3
+ VDDIO NC/300K 4
+ NC NC/300K 5
+*/
+// TODO: define your own default or for Sensor_ID == 0 config here.
+// The predefined one is just a sample config, which is not suitable for your tp in most cases.
+#define CTP_CFG_GROUP1 {\
+ 0x41,0x1C,0x02,0xC0,0x03,0x0A,0x05,0x01,0x01,0x0F,\
+ 0x23,0x0F,0x5F,0x41,0x03,0x05,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x91,0x00,0x0A,\
+ 0x28,0x00,0xB8,0x0B,0x00,0x00,0x00,0x9A,0x03,0x25,\
+ 0x00,0x00,0x00,0x00,0x00,0x03,0x64,0x32,0x00,0x00,\
+ 0x00,0x32,0x8C,0x94,0x05,0x01,0x05,0x00,0x00,0x96,\
+ 0x0C,0x22,0xD8,0x0E,0x23,0x56,0x11,0x25,0xFF,0x13,\
+ 0x28,0xA7,0x15,0x2E,0x00,0x00,0x10,0x30,0x48,0x00,\
+ 0x56,0x4A,0x3A,0xFF,0xFF,0x16,0x00,0x00,0x00,0x00,\
+ 0x00,0x01,0x1B,0x14,0x0D,0x19,0x00,0x00,0x01,0x00,\
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,0x1A,0x18,0x16,0x14,0x12,0x10,0x0E,0x0C,\
+ 0x0A,0x08,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,\
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,\
+ 0xFF,0xFF,0x1D,0x1E,0x1F,0x20,0x22,0x24,0x28,0x29,\
+ 0x0C,0x0A,0x08,0x00,0x02,0x04,0x05,0x06,0x0E,0xFF,\
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,\
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,\
+ 0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x91,0x01\
+ }
+
+// TODO: define your config for Sensor_ID == 1 here, if needed
+#define CTP_CFG_GROUP2 {\
+ }
+// TODO: define your config for Sensor_ID == 2 here, if needed
+#define CTP_CFG_GROUP3 {\
+ }
+
+// TODO: define your config for Sensor_ID == 3 here, if needed
+#define CTP_CFG_GROUP4 {\
+ }
+
+// TODO: define your config for Sensor_ID == 4 here, if needed
+#define CTP_CFG_GROUP5 {\
+ }
+
+// TODO: define your config for Sensor_ID == 5 here, if needed
+#define CTP_CFG_GROUP6 {\
+ }
+
+// STEP_2(REQUIRED): Customize your I/O ports & I/O operations
+#define GTP_RST_PORT S5PV210_GPJ3(6)
+#define GTP_INT_PORT S5PV210_GPH1(3)
+#define GTP_INT_IRQ gpio_to_irq(GTP_INT_PORT)
+#define GTP_INT_CFG S3C_GPIO_SFN(0xF)
+
+#define GTP_GPIO_AS_INPUT(pin) do{\
+ gpio_direction_input(pin);\
+ s3c_gpio_setpull(pin, S3C_GPIO_PULL_NONE);\
+ }while(0)
+#define GTP_GPIO_AS_INT(pin) do{\
+ GTP_GPIO_AS_INPUT(pin);\
+ s3c_gpio_cfgpin(pin, GTP_INT_CFG);\
+ }while(0)
+#define GTP_GPIO_GET_VALUE(pin) gpio_get_value(pin)
+#define GTP_GPIO_OUTPUT(pin,level) gpio_direction_output(pin,level)
+#define GTP_GPIO_REQUEST(pin, label) gpio_request(pin, label)
+#define GTP_GPIO_FREE(pin) gpio_free(pin)
+#define GTP_IRQ_TAB {IRQ_TYPE_EDGE_RISING, IRQ_TYPE_EDGE_FALLING, IRQ_TYPE_LEVEL_LOW, IRQ_TYPE_LEVEL_HIGH}
+
+// STEP_3(optional): Specify your special config info if needed
+#if GTP_CUSTOM_CFG
+ #define GTP_MAX_HEIGHT 800
+ #define GTP_MAX_WIDTH 480
+ #define GTP_INT_TRIGGER 0 // 0: Rising 1: Falling
+#else
+ #define GTP_MAX_HEIGHT 4096
+ #define GTP_MAX_WIDTH 4096
+ #define GTP_INT_TRIGGER 1
+#endif
+#define GTP_MAX_TOUCH 5
+#define GTP_ESD_CHECK_CIRCLE 2000 // jiffy: ms
+
+// STEP_4(optional): If keys are available and reported as keys, config your key info here
+#if GTP_HAVE_TOUCH_KEY
+ #define GTP_KEY_TAB {KEY_MENU, KEY_HOME, KEY_BACK}
+#endif
+
+//***************************PART3:OTHER define*********************************
+#define GTP_DRIVER_VERSION "V1.8<2013/06/08>"
+#define GTP_I2C_NAME "Goodix-TS"
+#define GTP_POLL_TIME 10 // jiffy: ms
+#define GTP_ADDR_LENGTH 2
+#define GTP_CONFIG_MIN_LENGTH 186
+#define GTP_CONFIG_MAX_LENGTH 240
+#define FAIL 0
+#define SUCCESS 1
+#define SWITCH_OFF 0
+#define SWITCH_ON 1
+
+// Registers define
+#define GTP_READ_COOR_ADDR 0x814E
+#define GTP_REG_SLEEP 0x8040
+#define GTP_REG_SENSOR_ID 0x814A
+#define GTP_REG_CONFIG_DATA 0x8047
+#define GTP_REG_VERSION 0x8140
+
+#define RESOLUTION_LOC 3
+#define TRIGGER_LOC 8
+
+#define CFG_GROUP_LEN(p_cfg_grp) (sizeof(p_cfg_grp) / sizeof(p_cfg_grp[0]))
+// Log define
+#define GTP_INFO(fmt,arg...) printk("<<-GTP-INFO->> "fmt"\n",##arg)
+#define GTP_ERROR(fmt,arg...) printk("<<-GTP-ERROR->> "fmt"\n",##arg)
+#define GTP_DEBUG(fmt,arg...) do{\
+ if(GTP_DEBUG_ON)\
+ printk("<<-GTP-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\
+ }while(0)
+#define GTP_DEBUG_ARRAY(array, num) do{\
+ s32 i;\
+ u8* a = array;\
+ if(GTP_DEBUG_ARRAY_ON)\
+ {\
+ printk("<<-GTP-DEBUG-ARRAY->>\n");\
+ for (i = 0; i < (num); i++)\
+ {\
+ printk("%02x ", (a)[i]);\
+ if ((i + 1 ) %10 == 0)\
+ {\
+ printk("\n");\
+ }\
+ }\
+ printk("\n");\
+ }\
+ }while(0)
+#define GTP_DEBUG_FUNC() do{\
+ if(GTP_DEBUG_FUNC_ON)\
+ printk("<<-GTP-FUNC->> Func:%s@Line:%d\n",__func__,__LINE__);\
+ }while(0)
+#define GTP_SWAP(x, y) do{\
+ typeof(x) z = x;\
+ x = y;\
+ y = z;\
+ }while (0)
+
+//*****************************End of Part III********************************
+
+#endif /* _GOODIX_GT9XX_H_ */
diff --git a/input/touchscreen/gt9xx/gt9xx_firmware.h b/input/touchscreen/gt9xx/gt9xx_firmware.h
new file mode 100644
index 000000000000..3998bf0023f8
--- /dev/null
+++ b/input/touchscreen/gt9xx/gt9xx_firmware.h
@@ -0,0 +1,6 @@
+// make sense only when GTP_HEADER_FW_UPDATE & GTP_AUTO_UPDATE are enabled
+// define your own firmware array here
+const unsigned char header_fw_array[] =
+{
+
+}; \ No newline at end of file
diff --git a/input/touchscreen/gt9xx/gt9xx_update.c b/input/touchscreen/gt9xx/gt9xx_update.c
new file mode 100644
index 000000000000..f564a6b3aaed
--- /dev/null
+++ b/input/touchscreen/gt9xx/gt9xx_update.c
@@ -0,0 +1,1930 @@
+/* drivers/input/touchscreen/gt9xx_update.c
+ *
+ * 2010 - 2012 Goodix Technology.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be a reference
+ * to you, when you are integrating the GOODiX's CTP IC into your system,
+ * 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.
+ *
+ * Latest Version:1.6
+ * Author: andrew@goodix.com
+ * Revision Record:
+ * V1.0:
+ * first release. By Andrew, 2012/08/31
+ * V1.2:
+ * add force update,GT9110P pid map. By Andrew, 2012/10/15
+ * V1.4:
+ * 1. add config auto update function;
+ * 2. modify enter_update_mode;
+ * 3. add update file cal checksum.
+ * By Andrew, 2012/12/12
+ * V1.6:
+ * 1. replace guitar_client with i2c_connect_client;
+ * 2. support firmware header array update.
+ * By Meta, 2013/03/11
+ */
+#include <linux/kthread.h>
+#include "gt9xx.h"
+
+#if GTP_HEADER_FW_UPDATE
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include "gt9xx_firmware.h"
+#endif
+
+#define GUP_REG_HW_INFO 0x4220
+#define GUP_REG_FW_MSG 0x41E4
+#define GUP_REG_PID_VID 0x8140
+
+#define GUP_SEARCH_FILE_TIMES 50
+#define UPDATE_FILE_PATH_2 "/data/_goodix_update_.bin"
+#define UPDATE_FILE_PATH_1 "/sdcard/_goodix_update_.bin"
+
+#define CONFIG_FILE_PATH_1 "/data/_goodix_config_.cfg"
+#define CONFIG_FILE_PATH_2 "/sdcard/_goodix_config_.cfg"
+
+#define FW_HEAD_LENGTH 14
+#define FW_SECTION_LENGTH 0x2000
+#define FW_DSP_ISP_LENGTH 0x1000
+#define FW_DSP_LENGTH 0x1000
+#define FW_BOOT_LENGTH 0x800
+
+#define PACK_SIZE 256
+#define MAX_FRAME_CHECK_TIME 5
+
+#define _bRW_MISCTL__SRAM_BANK 0x4048
+#define _bRW_MISCTL__MEM_CD_EN 0x4049
+#define _bRW_MISCTL__CACHE_EN 0x404B
+#define _bRW_MISCTL__TMR0_EN 0x40B0
+#define _rRW_MISCTL__SWRST_B0_ 0x4180
+#define _bWO_MISCTL__CPU_SWRST_PULSE 0x4184
+#define _rRW_MISCTL__BOOTCTL_B0_ 0x4190
+#define _rRW_MISCTL__BOOT_OPT_B0_ 0x4218
+#define _rRW_MISCTL__BOOT_CTL_ 0x5094
+
+#define FAIL 0
+#define SUCCESS 1
+
+#pragma pack(1)
+typedef struct
+{
+ u8 hw_info[4]; //hardware info//
+ u8 pid[8]; //product id //
+ u16 vid; //version id //
+}st_fw_head;
+#pragma pack()
+
+typedef struct
+{
+ u8 force_update;
+ u8 fw_flag;
+ struct file *file;
+ struct file *cfg_file;
+ st_fw_head ic_fw_msg;
+ mm_segment_t old_fs;
+}st_update_msg;
+
+st_update_msg update_msg;
+u16 show_len;
+u16 total_len;
+u8 got_file_flag = 0;
+u8 searching_file = 0;
+extern u8 config[GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH];
+extern void gtp_reset_guitar(struct i2c_client *client, s32 ms);
+extern s32 gtp_send_cfg(struct i2c_client *client);
+extern struct i2c_client * i2c_connect_client;
+extern void gtp_irq_enable(struct goodix_ts_data *ts);
+extern void gtp_irq_disable(struct goodix_ts_data *ts);
+extern s32 gtp_i2c_read_dbl_check(struct i2c_client *, u16, u8 *, int);
+#if GTP_ESD_PROTECT
+extern void gtp_esd_switch(struct i2c_client *, s32);
+#endif
+/*******************************************************
+Function:
+ Read data from the i2c slave device.
+Input:
+ client: i2c device.
+ buf[0~1]: read start address.
+ buf[2~len-1]: read data buffer.
+ len: GTP_ADDR_LENGTH + read bytes count
+Output:
+ numbers of i2c_msgs to transfer:
+ 2: succeed, otherwise: failed
+*********************************************************/
+s32 gup_i2c_read(struct i2c_client *client, u8 *buf, s32 len)
+{
+ struct i2c_msg msgs[2];
+ s32 ret=-1;
+ s32 retries = 0;
+
+ GTP_DEBUG_FUNC();
+
+ msgs[0].flags = !I2C_M_RD;
+ msgs[0].addr = client->addr;
+ msgs[0].len = GTP_ADDR_LENGTH;
+ msgs[0].buf = &buf[0];
+ //msgs[0].scl_rate = 300 * 1000; // for Rockchip
+
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].addr = client->addr;
+ msgs[1].len = len - GTP_ADDR_LENGTH;
+ msgs[1].buf = &buf[GTP_ADDR_LENGTH];
+ //msgs[1].scl_rate = 300 * 1000;
+
+ while(retries < 5)
+ {
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ if(ret == 2)break;
+ retries++;
+ }
+
+ return ret;
+}
+
+/*******************************************************
+Function:
+ Write data to the i2c slave device.
+Input:
+ client: i2c device.
+ buf[0~1]: write start address.
+ buf[2~len-1]: data buffer
+ len: GTP_ADDR_LENGTH + write bytes count
+Output:
+ numbers of i2c_msgs to transfer:
+ 1: succeed, otherwise: failed
+*********************************************************/
+s32 gup_i2c_write(struct i2c_client *client,u8 *buf,s32 len)
+{
+ struct i2c_msg msg;
+ s32 ret=-1;
+ s32 retries = 0;
+
+ GTP_DEBUG_FUNC();
+
+ msg.flags = !I2C_M_RD;
+ msg.addr = client->addr;
+ msg.len = len;
+ msg.buf = buf;
+ //msg.scl_rate = 300 * 1000; // for Rockchip
+
+ while(retries < 5)
+ {
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret == 1)break;
+ retries++;
+ }
+
+ return ret;
+}
+
+static s32 gup_init_panel(struct goodix_ts_data *ts)
+{
+ s32 ret = 0;
+ s32 i = 0;
+ u8 check_sum = 0;
+ u8 opr_buf[16];
+ u8 sensor_id = 0;
+
+ u8 cfg_info_group1[] = CTP_CFG_GROUP1;
+ u8 cfg_info_group2[] = CTP_CFG_GROUP2;
+ u8 cfg_info_group3[] = CTP_CFG_GROUP3;
+ u8 cfg_info_group4[] = CTP_CFG_GROUP4;
+ u8 cfg_info_group5[] = CTP_CFG_GROUP5;
+ u8 cfg_info_group6[] = CTP_CFG_GROUP6;
+ u8 *send_cfg_buf[] = {cfg_info_group1, cfg_info_group2, cfg_info_group3,
+ cfg_info_group4, cfg_info_group5, cfg_info_group6};
+ u8 cfg_info_len[] = { CFG_GROUP_LEN(cfg_info_group1),
+ CFG_GROUP_LEN(cfg_info_group2),
+ CFG_GROUP_LEN(cfg_info_group3),
+ CFG_GROUP_LEN(cfg_info_group4),
+ CFG_GROUP_LEN(cfg_info_group5),
+ CFG_GROUP_LEN(cfg_info_group6)};
+
+ if ((!cfg_info_len[1]) && (!cfg_info_len[2]) &&
+ (!cfg_info_len[3]) && (!cfg_info_len[4]) &&
+ (!cfg_info_len[5]))
+ {
+ sensor_id = 0;
+ }
+ else
+ {
+ ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_SENSOR_ID, &sensor_id, 1);
+ if (SUCCESS == ret)
+ {
+ if (sensor_id >= 0x06)
+ {
+ GTP_ERROR("Invalid sensor_id(0x%02X), No Config Sent!", sensor_id);
+ return -1;
+ }
+ }
+ else
+ {
+ GTP_ERROR("Failed to get sensor_id, No config sent!");
+ return -1;
+ }
+ }
+
+ GTP_DEBUG("Sensor_ID: %d", sensor_id);
+
+ ts->gtp_cfg_len = cfg_info_len[sensor_id];
+
+ if (ts->gtp_cfg_len < GTP_CONFIG_MIN_LENGTH)
+ {
+ GTP_ERROR("Sensor_ID(%d) matches with NULL or INVALID CONFIG GROUP! NO Config Sent! You need to check you header file CFG_GROUP section!", sensor_id);
+ return -1;
+ }
+
+ ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA, &opr_buf[0], 1);
+
+ if (ret == SUCCESS)
+ {
+ GTP_DEBUG("CFG_GROUP%d Config Version: %d, IC Config Version: %d", sensor_id+1,
+ send_cfg_buf[sensor_id][0], opr_buf[0]);
+
+ send_cfg_buf[sensor_id][0] = opr_buf[0];
+ ts->fixed_cfg = 0;
+ /*
+ if (opr_buf[0] < 90)
+ {
+ grp_cfg_version = send_cfg_buf[sensor_id][0]; // backup group config version
+ send_cfg_buf[sensor_id][0] = 0x00;
+ ts->fixed_cfg = 0;
+ }
+ else // treated as fixed config, not send config
+ {
+ GTP_INFO("Ic fixed config with config version(%d)", opr_buf[0]);
+ ts->fixed_cfg = 1;
+ }*/
+ }
+ else
+ {
+ GTP_ERROR("Failed to get ic config version!No config sent!");
+ return -1;
+ }
+
+ memset(&config[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH);
+ memcpy(&config[GTP_ADDR_LENGTH], send_cfg_buf[sensor_id], ts->gtp_cfg_len);
+
+ GTP_DEBUG("X_MAX = %d, Y_MAX = %d, TRIGGER = 0x%02x",
+ ts->abs_x_max, ts->abs_y_max, ts->int_trigger_type);
+
+ config[RESOLUTION_LOC] = (u8)GTP_MAX_WIDTH;
+ config[RESOLUTION_LOC + 1] = (u8)(GTP_MAX_WIDTH>>8);
+ config[RESOLUTION_LOC + 2] = (u8)GTP_MAX_HEIGHT;
+ config[RESOLUTION_LOC + 3] = (u8)(GTP_MAX_HEIGHT>>8);
+
+ if (GTP_INT_TRIGGER == 0) //RISING
+ {
+ config[TRIGGER_LOC] &= 0xfe;
+ }
+ else if (GTP_INT_TRIGGER == 1) //FALLING
+ {
+ config[TRIGGER_LOC] |= 0x01;
+ }
+
+ check_sum = 0;
+ for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++)
+ {
+ check_sum += config[i];
+ }
+ config[ts->gtp_cfg_len] = (~check_sum) + 1;
+
+ GTP_DEBUG_FUNC();
+ ret = gtp_send_cfg(ts->client);
+ if (ret < 0)
+ {
+ GTP_ERROR("Send config error.");
+ }
+
+ msleep(10);
+ return 0;
+}
+
+
+static u8 gup_get_ic_msg(struct i2c_client *client, u16 addr, u8* msg, s32 len)
+{
+ s32 i = 0;
+
+ msg[0] = (addr >> 8) & 0xff;
+ msg[1] = addr & 0xff;
+
+ for (i = 0; i < 5; i++)
+ {
+ if (gup_i2c_read(client, msg, GTP_ADDR_LENGTH + len) > 0)
+ {
+ break;
+ }
+ }
+
+ if (i >= 5)
+ {
+ GTP_ERROR("Read data from 0x%02x%02x failed!", msg[0], msg[1]);
+ return FAIL;
+ }
+
+ return SUCCESS;
+}
+
+static u8 gup_set_ic_msg(struct i2c_client *client, u16 addr, u8 val)
+{
+ s32 i = 0;
+ u8 msg[3];
+
+ msg[0] = (addr >> 8) & 0xff;
+ msg[1] = addr & 0xff;
+ msg[2] = val;
+
+ for (i = 0; i < 5; i++)
+ {
+ if (gup_i2c_write(client, msg, GTP_ADDR_LENGTH + 1) > 0)
+ {
+ break;
+ }
+ }
+
+ if (i >= 5)
+ {
+ GTP_ERROR("Set data to 0x%02x%02x failed!", msg[0], msg[1]);
+ return FAIL;
+ }
+
+ return SUCCESS;
+}
+
+static u8 gup_get_ic_fw_msg(struct i2c_client *client)
+{
+ s32 ret = -1;
+ u8 retry = 0;
+ u8 buf[16];
+ u8 i;
+
+ // step1:get hardware info
+ ret = gtp_i2c_read_dbl_check(client, GUP_REG_HW_INFO, &buf[GTP_ADDR_LENGTH], 4);
+ if (FAIL == ret)
+ {
+ GTP_ERROR("[get_ic_fw_msg]get hw_info failed,exit");
+ return FAIL;
+ }
+
+ // buf[2~5]: 00 06 90 00
+ // hw_info: 00 90 06 00
+ for(i=0; i<4; i++)
+ {
+ update_msg.ic_fw_msg.hw_info[i] = buf[GTP_ADDR_LENGTH + 3 - i];
+ }
+ GTP_DEBUG("IC Hardware info:%02x%02x%02x%02x", update_msg.ic_fw_msg.hw_info[0], update_msg.ic_fw_msg.hw_info[1],
+ update_msg.ic_fw_msg.hw_info[2], update_msg.ic_fw_msg.hw_info[3]);
+ // step2:get firmware message
+ for(retry=0; retry<2; retry++)
+ {
+ ret = gup_get_ic_msg(client, GUP_REG_FW_MSG, buf, 1);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("Read firmware message fail.");
+ return ret;
+ }
+
+ update_msg.force_update = buf[GTP_ADDR_LENGTH];
+ if((0xBE != update_msg.force_update)&&(!retry))
+ {
+ GTP_INFO("The check sum in ic is error.");
+ GTP_INFO("The IC will be updated by force.");
+ continue;
+ }
+ break;
+ }
+ GTP_DEBUG("IC force update flag:0x%x", update_msg.force_update);
+
+ // step3:get pid & vid
+ ret = gtp_i2c_read_dbl_check(client, GUP_REG_PID_VID, &buf[GTP_ADDR_LENGTH], 6);
+ if (FAIL == ret)
+ {
+ GTP_ERROR("[get_ic_fw_msg]get pid & vid failed,exit");
+ return FAIL;
+ }
+
+ memset(update_msg.ic_fw_msg.pid, 0, sizeof(update_msg.ic_fw_msg.pid));
+ memcpy(update_msg.ic_fw_msg.pid, &buf[GTP_ADDR_LENGTH], 4);
+ GTP_DEBUG("IC Product id:%s", update_msg.ic_fw_msg.pid);
+
+ //GT9XX PID MAPPING
+ /*|-----FLASH-----RAM-----|
+ |------918------918-----|
+ |------968------968-----|
+ |------913------913-----|
+ |------913P-----913P----|
+ |------927------927-----|
+ |------927P-----927P----|
+ |------9110-----9110----|
+ |------9110P----9111----|*/
+ if(update_msg.ic_fw_msg.pid[0] != 0)
+ {
+ if(!memcmp(update_msg.ic_fw_msg.pid, "9111", 4))
+ {
+ GTP_DEBUG("IC Mapping Product id:%s", update_msg.ic_fw_msg.pid);
+ memcpy(update_msg.ic_fw_msg.pid, "9110P", 5);
+ }
+ }
+
+ update_msg.ic_fw_msg.vid = buf[GTP_ADDR_LENGTH+4] + (buf[GTP_ADDR_LENGTH+5]<<8);
+ GTP_DEBUG("IC version id:%04x", update_msg.ic_fw_msg.vid);
+
+ return SUCCESS;
+}
+
+s32 gup_enter_update_mode(struct i2c_client *client)
+{
+ s32 ret = -1;
+ s32 retry = 0;
+ u8 rd_buf[3];
+
+ //step1:RST output low last at least 2ms
+ GTP_GPIO_OUTPUT(GTP_RST_PORT, 0);
+ msleep(2);
+
+ //step2:select I2C slave addr,INT:0--0xBA;1--0x28.
+ GTP_GPIO_OUTPUT(GTP_INT_PORT, (client->addr == 0x14));
+ msleep(2);
+
+ //step3:RST output high reset guitar
+ GTP_GPIO_OUTPUT(GTP_RST_PORT, 1);
+
+ //20121211 modify start
+ msleep(5);
+ while(retry++ < 200)
+ {
+ //step4:Hold ss51 & dsp
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
+ if(ret <= 0)
+ {
+ GTP_DEBUG("Hold ss51 & dsp I2C error,retry:%d", retry);
+ continue;
+ }
+
+ //step5:Confirm hold
+ ret = gup_get_ic_msg(client, _rRW_MISCTL__SWRST_B0_, rd_buf, 1);
+ if(ret <= 0)
+ {
+ GTP_DEBUG("Hold ss51 & dsp I2C error,retry:%d", retry);
+ continue;
+ }
+ if(0x0C == rd_buf[GTP_ADDR_LENGTH])
+ {
+ GTP_DEBUG("Hold ss51 & dsp confirm SUCCESS");
+ break;
+ }
+ GTP_DEBUG("Hold ss51 & dsp confirm 0x4180 failed,value:%d", rd_buf[GTP_ADDR_LENGTH]);
+ }
+ if(retry >= 200)
+ {
+ GTP_ERROR("Enter update Hold ss51 failed.");
+ return FAIL;
+ }
+
+ //step6:DSP_CK and DSP_ALU_CK PowerOn
+ ret = gup_set_ic_msg(client, 0x4010, 0x00);
+
+ //20121211 modify end
+ return ret;
+}
+
+void gup_leave_update_mode(void)
+{
+ GTP_GPIO_AS_INT(GTP_INT_PORT);
+
+ GTP_DEBUG("[leave_update_mode]reset chip.");
+ gtp_reset_guitar(i2c_connect_client, 20);
+}
+
+// Get the correct nvram data
+// The correct conditions:
+// 1. the hardware info is the same
+// 2. the product id is the same
+// 3. the firmware version in update file is greater than the firmware version in ic
+// or the check sum in ic is wrong
+/* Update Conditions:
+ 1. Same hardware info
+ 2. Same PID
+ 3. File PID > IC PID
+ Force Update Conditions:
+ 1. Wrong ic firmware checksum
+ 2. INVALID IC PID or VID
+ 3. IC PID == 91XX || File PID == 91XX
+*/
+
+static u8 gup_enter_update_judge(st_fw_head *fw_head)
+{
+ u16 u16_tmp;
+ s32 i = 0;
+
+ u16_tmp = fw_head->vid;
+ fw_head->vid = (u16)(u16_tmp>>8) + (u16)(u16_tmp<<8);
+
+ GTP_DEBUG("FILE HARDWARE INFO:%02x%02x%02x%02x", fw_head->hw_info[0], fw_head->hw_info[1], fw_head->hw_info[2], fw_head->hw_info[3]);
+ GTP_DEBUG("FILE PID:%s", fw_head->pid);
+ GTP_DEBUG("FILE VID:%04x", fw_head->vid);
+
+ GTP_DEBUG("IC HARDWARE INFO:%02x%02x%02x%02x", update_msg.ic_fw_msg.hw_info[0], update_msg.ic_fw_msg.hw_info[1],
+ update_msg.ic_fw_msg.hw_info[2], update_msg.ic_fw_msg.hw_info[3]);
+ GTP_DEBUG("IC PID:%s", update_msg.ic_fw_msg.pid);
+ GTP_DEBUG("IC VID:%04x", update_msg.ic_fw_msg.vid);
+
+ //First two conditions
+ if ( !memcmp(fw_head->hw_info, update_msg.ic_fw_msg.hw_info, sizeof(update_msg.ic_fw_msg.hw_info)))
+ {
+ GTP_DEBUG("Get the same hardware info.");
+ if( update_msg.force_update != 0xBE )
+ {
+ GTP_INFO("FW chksum error,need enter update.");
+ return SUCCESS;
+ }
+
+ // 20130523 start
+ if (strlen(update_msg.ic_fw_msg.pid) < 3)
+ {
+ GTP_INFO("Illegal IC pid, need enter update");
+ return SUCCESS;
+ }
+ else
+ {
+ for (i = 0; i < 3; i++)
+ {
+ if ((update_msg.ic_fw_msg.pid[i] < 0x30) || (update_msg.ic_fw_msg.pid[i] > 0x39))
+ {
+ GTP_INFO("Illegal IC pid, out of bound, need enter update");
+ return SUCCESS;
+ }
+ }
+ }
+ // 20130523 end
+
+
+ if (( !memcmp(fw_head->pid, update_msg.ic_fw_msg.pid, (strlen(fw_head->pid)<3?3:strlen(fw_head->pid))))||
+ (!memcmp(update_msg.ic_fw_msg.pid, "91XX", 4))||
+ (!memcmp(fw_head->pid, "91XX", 4)))
+ {
+ if(!memcmp(fw_head->pid, "91XX", 4))
+ {
+ GTP_DEBUG("Force none same pid update mode.");
+ }
+ else
+ {
+ GTP_DEBUG("Get the same pid.");
+ }
+ //The third condition
+ if (fw_head->vid > update_msg.ic_fw_msg.vid)
+ {
+
+ GTP_INFO("Need enter update.");
+ return SUCCESS;
+ }
+ GTP_ERROR("Don't meet the third condition.");
+ GTP_ERROR("File VID <= Ic VID, update aborted!");
+ }
+ else
+ {
+ GTP_ERROR("File PID != Ic PID, update aborted!");
+ }
+ }
+ else
+ {
+ GTP_ERROR("Different Hardware, update aborted!");
+ }
+ return FAIL;
+}
+
+static u8 ascii2hex(u8 a)
+{
+ s8 value = 0;
+
+ if(a >= '0' && a <= '9')
+ {
+ value = a - '0';
+ }
+ else if(a >= 'A' && a <= 'F')
+ {
+ value = a - 'A' + 0x0A;
+ }
+ else if(a >= 'a' && a <= 'f')
+ {
+ value = a - 'a' + 0x0A;
+ }
+ else
+ {
+ value = 0xff;
+ }
+
+ return value;
+}
+
+static s8 gup_update_config(struct i2c_client *client)
+{
+ s32 file_len = 0;
+ s32 ret = 0;
+ s32 i = 0;
+ s32 file_cfg_len = 0;
+ s32 chip_cfg_len = 0;
+ s32 count = 0;
+ u8 *buf;
+ u8 *pre_buf;
+ u8 *file_config;
+ //u8 checksum = 0;
+ u8 pid[8];
+
+ if(NULL == update_msg.cfg_file)
+ {
+ GTP_ERROR("[update_cfg]No need to upgrade config!");
+ return FAIL;
+ }
+ file_len = update_msg.cfg_file->f_op->llseek(update_msg.cfg_file, 0, SEEK_END);
+
+ ret = gup_get_ic_msg(client, GUP_REG_PID_VID, pid, 6);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[update_cfg]Read product id & version id fail.");
+ return FAIL;
+ }
+ pid[5] = '\0';
+ GTP_DEBUG("update cfg get pid:%s", &pid[GTP_ADDR_LENGTH]);
+
+ chip_cfg_len = 186;
+ if(!memcmp(&pid[GTP_ADDR_LENGTH], "968", 3) ||
+ !memcmp(&pid[GTP_ADDR_LENGTH], "910", 3) ||
+ !memcmp(&pid[GTP_ADDR_LENGTH], "960", 3))
+ {
+ chip_cfg_len = 228;
+ }
+ GTP_DEBUG("[update_cfg]config file len:%d", file_len);
+ GTP_DEBUG("[update_cfg]need config len:%d",chip_cfg_len);
+ if((file_len+5) < chip_cfg_len*5)
+ {
+ GTP_ERROR("Config length error");
+ return -1;
+ }
+
+ buf = (u8*)kzalloc(file_len, GFP_KERNEL);
+ pre_buf = (u8*)kzalloc(file_len, GFP_KERNEL);
+ file_config = (u8*)kzalloc(chip_cfg_len + GTP_ADDR_LENGTH, GFP_KERNEL);
+ update_msg.cfg_file->f_op->llseek(update_msg.cfg_file, 0, SEEK_SET);
+
+ GTP_DEBUG("[update_cfg]Read config from file.");
+ ret = update_msg.cfg_file->f_op->read(update_msg.cfg_file, (char*)pre_buf, file_len, &update_msg.cfg_file->f_pos);
+ if(ret<0)
+ {
+ GTP_ERROR("[update_cfg]Read config file failed.");
+ goto update_cfg_file_failed;
+ }
+
+ GTP_DEBUG("[update_cfg]Delete illgal charactor.");
+ for(i=0,count=0; i<file_len; i++)
+ {
+ if (pre_buf[i] == ' ' || pre_buf[i] == '\r' || pre_buf[i] == '\n')
+ {
+ continue;
+ }
+ buf[count++] = pre_buf[i];
+ }
+
+ GTP_DEBUG("[update_cfg]Ascii to hex.");
+ file_config[0] = GTP_REG_CONFIG_DATA >> 8;
+ file_config[1] = GTP_REG_CONFIG_DATA & 0xff;
+ for(i=0,file_cfg_len=GTP_ADDR_LENGTH; i<count; i+=5)
+ {
+ if((buf[i]=='0') && ((buf[i+1]=='x') || (buf[i+1]=='X')))
+ {
+ u8 high,low;
+ high = ascii2hex(buf[i+2]);
+ low = ascii2hex(buf[i+3]);
+
+ if((high == 0xFF) || (low == 0xFF))
+ {
+ ret = 0;
+ GTP_ERROR("[update_cfg]Illegal config file.");
+ goto update_cfg_file_failed;
+ }
+ file_config[file_cfg_len++] = (high<<4) + low;
+ }
+ else
+ {
+ ret = 0;
+ GTP_ERROR("[update_cfg]Illegal config file.");
+ goto update_cfg_file_failed;
+ }
+ }
+
+// //cal checksum
+// for(i=GTP_ADDR_LENGTH; i<chip_cfg_len; i++)
+// {
+// checksum += file_config[i];
+// }
+// file_config[chip_cfg_len] = (~checksum) + 1;
+// file_config[chip_cfg_len+1] = 0x01;
+
+ GTP_DEBUG("config:");
+ GTP_DEBUG_ARRAY(file_config+2, file_cfg_len);
+
+ i = 0;
+ while(i++ < 5)
+ {
+ ret = gup_i2c_write(client, file_config, file_cfg_len);
+ if(ret > 0)
+ {
+ GTP_INFO("[update_cfg]Send config SUCCESS.");
+ break;
+ }
+ GTP_ERROR("[update_cfg]Send config i2c error.");
+ }
+
+update_cfg_file_failed:
+ kfree(pre_buf);
+ kfree(buf);
+ kfree(file_config);
+ return ret;
+}
+
+#if GTP_HEADER_FW_UPDATE
+static u8 gup_check_fs_mounted(char *path_name)
+{
+ struct path root_path;
+ struct path path;
+ int err;
+ err = kern_path("/", LOOKUP_FOLLOW, &root_path);
+
+ if (err)
+ {
+ GTP_DEBUG("\"/\" NOT Mounted: %d", err);
+ return FAIL;
+ }
+ err = kern_path(path_name, LOOKUP_FOLLOW, &path);
+
+ if (err)
+ {
+ GTP_DEBUG("/data/ NOT Mounted: %d", err);
+ return FAIL;
+ }
+
+ return SUCCESS;
+
+ /*
+ if (path.mnt->mnt_sb == root_path.mnt->mnt_sb)
+ {
+ //-- not mounted
+ return FAIL;
+ }
+ else
+ {
+ return SUCCESS;
+ }*/
+
+}
+#endif
+static u8 gup_check_update_file(struct i2c_client *client, st_fw_head* fw_head, u8* path)
+{
+ s32 ret = 0;
+ s32 i = 0;
+ s32 fw_checksum = 0;
+ u8 buf[FW_HEAD_LENGTH];
+
+ if (path)
+ {
+ GTP_DEBUG("Update File path:%s, %d", path, strlen(path));
+ update_msg.file = filp_open(path, O_RDONLY, 0);
+
+ if (IS_ERR(update_msg.file))
+ {
+ GTP_ERROR("Open update file(%s) error!", path);
+ return FAIL;
+ }
+ }
+ else
+ {
+#if GTP_HEADER_FW_UPDATE
+ for (i = 0; i < (GUP_SEARCH_FILE_TIMES); i++)
+ {
+ GTP_DEBUG("Waiting for /data mounted [%d]", i);
+
+ if (gup_check_fs_mounted("/data") == SUCCESS)
+ {
+ GTP_DEBUG("/data Mounted!");
+ break;
+ }
+ msleep(3000);
+ }
+ if (i >= (GUP_SEARCH_FILE_TIMES))
+ {
+ GTP_ERROR("Wait for /data mounted timeout!");
+ return FAIL;
+ }
+
+ // update config
+ update_msg.cfg_file = filp_open(CONFIG_FILE_PATH_1, O_RDONLY, 0);
+ if (IS_ERR(update_msg.cfg_file))
+ {
+ GTP_DEBUG("%s is unavailable", CONFIG_FILE_PATH_1);
+ }
+ else
+ {
+ GTP_INFO("Update Config File: %s", CONFIG_FILE_PATH_1);
+ ret = gup_update_config(client);
+ if(ret <= 0)
+ {
+ GTP_ERROR("Update config failed.");
+ }
+ filp_close(update_msg.cfg_file, NULL);
+ }
+
+ if (sizeof(header_fw_array) < (FW_HEAD_LENGTH+FW_SECTION_LENGTH*4+FW_DSP_ISP_LENGTH+FW_DSP_LENGTH+FW_BOOT_LENGTH))
+ {
+ GTP_ERROR("INVALID header_fw_array, check your gt9xx_firmware.h file!");
+ return FAIL;
+ }
+ update_msg.file = filp_open(UPDATE_FILE_PATH_2, O_CREAT | O_RDWR, 0666);
+ if ((IS_ERR(update_msg.file)))
+ {
+ GTP_ERROR("Failed to Create file: %s for fw_header!", UPDATE_FILE_PATH_2);
+ return FAIL;
+ }
+ update_msg.file->f_op->llseek(update_msg.file, 0, SEEK_SET);
+ update_msg.file->f_op->write(update_msg.file, (char *)header_fw_array, sizeof(header_fw_array), &update_msg.file->f_pos);
+ filp_close(update_msg.file, NULL);
+ update_msg.file = filp_open(UPDATE_FILE_PATH_2, O_RDONLY, 0);
+#else
+ u8 fp_len = max(sizeof(UPDATE_FILE_PATH_1), sizeof(UPDATE_FILE_PATH_2));
+ u8 cfp_len = max(sizeof(CONFIG_FILE_PATH_1), sizeof(CONFIG_FILE_PATH_2));
+ u8 *search_update_path = (u8*)kzalloc(fp_len, GFP_KERNEL);
+ u8 *search_cfg_path = (u8*)kzalloc(cfp_len, GFP_KERNEL);
+ //Begin to search update file,the config file & firmware file must be in the same path,single or double.
+ searching_file = 1;
+ for (i = 0; i < GUP_SEARCH_FILE_TIMES; i++)
+ {
+ if (searching_file == 0)
+ {
+ kfree(search_update_path);
+ kfree(search_cfg_path);
+ GTP_INFO(".bin/.cfg update file search forcely terminated!");
+ return FAIL;
+ }
+ if(i%2)
+ {
+ memcpy(search_update_path, UPDATE_FILE_PATH_1, sizeof(UPDATE_FILE_PATH_1));
+ memcpy(search_cfg_path, CONFIG_FILE_PATH_1, sizeof(CONFIG_FILE_PATH_1));
+ }
+ else
+ {
+ memcpy(search_update_path, UPDATE_FILE_PATH_2, sizeof(UPDATE_FILE_PATH_2));
+ memcpy(search_cfg_path, CONFIG_FILE_PATH_2, sizeof(CONFIG_FILE_PATH_2));
+ }
+
+ if(!(got_file_flag&0x0F))
+ {
+ update_msg.file = filp_open(search_update_path, O_RDONLY, 0);
+ if(!IS_ERR(update_msg.file))
+ {
+ GTP_DEBUG("Find the bin file");
+ got_file_flag |= 0x0F;
+ }
+ }
+ if(!(got_file_flag&0xF0))
+ {
+ update_msg.cfg_file = filp_open(search_cfg_path, O_RDONLY, 0);
+ if(!IS_ERR(update_msg.cfg_file))
+ {
+ GTP_DEBUG("Find the cfg file");
+ got_file_flag |= 0xF0;
+ }
+ }
+
+ if(got_file_flag)
+ {
+ if(got_file_flag == 0xFF)
+ {
+ break;
+ }
+ else
+ {
+ i += 4;
+ }
+ }
+ GTP_DEBUG("%3d:Searching %s %s file...", i, (got_file_flag&0x0F)?"":"bin", (got_file_flag&0xF0)?"":"cfg");
+ msleep(3000);
+ }
+ searching_file = 0;
+ kfree(search_update_path);
+ kfree(search_cfg_path);
+
+ if(!got_file_flag)
+ {
+ GTP_ERROR("Can't find update file.");
+ goto load_failed;
+ }
+
+ if(got_file_flag&0xF0)
+ {
+ GTP_DEBUG("Got the update config file.");
+ ret = gup_update_config(client);
+ if(ret <= 0)
+ {
+ GTP_ERROR("Update config failed.");
+ }
+ filp_close(update_msg.cfg_file, NULL);
+ msleep(500); //waiting config to be stored in FLASH.
+ }
+ if(got_file_flag&0x0F)
+ {
+ GTP_DEBUG("Got the update firmware file.");
+ }
+ else
+ {
+ GTP_ERROR("No need to upgrade firmware.");
+ goto load_failed;
+ }
+#endif
+ }
+
+ update_msg.old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ update_msg.file->f_op->llseek(update_msg.file, 0, SEEK_SET);
+ //update_msg.file->f_pos = 0;
+
+ ret = update_msg.file->f_op->read(update_msg.file, (char*)buf, FW_HEAD_LENGTH, &update_msg.file->f_pos);
+ if (ret < 0)
+ {
+ GTP_ERROR("Read firmware head in update file error.");
+ goto load_failed;
+ }
+ memcpy(fw_head, buf, FW_HEAD_LENGTH);
+
+ //check firmware legality
+ fw_checksum = 0;
+ for(i=0; i<FW_SECTION_LENGTH*4+FW_DSP_ISP_LENGTH+FW_DSP_LENGTH+FW_BOOT_LENGTH; i+=2)
+ {
+ u16 temp;
+ ret = update_msg.file->f_op->read(update_msg.file, (char*)buf, 2, &update_msg.file->f_pos);
+ if (ret < 0)
+ {
+ GTP_ERROR("Read firmware file error.");
+ goto load_failed;
+ }
+ //GTP_DEBUG("BUF[0]:%x", buf[0]);
+ temp = (buf[0]<<8) + buf[1];
+ fw_checksum += temp;
+ }
+
+ GTP_DEBUG("firmware checksum:%x", fw_checksum&0xFFFF);
+ if(fw_checksum&0xFFFF)
+ {
+ GTP_ERROR("Illegal firmware file.");
+ goto load_failed;
+ }
+
+ return SUCCESS;
+
+load_failed:
+ set_fs(update_msg.old_fs);
+ return FAIL;
+}
+
+#if 0
+static u8 gup_check_update_header(struct i2c_client *client, st_fw_head* fw_head)
+{
+ const u8* pos;
+ int i = 0;
+ u8 mask_num = 0;
+ s32 ret = 0;
+
+ pos = HEADER_UPDATE_DATA;
+
+ memcpy(fw_head, pos, FW_HEAD_LENGTH);
+ pos += FW_HEAD_LENGTH;
+
+ ret = gup_enter_update_judge(fw_head);
+ if(SUCCESS == ret)
+ {
+ return SUCCESS;
+ }
+ return FAIL;
+}
+#endif
+
+static u8 gup_burn_proc(struct i2c_client *client, u8 *burn_buf, u16 start_addr, u16 total_length)
+{
+ s32 ret = 0;
+ u16 burn_addr = start_addr;
+ u16 frame_length = 0;
+ u16 burn_length = 0;
+ u8 wr_buf[PACK_SIZE + GTP_ADDR_LENGTH];
+ u8 rd_buf[PACK_SIZE + GTP_ADDR_LENGTH];
+ u8 retry = 0;
+
+ GTP_DEBUG("Begin burn %dk data to addr 0x%x", (total_length/1024), start_addr);
+ while(burn_length < total_length)
+ {
+ GTP_DEBUG("B/T:%04d/%04d", burn_length, total_length);
+ frame_length = ((total_length - burn_length) > PACK_SIZE) ? PACK_SIZE : (total_length - burn_length);
+ wr_buf[0] = (u8)(burn_addr>>8);
+ rd_buf[0] = wr_buf[0];
+ wr_buf[1] = (u8)burn_addr;
+ rd_buf[1] = wr_buf[1];
+ memcpy(&wr_buf[GTP_ADDR_LENGTH], &burn_buf[burn_length], frame_length);
+
+ for(retry = 0; retry < MAX_FRAME_CHECK_TIME; retry++)
+ {
+ ret = gup_i2c_write(client, wr_buf, GTP_ADDR_LENGTH + frame_length);
+ if(ret <= 0)
+ {
+ GTP_ERROR("Write frame data i2c error.");
+ continue;
+ }
+ ret = gup_i2c_read(client, rd_buf, GTP_ADDR_LENGTH + frame_length);
+ if(ret <= 0)
+ {
+ GTP_ERROR("Read back frame data i2c error.");
+ continue;
+ }
+
+ if(memcmp(&wr_buf[GTP_ADDR_LENGTH], &rd_buf[GTP_ADDR_LENGTH], frame_length))
+ {
+ GTP_ERROR("Check frame data fail,not equal.");
+ GTP_DEBUG("write array:");
+ GTP_DEBUG_ARRAY(&wr_buf[GTP_ADDR_LENGTH], frame_length);
+ GTP_DEBUG("read array:");
+ GTP_DEBUG_ARRAY(&rd_buf[GTP_ADDR_LENGTH], frame_length);
+ continue;
+ }
+ else
+ {
+ //GTP_DEBUG("Check frame data success.");
+ break;
+ }
+ }
+ if(retry >= MAX_FRAME_CHECK_TIME)
+ {
+ GTP_ERROR("Burn frame data time out,exit.");
+ return FAIL;
+ }
+ burn_length += frame_length;
+ burn_addr += frame_length;
+ }
+ return SUCCESS;
+}
+
+static u8 gup_load_section_file(u8* buf, u16 offset, u16 length)
+{
+ s32 ret = 0;
+
+ if(update_msg.file == NULL)
+ {
+ GTP_ERROR("cannot find update file,load section file fail.");
+ return FAIL;
+ }
+ update_msg.file->f_pos = FW_HEAD_LENGTH + offset;
+
+ ret = update_msg.file->f_op->read(update_msg.file, (char*)buf, length, &update_msg.file->f_pos);
+ if(ret < 0)
+ {
+ GTP_ERROR("Read update file fail.");
+ return FAIL;
+ }
+
+ return SUCCESS;
+}
+
+static u8 gup_recall_check(struct i2c_client *client, u8* chk_src, u16 start_rd_addr, u16 chk_length)
+{
+ u8 rd_buf[PACK_SIZE + GTP_ADDR_LENGTH];
+ s32 ret = 0;
+ u16 recall_addr = start_rd_addr;
+ u16 recall_length = 0;
+ u16 frame_length = 0;
+
+ while(recall_length < chk_length)
+ {
+ frame_length = ((chk_length - recall_length) > PACK_SIZE) ? PACK_SIZE : (chk_length - recall_length);
+ ret = gup_get_ic_msg(client, recall_addr, rd_buf, frame_length);
+ if(ret <= 0)
+ {
+ GTP_ERROR("recall i2c error,exit");
+ return FAIL;
+ }
+
+ if(memcmp(&rd_buf[GTP_ADDR_LENGTH], &chk_src[recall_length], frame_length))
+ {
+ GTP_ERROR("Recall frame data fail,not equal.");
+ GTP_DEBUG("chk_src array:");
+ GTP_DEBUG_ARRAY(&chk_src[recall_length], frame_length);
+ GTP_DEBUG("recall array:");
+ GTP_DEBUG_ARRAY(&rd_buf[GTP_ADDR_LENGTH], frame_length);
+ return FAIL;
+ }
+
+ recall_length += frame_length;
+ recall_addr += frame_length;
+ }
+ GTP_DEBUG("Recall check %dk firmware success.", (chk_length/1024));
+
+ return SUCCESS;
+}
+
+static u8 gup_burn_fw_section(struct i2c_client *client, u8 *fw_section, u16 start_addr, u8 bank_cmd )
+{
+ s32 ret = 0;
+ u8 rd_buf[5];
+
+ //step1:hold ss51 & dsp
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_section]hold ss51 & dsp fail.");
+ return FAIL;
+ }
+
+ //step2:set scramble
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_section]set scramble fail.");
+ return FAIL;
+ }
+
+ //step3:select bank
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, (bank_cmd >> 4)&0x0F);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_section]select bank %d fail.", (bank_cmd >> 4)&0x0F);
+ return FAIL;
+ }
+
+ //step4:enable accessing code
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_section]enable accessing code fail.");
+ return FAIL;
+ }
+
+ //step5:burn 8k fw section
+ ret = gup_burn_proc(client, fw_section, start_addr, FW_SECTION_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_section]burn fw_section fail.");
+ return FAIL;
+ }
+
+ //step6:hold ss51 & release dsp
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_section]hold ss51 & release dsp fail.");
+ return FAIL;
+ }
+ //must delay
+ msleep(1);
+
+ //step7:send burn cmd to move data to flash from sram
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, bank_cmd&0x0f);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_section]send burn cmd fail.");
+ return FAIL;
+ }
+ GTP_DEBUG("[burn_fw_section]Wait for the burn is complete......");
+ do{
+ ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_section]Get burn state fail");
+ return FAIL;
+ }
+ msleep(10);
+ //GTP_DEBUG("[burn_fw_section]Get burn state:%d.", rd_buf[GTP_ADDR_LENGTH]);
+ }while(rd_buf[GTP_ADDR_LENGTH]);
+
+ //step8:select bank
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, (bank_cmd >> 4)&0x0F);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_section]select bank %d fail.", (bank_cmd >> 4)&0x0F);
+ return FAIL;
+ }
+
+ //step9:enable accessing code
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_section]enable accessing code fail.");
+ return FAIL;
+ }
+
+ //step10:recall 8k fw section
+ ret = gup_recall_check(client, fw_section, start_addr, FW_SECTION_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_section]recall check 8k firmware fail.");
+ return FAIL;
+ }
+
+ //step11:disable accessing code
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x00);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_section]disable accessing code fail.");
+ return FAIL;
+ }
+
+ return SUCCESS;
+}
+
+static u8 gup_burn_dsp_isp(struct i2c_client *client)
+{
+ s32 ret = 0;
+ u8* fw_dsp_isp = NULL;
+ u8 retry = 0;
+
+ GTP_DEBUG("[burn_dsp_isp]Begin burn dsp isp---->>");
+
+ //step1:alloc memory
+ GTP_DEBUG("[burn_dsp_isp]step1:alloc memory");
+ while(retry++ < 5)
+ {
+ fw_dsp_isp = (u8*)kzalloc(FW_DSP_ISP_LENGTH, GFP_KERNEL);
+ if(fw_dsp_isp == NULL)
+ {
+ continue;
+ }
+ else
+ {
+ GTP_INFO("[burn_dsp_isp]Alloc %dk byte memory success.", (FW_DSP_ISP_LENGTH/1024));
+ break;
+ }
+ }
+ if(retry >= 5)
+ {
+ GTP_ERROR("[burn_dsp_isp]Alloc memory fail,exit.");
+ return FAIL;
+ }
+
+ //step2:load dsp isp file data
+ GTP_DEBUG("[burn_dsp_isp]step2:load dsp isp file data");
+ ret = gup_load_section_file(fw_dsp_isp, (4*FW_SECTION_LENGTH+FW_DSP_LENGTH+FW_BOOT_LENGTH), FW_DSP_ISP_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_dsp_isp]load firmware dsp_isp fail.");
+ goto exit_burn_dsp_isp;
+ }
+
+ //step3:disable wdt,clear cache enable
+ GTP_DEBUG("[burn_dsp_isp]step3:disable wdt,clear cache enable");
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__TMR0_EN, 0x00);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_dsp_isp]disable wdt fail.");
+ ret = FAIL;
+ goto exit_burn_dsp_isp;
+ }
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__CACHE_EN, 0x00);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_dsp_isp]clear cache enable fail.");
+ ret = FAIL;
+ goto exit_burn_dsp_isp;
+ }
+
+ //step4:hold ss51 & dsp
+ GTP_DEBUG("[burn_dsp_isp]step4:hold ss51 & dsp");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_dsp_isp]hold ss51 & dsp fail.");
+ ret = FAIL;
+ goto exit_burn_dsp_isp;
+ }
+
+ //step5:set boot from sram
+ GTP_DEBUG("[burn_dsp_isp]step5:set boot from sram");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOTCTL_B0_, 0x02);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_dsp_isp]set boot from sram fail.");
+ ret = FAIL;
+ goto exit_burn_dsp_isp;
+ }
+
+ //step6:software reboot
+ GTP_DEBUG("[burn_dsp_isp]step6:software reboot");
+ ret = gup_set_ic_msg(client, _bWO_MISCTL__CPU_SWRST_PULSE, 0x01);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_dsp_isp]software reboot fail.");
+ ret = FAIL;
+ goto exit_burn_dsp_isp;
+ }
+
+ //step7:select bank2
+ GTP_DEBUG("[burn_dsp_isp]step7:select bank2");
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x02);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_dsp_isp]select bank2 fail.");
+ ret = FAIL;
+ goto exit_burn_dsp_isp;
+ }
+
+ //step8:enable accessing code
+ GTP_DEBUG("[burn_dsp_isp]step8:enable accessing code");
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_dsp_isp]enable accessing code fail.");
+ ret = FAIL;
+ goto exit_burn_dsp_isp;
+ }
+
+ //step9:burn 4k dsp_isp
+ GTP_DEBUG("[burn_dsp_isp]step9:burn 4k dsp_isp");
+ ret = gup_burn_proc(client, fw_dsp_isp, 0xC000, FW_DSP_ISP_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_dsp_isp]burn dsp_isp fail.");
+ goto exit_burn_dsp_isp;
+ }
+
+ //step10:set scramble
+ GTP_DEBUG("[burn_dsp_isp]step10:set scramble");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_dsp_isp]set scramble fail.");
+ ret = FAIL;
+ goto exit_burn_dsp_isp;
+ }
+ ret = SUCCESS;
+
+exit_burn_dsp_isp:
+ kfree(fw_dsp_isp);
+ return ret;
+}
+
+static u8 gup_burn_fw_ss51(struct i2c_client *client)
+{
+ u8* fw_ss51 = NULL;
+ u8 retry = 0;
+ s32 ret = 0;
+
+ GTP_DEBUG("[burn_fw_ss51]Begin burn ss51 firmware---->>");
+
+ //step1:alloc memory
+ GTP_DEBUG("[burn_fw_ss51]step1:alloc memory");
+ while(retry++ < 5)
+ {
+ fw_ss51 = (u8*)kzalloc(FW_SECTION_LENGTH, GFP_KERNEL);
+ if(fw_ss51 == NULL)
+ {
+ continue;
+ }
+ else
+ {
+ GTP_INFO("[burn_fw_ss51]Alloc %dk byte memory success.", (FW_SECTION_LENGTH/1024));
+ break;
+ }
+ }
+ if(retry >= 5)
+ {
+ GTP_ERROR("[burn_fw_ss51]Alloc memory fail,exit.");
+ return FAIL;
+ }
+
+ //step2:load ss51 firmware section 1 file data
+ GTP_DEBUG("[burn_fw_ss51]step2:load ss51 firmware section 1 file data");
+ ret = gup_load_section_file(fw_ss51, 0, FW_SECTION_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 1 fail.");
+ goto exit_burn_fw_ss51;
+ }
+
+ //step3:clear control flag
+ GTP_DEBUG("[burn_fw_ss51]step3:clear control flag");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x00);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_ss51]clear control flag fail.");
+ ret = FAIL;
+ goto exit_burn_fw_ss51;
+ }
+
+ //step4:burn ss51 firmware section 1
+ GTP_DEBUG("[burn_fw_ss51]step4:burn ss51 firmware section 1");
+ ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x01);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 1 fail.");
+ goto exit_burn_fw_ss51;
+ }
+
+ //step5:load ss51 firmware section 2 file data
+ GTP_DEBUG("[burn_fw_ss51]step5:load ss51 firmware section 2 file data");
+ ret = gup_load_section_file(fw_ss51, FW_SECTION_LENGTH, FW_SECTION_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 2 fail.");
+ goto exit_burn_fw_ss51;
+ }
+
+ //step6:burn ss51 firmware section 2
+ GTP_DEBUG("[burn_fw_ss51]step6:burn ss51 firmware section 2");
+ ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x02);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 2 fail.");
+ goto exit_burn_fw_ss51;
+ }
+
+ //step7:load ss51 firmware section 3 file data
+ GTP_DEBUG("[burn_fw_ss51]step7:load ss51 firmware section 3 file data");
+ ret = gup_load_section_file(fw_ss51, 2*FW_SECTION_LENGTH, FW_SECTION_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 3 fail.");
+ goto exit_burn_fw_ss51;
+ }
+
+ //step8:burn ss51 firmware section 3
+ GTP_DEBUG("[burn_fw_ss51]step8:burn ss51 firmware section 3");
+ ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x13);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 3 fail.");
+ goto exit_burn_fw_ss51;
+ }
+
+ //step9:load ss51 firmware section 4 file data
+ GTP_DEBUG("[burn_fw_ss51]step9:load ss51 firmware section 4 file data");
+ ret = gup_load_section_file(fw_ss51, 3*FW_SECTION_LENGTH, FW_SECTION_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 4 fail.");
+ goto exit_burn_fw_ss51;
+ }
+
+ //step10:burn ss51 firmware section 4
+ GTP_DEBUG("[burn_fw_ss51]step10:burn ss51 firmware section 4");
+ ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x14);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 4 fail.");
+ goto exit_burn_fw_ss51;
+ }
+
+ ret = SUCCESS;
+
+exit_burn_fw_ss51:
+ kfree(fw_ss51);
+ return ret;
+}
+
+static u8 gup_burn_fw_dsp(struct i2c_client *client)
+{
+ s32 ret = 0;
+ u8* fw_dsp = NULL;
+ u8 retry = 0;
+ u8 rd_buf[5];
+
+ GTP_DEBUG("[burn_fw_dsp]Begin burn dsp firmware---->>");
+ //step1:alloc memory
+ GTP_DEBUG("[burn_fw_dsp]step1:alloc memory");
+ while(retry++ < 5)
+ {
+ fw_dsp = (u8*)kzalloc(FW_DSP_LENGTH, GFP_KERNEL);
+ if(fw_dsp == NULL)
+ {
+ continue;
+ }
+ else
+ {
+ GTP_INFO("[burn_fw_dsp]Alloc %dk byte memory success.", (FW_SECTION_LENGTH/1024));
+ break;
+ }
+ }
+ if(retry >= 5)
+ {
+ GTP_ERROR("[burn_fw_dsp]Alloc memory fail,exit.");
+ return FAIL;
+ }
+
+ //step2:load firmware dsp
+ GTP_DEBUG("[burn_fw_dsp]step2:load firmware dsp");
+ ret = gup_load_section_file(fw_dsp, 4*FW_SECTION_LENGTH, FW_DSP_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_dsp]load firmware dsp fail.");
+ goto exit_burn_fw_dsp;
+ }
+
+ //step3:select bank3
+ GTP_DEBUG("[burn_fw_dsp]step3:select bank3");
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_dsp]select bank3 fail.");
+ ret = FAIL;
+ goto exit_burn_fw_dsp;
+ }
+
+ //step4:hold ss51 & dsp
+ GTP_DEBUG("[burn_fw_dsp]step4:hold ss51 & dsp");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_dsp]hold ss51 & dsp fail.");
+ ret = FAIL;
+ goto exit_burn_fw_dsp;
+ }
+
+ //step5:set scramble
+ GTP_DEBUG("[burn_fw_dsp]step5:set scramble");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_dsp]set scramble fail.");
+ ret = FAIL;
+ goto exit_burn_fw_dsp;
+ }
+
+ //step6:release ss51 & dsp
+ GTP_DEBUG("[burn_fw_dsp]step6:release ss51 & dsp");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04); //20121211
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_dsp]release ss51 & dsp fail.");
+ ret = FAIL;
+ goto exit_burn_fw_dsp;
+ }
+ //must delay
+ msleep(1);
+
+ //step7:burn 4k dsp firmware
+ GTP_DEBUG("[burn_fw_dsp]step7:burn 4k dsp firmware");
+ ret = gup_burn_proc(client, fw_dsp, 0x9000, FW_DSP_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_dsp]burn fw_section fail.");
+ goto exit_burn_fw_dsp;
+ }
+
+ //step8:send burn cmd to move data to flash from sram
+ GTP_DEBUG("[burn_fw_dsp]step8:send burn cmd to move data to flash from sram");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x05);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_dsp]send burn cmd fail.");
+ goto exit_burn_fw_dsp;
+ }
+ GTP_DEBUG("[burn_fw_dsp]Wait for the burn is complete......");
+ do{
+ ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_dsp]Get burn state fail");
+ goto exit_burn_fw_dsp;
+ }
+ msleep(10);
+ //GTP_DEBUG("[burn_fw_dsp]Get burn state:%d.", rd_buf[GTP_ADDR_LENGTH]);
+ }while(rd_buf[GTP_ADDR_LENGTH]);
+
+ //step9:recall check 4k dsp firmware
+ GTP_DEBUG("[burn_fw_dsp]step9:recall check 4k dsp firmware");
+ ret = gup_recall_check(client, fw_dsp, 0x9000, FW_DSP_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_dsp]recall check 4k dsp firmware fail.");
+ goto exit_burn_fw_dsp;
+ }
+
+ ret = SUCCESS;
+
+exit_burn_fw_dsp:
+ kfree(fw_dsp);
+ return ret;
+}
+
+static u8 gup_burn_fw_boot(struct i2c_client *client)
+{
+ s32 ret = 0;
+ u8* fw_boot = NULL;
+ u8 retry = 0;
+ u8 rd_buf[5];
+
+ GTP_DEBUG("[burn_fw_boot]Begin burn bootloader firmware---->>");
+
+ //step1:Alloc memory
+ GTP_DEBUG("[burn_fw_boot]step1:Alloc memory");
+ while(retry++ < 5)
+ {
+ fw_boot = (u8*)kzalloc(FW_BOOT_LENGTH, GFP_KERNEL);
+ if(fw_boot == NULL)
+ {
+ continue;
+ }
+ else
+ {
+ GTP_INFO("[burn_fw_boot]Alloc %dk byte memory success.", (FW_BOOT_LENGTH/1024));
+ break;
+ }
+ }
+ if(retry >= 5)
+ {
+ GTP_ERROR("[burn_fw_boot]Alloc memory fail,exit.");
+ return FAIL;
+ }
+
+ //step2:load firmware bootloader
+ GTP_DEBUG("[burn_fw_boot]step2:load firmware bootloader");
+ ret = gup_load_section_file(fw_boot, (4*FW_SECTION_LENGTH+FW_DSP_LENGTH), FW_BOOT_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_boot]load firmware dsp fail.");
+ goto exit_burn_fw_boot;
+ }
+
+ //step3:hold ss51 & dsp
+ GTP_DEBUG("[burn_fw_boot]step3:hold ss51 & dsp");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_boot]hold ss51 & dsp fail.");
+ ret = FAIL;
+ goto exit_burn_fw_boot;
+ }
+
+ //step4:set scramble
+ GTP_DEBUG("[burn_fw_boot]step4:set scramble");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_boot]set scramble fail.");
+ ret = FAIL;
+ goto exit_burn_fw_boot;
+ }
+
+ //step5:release ss51 & dsp
+ GTP_DEBUG("[burn_fw_boot]step5:release ss51 & dsp");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04); //20121211
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_boot]release ss51 & dsp fail.");
+ ret = FAIL;
+ goto exit_burn_fw_boot;
+ }
+ //must delay
+ msleep(1);
+
+ //step6:select bank3
+ GTP_DEBUG("[burn_fw_boot]step6:select bank3");
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_boot]select bank3 fail.");
+ ret = FAIL;
+ goto exit_burn_fw_boot;
+ }
+
+ //step7:burn 2k bootloader firmware
+ GTP_DEBUG("[burn_fw_boot]step7:burn 2k bootloader firmware");
+ ret = gup_burn_proc(client, fw_boot, 0x9000, FW_BOOT_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_boot]burn fw_section fail.");
+ goto exit_burn_fw_boot;
+ }
+
+ //step7:send burn cmd to move data to flash from sram
+ GTP_DEBUG("[burn_fw_boot]step7:send burn cmd to move data to flash from sram");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x06);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_boot]send burn cmd fail.");
+ goto exit_burn_fw_boot;
+ }
+ GTP_DEBUG("[burn_fw_boot]Wait for the burn is complete......");
+ do{
+ ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_boot]Get burn state fail");
+ goto exit_burn_fw_boot;
+ }
+ msleep(10);
+ //GTP_DEBUG("[burn_fw_boot]Get burn state:%d.", rd_buf[GTP_ADDR_LENGTH]);
+ }while(rd_buf[GTP_ADDR_LENGTH]);
+
+ //step8:recall check 2k bootloader firmware
+ GTP_DEBUG("[burn_fw_boot]step8:recall check 2k bootloader firmware");
+ ret = gup_recall_check(client, fw_boot, 0x9000, FW_BOOT_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_boot]recall check 4k dsp firmware fail.");
+ goto exit_burn_fw_boot;
+ }
+
+ //step9:enable download DSP code
+ GTP_DEBUG("[burn_fw_boot]step9:enable download DSP code ");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x99);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_boot]enable download DSP code fail.");
+ ret = FAIL;
+ goto exit_burn_fw_boot;
+ }
+
+ //step10:release ss51 & hold dsp
+ GTP_DEBUG("[burn_fw_boot]step10:release ss51 & hold dsp");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x08);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_boot]release ss51 & hold dsp fail.");
+ ret = FAIL;
+ goto exit_burn_fw_boot;
+ }
+
+ ret = SUCCESS;
+
+exit_burn_fw_boot:
+ kfree(fw_boot);
+ return ret;
+}
+
+s32 gup_update_proc(void *dir)
+{
+ s32 ret = 0;
+ u8 retry = 0;
+ st_fw_head fw_head;
+ struct goodix_ts_data *ts = NULL;
+
+ GTP_DEBUG("[update_proc]Begin update ......");
+
+ show_len = 1;
+ total_len = 100;
+ if(dir == NULL)
+ {
+ msleep(3000); //wait main thread to be completed
+ }
+
+ ts = i2c_get_clientdata(i2c_connect_client);
+
+ if (searching_file)
+ {
+ searching_file = 0; // exit .bin update file searching
+ GTP_INFO("Exiting searching .bin update file...");
+ while ((show_len != 200) && (show_len != 100)) // wait for auto update quitted completely
+ {
+ msleep(100);
+ }
+ }
+
+ update_msg.file = NULL;
+ ret = gup_check_update_file(i2c_connect_client, &fw_head, (u8*)dir); //20121211
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[update_proc]check update file fail.");
+ goto file_fail;
+ }
+
+ //gtp_reset_guitar(i2c_connect_client, 20);
+ ret = gup_get_ic_fw_msg(i2c_connect_client);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[update_proc]get ic message fail.");
+ goto file_fail;
+ }
+
+ ret = gup_enter_update_judge(&fw_head);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[update_proc]Check *.bin file fail.");
+ goto file_fail;
+ }
+
+ ts->enter_update = 1;
+ gtp_irq_disable(ts);
+#if GTP_ESD_PROTECT
+ gtp_esd_switch(ts->client, SWITCH_OFF);
+#endif
+ ret = gup_enter_update_mode(i2c_connect_client);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[update_proc]enter update mode fail.");
+ goto update_fail;
+ }
+
+ while(retry++ < 5)
+ {
+ show_len = 10;
+ total_len = 100;
+ ret = gup_burn_dsp_isp(i2c_connect_client);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[update_proc]burn dsp isp fail.");
+ continue;
+ }
+
+ show_len += 10;
+ ret = gup_burn_fw_ss51(i2c_connect_client);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[update_proc]burn ss51 firmware fail.");
+ continue;
+ }
+
+ show_len += 40;
+ ret = gup_burn_fw_dsp(i2c_connect_client);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[update_proc]burn dsp firmware fail.");
+ continue;
+ }
+
+ show_len += 20;
+ ret = gup_burn_fw_boot(i2c_connect_client);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[update_proc]burn bootloader firmware fail.");
+ continue;
+ }
+ show_len += 10;
+ GTP_INFO("[update_proc]UPDATE SUCCESS.");
+ break;
+ }
+ if(retry >= 5)
+ {
+ GTP_ERROR("[update_proc]retry timeout,UPDATE FAIL.");
+ goto update_fail;
+ }
+
+ GTP_DEBUG("[update_proc]leave update mode.");
+ gup_leave_update_mode();
+
+ msleep(100);
+// GTP_DEBUG("[update_proc]send config.");
+// ret = gtp_send_cfg(i2c_connect_client);
+// if(ret < 0)
+// {
+// GTP_ERROR("[update_proc]send config fail.");
+// }
+ if (ts->fw_error)
+ {
+ GTP_INFO("firmware error auto update, resent config!");
+ gup_init_panel(ts);
+ }
+ show_len = 100;
+ total_len = 100;
+ ts->enter_update = 0;
+ gtp_irq_enable(ts);
+
+#if GTP_ESD_PROTECT
+ gtp_esd_switch(ts->client, SWITCH_ON);
+#endif
+ filp_close(update_msg.file, NULL);
+ return SUCCESS;
+
+update_fail:
+ ts->enter_update = 0;
+ gtp_irq_enable(ts);
+
+#if GTP_ESD_PROTECT
+ gtp_esd_switch(ts->client, SWITCH_ON);
+#endif
+
+file_fail:
+ if(update_msg.file && !IS_ERR(update_msg.file))
+ {
+ filp_close(update_msg.file, NULL);
+ }
+ show_len = 200;
+ total_len = 100;
+ return FAIL;
+}
+
+#if GTP_AUTO_UPDATE
+u8 gup_init_update_proc(struct goodix_ts_data *ts)
+{
+ struct task_struct *thread = NULL;
+
+ GTP_INFO("Ready to run update thread.");
+ thread = kthread_run(gup_update_proc, (void*)NULL, "guitar_update");
+ if (IS_ERR(thread))
+ {
+ GTP_ERROR("Failed to create update thread.\n");
+ return -1;
+ }
+
+ return 0;
+}
+#endif \ No newline at end of file
diff --git a/kernel/Documentation/firmware_updater/request_firmware.txt b/kernel/Documentation/firmware_updater/request_firmware.txt
new file mode 100644
index 000000000000..317f04ac5684
--- /dev/null
+++ b/kernel/Documentation/firmware_updater/request_firmware.txt
@@ -0,0 +1,22 @@
+Firmware Update Function
+========================
+
+Call export function "synaptics_fw_updater" in rmi_fw_update.c to start
+firmware updating process in the driver.
+
+The RMI4 driver uses the kernel's request_firmware() feature to obtain
+firmware for the touch sensor. The firmware is expected to live in
+the file firmware/<firmware_name>.img.ihex.
+
+To prepare Synaptics provided .img file for reflashing, convert it to .ihex
+format using the following command:
+
+ objcopy -I binary -O ihex <firmware_name>.img firmware/<firmware_name>.img.ihex
+
+Then make sure to add the image file name to the
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI4_FW_UPDATE entry in firmware/Makefile.
+If you don't do this, the image file won't be included, and
+the firmware loader class will delay for 60 seconds waiting for a non-existent
+userspace response to the firmware load request.
+
+Firmware updates for multichip solutions (aka LTS) are not supported.
diff --git a/kernel/Documentation/firmware_updater/synaptics_fw_updater b/kernel/Documentation/firmware_updater/synaptics_fw_updater
new file mode 100644
index 000000000000..b0c1b4d9e770
--- /dev/null
+++ b/kernel/Documentation/firmware_updater/synaptics_fw_updater
Binary files differ
diff --git a/kernel/Documentation/firmware_updater/synaptics_fw_updater.c b/kernel/Documentation/firmware_updater/synaptics_fw_updater.c
new file mode 100644
index 000000000000..7409dd424109
--- /dev/null
+++ b/kernel/Documentation/firmware_updater/synaptics_fw_updater.c
@@ -0,0 +1,753 @@
+/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Copyright © 2011, 2012 Synaptics Incorporated. All rights reserved.
+ *
+ * The information in this file is confidential under the terms
+ * of a non-disclosure agreement with Synaptics and is provided
+ * AS IS without warranties or guarantees of any kind.
+ *
+ * The information in this file shall remain the exclusive property
+ * of Synaptics and may be the subject of Synaptics patents, in
+ * whole or part. Synaptics intellectual property rights in the
+ * information in this file are not expressly or implicitly licensed
+ * or otherwise transferred to you as a result of such information
+ * being made available to you.
+ *
+ * File: synaptics_fw_updater.c
+ *
+ * Description: command line reflash implimentation using command
+ * line args. This file should not be OS dependant and should build and
+ * run under any Linux based OS that utilizes the Synaptice rmi driver
+ * built into the kernel (kernel/drivers/input/rmi4).
+ *
+ * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ */
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#define DEFAULT_SENSOR "/sys/class/input/input1"
+
+#define MAX_STRING_LEN 256
+#define MAX_INT_LEN 33
+
+#define DATA_FILENAME "data"
+#define IMAGESIZE_FILENAME "imagesize"
+#define DOREFLASH_FILENAME "doreflash"
+#define CONFIGAREA_FILENAME "configarea"
+#define READCONFIG_FILENAME "readconfig"
+#define WRITECONFIG_FILENAME "writeconfig"
+#define BLOCKSIZE_FILENAME "blocksize"
+#define IMAGEBLOCKCOUNT_FILENAME "fwblockcount"
+#define CONFIGBLOCKCOUNT_FILENAME "configblockcount"
+#define PMCONFIGBLOCKCOUNT_FILENAME "permconfigblockcount"
+#define BUILDID_FILENAME "buildid"
+#define FLASHPROG_FILENAME "flashprog"
+
+#define UI_CONFIG_AREA 0
+#define PERM_CONFIG_AREA 1
+#define BL_CONFIG_AREA 2
+#define DISP_CONFIG_AREA 3
+
+#define IMAGE_FILE_CHECKSUM_SIZE 4
+
+unsigned char *firmware = NULL;
+int fileSize;
+int firmwareBlockSize;
+int firmwareBlockCount;
+int firmwareImgSize;
+int configBlockSize;
+int configBlockCount;
+int configImgSize;
+int totalBlockCount;
+int readConfig = 0;
+int writeConfig = 0;
+int uiConfig = 0;
+int pmConfig = 0;
+int blConfig = 0;
+int dpConfig = 0;
+int force = 0;
+int verbose = 0;
+
+char mySensor[MAX_STRING_LEN];
+char imageFileName[MAX_STRING_LEN];
+
+static void usage(char *name)
+{
+ printf("Usage: %s [-b {image_file}] [-d {sysfs_entry}] [-r] [-ui] [-pm] [-bl] [-dp] [-f] [-v]\n", name);
+ printf("\t[-b {image_file}] - Name of image file\n");
+ printf("\t[-d {sysfs_entry}] - Path to sysfs entry of sensor\n");
+ printf("\t[-r] - Read config area\n");
+ printf("\t[-ui] - UI config area\n");
+ printf("\t[-pm] - Permanent config area\n");
+ printf("\t[-bl] - BL config area\n");
+ printf("\t[-dp] - Display config area\n");
+ printf("\t[-f] - Force reflash\n");
+ printf("\t[-v] - Verbose output\n");
+
+ return;
+}
+
+static void TimeSubtract(struct timeval *result, struct timeval *x, struct timeval *y)
+{
+ if (x->tv_usec < y->tv_usec) {
+ result->tv_sec = x->tv_sec - y->tv_sec - 1;
+ result->tv_usec = y->tv_usec - x->tv_usec;
+ } else {
+ result->tv_sec = x->tv_sec - y->tv_sec;
+ result->tv_usec = x->tv_usec - y->tv_usec;
+ }
+
+ return;
+}
+
+static int CheckSysfsEntry(char *sensorName)
+{
+ int retval;
+ struct stat st;
+
+ retval = stat(sensorName, &st);
+ if (retval)
+ printf("ERROR: sensor sysfs entry %s not found\n", sensorName);
+
+ return retval;
+}
+
+static void WriteBinData(char *fname, unsigned char *buf, int len)
+{
+ int numBytesWritten;
+ FILE *fp;
+
+ fp = fopen(fname, "wb");
+ if (!fp) {
+ printf("ERROR: failed to open %s for writing data\n", fname);
+ exit(EIO);
+ }
+
+ numBytesWritten = fwrite(buf, 1, len, fp);
+
+ if (numBytesWritten != len) {
+ printf("ERROR: failed to write all data to bin file\n");
+ fclose(fp);
+ exit(EIO);
+ }
+
+ fclose(fp);
+
+ return;
+}
+
+static void ReadBinData(char *fname, unsigned char *buf, int len)
+{
+ int numBytesRead;
+ FILE *fp;
+
+ fp = fopen(fname, "rb");
+ if (!fp) {
+ printf("ERROR: failed to open %s for reading data\n", fname);
+ exit(EIO);
+ }
+
+ numBytesRead = fread(buf, 1, len, fp);
+
+ if (numBytesRead != len) {
+ printf("ERROR: failed to read all data from bin file\n");
+ fclose(fp);
+ exit(EIO);
+ }
+
+ fclose(fp);
+
+ return;
+}
+
+static void WriteValueToFp(FILE *fp, unsigned int value)
+{
+ int numBytesWritten;
+ char buf[MAX_INT_LEN];
+
+ snprintf(buf, MAX_INT_LEN, "%u", value);
+
+ fseek(fp, 0, 0);
+
+ numBytesWritten = fwrite(buf, 1, strlen(buf) + 1, fp);
+ if (numBytesWritten != ((int)(strlen(buf) + 1))) {
+ printf("ERROR: failed to write value to file pointer\n");
+ fclose(fp);
+ exit(EIO);
+ }
+
+ return;
+}
+
+static void WriteValueToSysfsFile(char *fname, unsigned int value)
+{
+ FILE *fp;
+
+ fp = fopen(fname, "w");
+ if (!fp) {
+ printf("ERROR: failed to open %s for writing value\n", fname);
+ exit(EIO);
+ }
+
+ WriteValueToFp(fp, value);
+
+ fclose(fp);
+
+ return;
+}
+
+static void ReadValueFromFp(FILE *fp, unsigned int *value)
+{
+ int retVal;
+ char buf[MAX_INT_LEN];
+
+ fseek(fp, 0, 0);
+
+ retVal = fread(buf, 1, sizeof(buf), fp);
+ if (retVal == -1) {
+ printf("ERROR: failed to read value from file pointer\n");
+ exit(EIO);
+ }
+
+ *value = strtoul(buf, NULL, 0);
+
+ return;
+}
+
+static void ReadValueFromSysfsFile(char *fname, unsigned int *value)
+{
+ FILE *fp;
+
+ fp = fopen(fname, "r");
+ if (!fp) {
+ printf("ERROR: failed to open %s for reading value\n", fname);
+ exit(EIO);
+ }
+
+ ReadValueFromFp(fp, value);
+
+ fclose(fp);
+
+ return;
+}
+
+static void WriteBlockData(char *buf, int len)
+{
+ char tmpfname[MAX_STRING_LEN];
+
+ snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, DATA_FILENAME);
+
+ WriteBinData(tmpfname, (unsigned char *)buf, len);
+
+ return;
+}
+
+static void ReadBlockData(char *buf, int len)
+{
+ char tmpfname[MAX_STRING_LEN];
+
+ snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, DATA_FILENAME);
+
+ ReadBinData(tmpfname, (unsigned char *)buf, len);
+
+ return;
+}
+
+static void SetImageSize(int value)
+{
+ char tmpfname[MAX_STRING_LEN];
+
+ snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, IMAGESIZE_FILENAME);
+
+ WriteValueToSysfsFile(tmpfname, value);
+
+ return;
+}
+
+static void StartReflash(int value)
+{
+ char tmpfname[MAX_STRING_LEN];
+
+ snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, DOREFLASH_FILENAME);
+
+ WriteValueToSysfsFile(tmpfname, value);
+
+ return;
+}
+
+static void SetConfigArea(int value)
+{
+ char tmpfname[MAX_STRING_LEN];
+
+ snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, CONFIGAREA_FILENAME);
+
+ WriteValueToSysfsFile(tmpfname, value);
+
+ return;
+}
+
+static void StartWriteConfig(int value)
+{
+ char tmpfname[MAX_STRING_LEN];
+
+ snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, WRITECONFIG_FILENAME);
+
+ WriteValueToSysfsFile(tmpfname, value);
+
+ return;
+}
+
+static void StartReadConfig(int value)
+{
+ char tmpfname[MAX_STRING_LEN];
+
+ snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, READCONFIG_FILENAME);
+
+ WriteValueToSysfsFile(tmpfname, value);
+
+ return;
+}
+
+static int ReadBlockSize(void)
+{
+ unsigned int blockSize;
+ char tmpfname[MAX_STRING_LEN];
+
+ snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, BLOCKSIZE_FILENAME);
+
+ ReadValueFromSysfsFile(tmpfname, &blockSize);
+
+ return blockSize;
+}
+
+static int ReadFirmwareBlockCount(void)
+{
+ unsigned int imageBlockCount;
+ char tmpfname[MAX_STRING_LEN];
+
+ snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, IMAGEBLOCKCOUNT_FILENAME);
+
+ ReadValueFromSysfsFile(tmpfname, &imageBlockCount);
+
+ return imageBlockCount;
+}
+
+static int ReadConfigBlockCount(void)
+{
+ unsigned int configBlockCount;
+ char tmpfname[MAX_STRING_LEN];
+
+ snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, CONFIGBLOCKCOUNT_FILENAME);
+
+ ReadValueFromSysfsFile(tmpfname, &configBlockCount);
+
+ return configBlockCount;
+}
+
+static int ReadPmConfigBlockCount(void)
+{
+ unsigned int configBlockCount;
+ char tmpfname[MAX_STRING_LEN];
+
+ snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, PMCONFIGBLOCKCOUNT_FILENAME);
+
+ ReadValueFromSysfsFile(tmpfname, &configBlockCount);
+
+ return configBlockCount;
+}
+
+static int ReadBuildID(void)
+{
+ unsigned int buildID;
+ char tmpfname[MAX_STRING_LEN];
+
+ snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, BUILDID_FILENAME);
+
+ ReadValueFromSysfsFile(tmpfname, &buildID);
+
+ return buildID;
+}
+
+static int ReadFlashProg(void)
+{
+ unsigned int flashProg;
+ char tmpfname[MAX_STRING_LEN];
+
+ snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, FLASHPROG_FILENAME);
+
+ ReadValueFromSysfsFile(tmpfname, &flashProg);
+
+ return flashProg;
+}
+
+static void ReadFirmwareInfo(void)
+{
+ firmwareBlockSize = ReadBlockSize();
+ firmwareBlockCount = ReadFirmwareBlockCount();
+ firmwareImgSize = firmwareBlockCount * firmwareBlockSize;
+
+ return;
+}
+
+static void ReadConfigInfo(void)
+{
+ configBlockSize = ReadBlockSize();
+ configBlockCount = ReadConfigBlockCount();
+ configImgSize = configBlockSize * configBlockCount;
+
+ return;
+}
+
+static void CalculateChecksum(unsigned short *data, unsigned short len, unsigned long *result)
+{
+ unsigned long temp;
+ unsigned long sum1 = 0xffff;
+ unsigned long sum2 = 0xffff;
+
+ *result = 0xffffffff;
+
+ while (len--) {
+ temp = *data;
+ sum1 += temp;
+ sum2 += sum1;
+ sum1 = (sum1 & 0xffff) + (sum1 >> 16);
+ sum2 = (sum2 & 0xffff) + (sum2 >> 16);
+ data++;
+ }
+
+ *result = sum2 << 16 | sum1;
+
+ return;
+}
+
+static int CompareChecksum(void)
+{
+ unsigned long headerChecksum;
+ unsigned long computedChecksum;
+
+ headerChecksum = (unsigned long)firmware[0] +
+ (unsigned long)firmware[1] * 0x100 +
+ (unsigned long)firmware[2] * 0x10000 +
+ (unsigned long)firmware[3] * 0x1000000;
+
+ CalculateChecksum((unsigned short *)&firmware[IMAGE_FILE_CHECKSUM_SIZE],
+ ((fileSize - IMAGE_FILE_CHECKSUM_SIZE) / 2), &computedChecksum);
+
+ if (verbose) {
+ printf("Checksum in image file header = 0x%08x\n", (unsigned int)headerChecksum);
+ printf("Checksum computed from image file = 0x%08x\n", (unsigned int)computedChecksum);
+ }
+
+ if (headerChecksum == computedChecksum)
+ return 1;
+ else
+ return 0;
+}
+
+static int ProceedWithReflash(void)
+{
+ int index = 0;
+ int deviceBuildID;
+ int imageBuildID;
+ char imagePR[MAX_STRING_LEN];
+ char *strptr;
+
+ if (force) {
+ printf("Force reflash...\n");
+ return 1;
+ }
+
+ if (ReadFlashProg()) {
+ printf("Force reflash (device in flash prog mode)...\n");
+ return 1;
+ }
+
+ strptr = strstr(imageFileName, "PR");
+ if (!strptr) {
+ printf("No valid PR number (PRxxxxxxx) found in image file name...\n");
+ return 0;
+ }
+
+ strptr += 2;
+ while (strptr[index] >= '0' && strptr[index] <= '9') {
+ imagePR[index] = strptr[index];
+ index++;
+ }
+ imagePR[index] = 0;
+
+ imageBuildID = strtoul(imagePR, NULL, 0);
+ deviceBuildID = ReadBuildID();
+ printf("Image file PR = %d\n", imageBuildID);
+ printf("Device PR = %d\n", deviceBuildID);
+
+ if (imageBuildID > deviceBuildID) {
+ printf("Proceed with reflash...\n");
+ return 1;
+ } else {
+ printf("No need to do reflash...\n");
+ return 0;
+ }
+}
+
+static void DoReadConfig(void)
+{
+ int ii;
+ int jj;
+ int index = 0;
+ int configSize;
+ int blockCount;
+ unsigned char *buffer;
+
+ if (uiConfig) {
+ SetConfigArea(UI_CONFIG_AREA);
+ StartReadConfig(1);
+ blockCount = configBlockCount;
+ configSize = configImgSize;
+ buffer = malloc(configSize);
+ if (!buffer)
+ exit(ENOMEM);
+ ReadBlockData((char *)&buffer[0], configSize);
+ } else if (pmConfig) {
+ SetConfigArea(PERM_CONFIG_AREA);
+ StartReadConfig(1);
+ blockCount = ReadPmConfigBlockCount();
+ configSize = configBlockSize * blockCount;
+ buffer = malloc(configSize);
+ if (!buffer)
+ exit(ENOMEM);
+ ReadBlockData((char *)&buffer[0], configSize);
+ } else {
+ return;
+ }
+
+ for (ii = 0; ii < blockCount; ii++) {
+ for (jj = 0; jj < configBlockSize; jj++) {
+ printf("0x%02x ", buffer[index]);
+ index++;
+ }
+ printf("\n");
+ }
+
+ free(buffer);
+
+ return;
+}
+
+static void DoWriteConfig(void)
+{
+ printf("Starting config programming...\n");
+
+ if (uiConfig)
+ SetConfigArea(UI_CONFIG_AREA);
+ else if (pmConfig)
+ SetConfigArea(PERM_CONFIG_AREA);
+ else if (blConfig)
+ SetConfigArea(BL_CONFIG_AREA);
+ else if (dpConfig)
+ SetConfigArea(DISP_CONFIG_AREA);
+ else
+ return;
+
+ SetImageSize(fileSize);
+ WriteBlockData((char *)&firmware[0], fileSize);
+ StartWriteConfig(1);
+
+ printf("Config programming completed...\n");
+
+ return;
+}
+
+static void DoReflash(void)
+{
+ if (verbose)
+ printf("Blocks: %d (firmware: %d, config: %d)\n", totalBlockCount, firmwareBlockCount, configBlockCount);
+
+ if (!ProceedWithReflash())
+ return;
+
+ printf("Starting reflash...\n");
+
+ SetImageSize(fileSize);
+ WriteBlockData((char *)&firmware[0], fileSize);
+ StartReflash(1);
+
+ printf("Reflash completed...\n");
+
+ return;
+}
+
+static int InitFirmwareImage(void)
+{
+ int numBytesRead;
+ FILE *fp;
+
+ if (!readConfig) {
+ fp = fopen(imageFileName, "rb");
+
+ if (!fp) {
+ printf("ERROR: image file %s not found\n", imageFileName);
+ exit(ENODEV);
+ }
+
+ fseek(fp, 0L, SEEK_END);
+ fileSize = ftell(fp);
+ if (fileSize == -1) {
+ printf("ERROR: failed to determine size of %s\n", imageFileName);
+ exit(EIO);
+ }
+
+ fseek(fp, 0L, SEEK_SET);
+
+ firmware = malloc(fileSize + 1);
+ if (!firmware) {
+ exit(ENOMEM);
+ } else {
+ numBytesRead = fread(firmware, 1, fileSize, fp);
+ if (numBytesRead != fileSize) {
+ printf("ERROR: failed to read entire content of image file\n");
+ exit(EIO);
+ }
+ }
+
+ fclose(fp);
+
+ if (!(pmConfig || blConfig || dpConfig)) {
+ if (!CompareChecksum()) {
+ printf("ERROR: failed to validate checksum of image file\n");
+ exit(EINVAL);
+ }
+ }
+ }
+
+ return 0;
+}
+
+int main(int argc, char* argv[])
+{
+ int retVal;
+ int this_arg = 1;
+ struct stat st;
+ struct timeval start_time;
+ struct timeval end_time;
+ struct timeval elapsed_time;
+
+ if (argc == 1) {
+ usage(argv[0]);
+ exit(EINVAL);
+ }
+
+ while (this_arg < argc) {
+ if (!strcmp((const char *)argv[this_arg], "-b")) {
+ /* Image file */
+ FILE *file;
+
+ this_arg++;
+ if (this_arg >= argc) {
+ printf("ERROR: image file missing\n");
+ exit(EINVAL);
+ }
+
+ /* check for presence of image file */
+ file = fopen(argv[this_arg], "rb");
+ if (file == 0) {
+ printf("ERROR: image file %s not found\n", argv[this_arg]);
+ exit(EINVAL);
+ }
+ fclose(file);
+
+ strncpy(imageFileName, argv[this_arg], MAX_STRING_LEN);
+ } else if (!strcmp((const char *)argv[this_arg], "-d")) {
+ /* path to sensor sysfs entry */
+ this_arg++;
+
+ if (stat(argv[this_arg], &st) == 0) {
+ strncpy(mySensor, argv[this_arg], MAX_STRING_LEN);
+ } else {
+ printf("ERROR: sensor sysfs entry %s not found\n", argv[this_arg]);
+ exit(EINVAL);
+ }
+ } else if (!strcmp((const char *)argv[this_arg], "-r")) {
+ readConfig = 1;
+ } else if (!strcmp((const char *)argv[this_arg], "-ui")) {
+ uiConfig = 1;
+ } else if (!strcmp((const char *)argv[this_arg], "-pm")) {
+ pmConfig = 1;
+ } else if (!strcmp((const char *)argv[this_arg], "-bl")) {
+ blConfig = 1;
+ } else if (!strcmp((const char *)argv[this_arg], "-dp")) {
+ dpConfig = 1;
+ } else if (!strcmp((const char *)argv[this_arg], "-f")) {
+ force = 1;
+ } else if (!strcmp((const char *)argv[this_arg], "-v")) {
+ verbose = 1;
+ } else {
+ usage(argv[0]);
+ printf("ERROR: invalid parameter %s supplied\n", argv[this_arg]);
+ exit(EINVAL);
+ }
+ this_arg++;
+ }
+
+ if ((uiConfig + pmConfig + blConfig + dpConfig) > 1) {
+ printf("ERROR: too many parameters\n");
+ exit(EINVAL);
+ }
+
+ if (uiConfig || pmConfig || blConfig || dpConfig)
+ writeConfig = 1;
+
+ if (!readConfig && !strlen(imageFileName)) {
+ printf("ERROR: no image file specified\n");
+ exit(EINVAL);
+ }
+
+ if (!strlen(mySensor))
+ strncpy(mySensor, DEFAULT_SENSOR, MAX_STRING_LEN);
+
+ if (CheckSysfsEntry(mySensor))
+ exit(ENODEV);
+
+ InitFirmwareImage();
+
+ ReadFirmwareInfo();
+ ReadConfigInfo();
+ totalBlockCount = configBlockCount + firmwareBlockCount;
+
+ retVal = gettimeofday(&start_time, NULL);
+ if (retVal)
+ printf("WARNING: failed to get start time\n");
+
+ if (verbose) {
+ if (!readConfig)
+ printf("Image file: %s\n", imageFileName);
+ printf("Sensor sysfs entry: %s\n", mySensor);
+ }
+
+ if (readConfig)
+ DoReadConfig();
+ else if (writeConfig)
+ DoWriteConfig();
+ else
+ DoReflash();
+
+ retVal = gettimeofday(&end_time, NULL);
+ if (retVal)
+ printf("WARNING: failed to get end time\n");
+
+ TimeSubtract(&elapsed_time, &end_time, &start_time);
+
+ if (verbose) {
+ printf("Elapsed time = %ld.%06ld seconds\n",
+ (long)elapsed_time.tv_sec,
+ (long)elapsed_time.tv_usec);
+ }
+
+ return 0;
+}
diff --git a/kernel/Documentation/firmware_updater/synaptics_fw_updater_readme.txt b/kernel/Documentation/firmware_updater/synaptics_fw_updater_readme.txt
new file mode 100644
index 000000000000..66f71922995a
--- /dev/null
+++ b/kernel/Documentation/firmware_updater/synaptics_fw_updater_readme.txt
@@ -0,0 +1,41 @@
+Use ADB (Android Debug Bridge) to do command-line reflash
+- Power on device.
+- Connect device to host via USB.
+- Open command prompt on host and go to directory where adb, synaptics_fw_updater, and FW image (e.g. PR1234567.img) reside.
+- Run "adb devices" to ensure connection with device.
+- Run "adb root" to have root privileges.
+- Run "adb push synaptics_fw_updater /data" to copy synaptics_fw_updater to /data directory on device.
+- Run "adb push firmware.img /data" to copy firmware.img to /data directory on device.
+- Run "adb shell chmod 777 /data/synaptics_fw_updater" to make synaptics_fw_updater executable.
+- Run "adb shell /data/synaptics_fw_updater -b /data/PR1234567.img -f -v" to start reflash process.
+
+Parameters
+[-b {image_file}] - Name of image file
+[-d {sysfs_entry}] - Path to sysfs entry of sensor
+[-r] - Read config area
+[-ui] - UI config area
+[-pm] - Permanent config area
+[-bl] - BL config area
+[-dp] - Display config area
+[-f] - Force reflash
+[-v] - Verbose output
+
+Procedures for checking whether to proceed with reflash
+- If [-f] flag is set, proceed with reflash
+- If device is in flash prog (bootloader) mode, proceed with reflash
+- If PR number contained in name of new FW image is greater than PR number of FW on device, proceed with reflash.
+- Otherwise, no reflash is performed
+
+Usage examples
+- Perform reflash using PR1234567.img regardless of PR number of FW on device
+ synaptics_fw_updater -b PR1234567.img -f
+- Perform reflash using PR1234567.img only if 1234567 is greater than PR number of FW on device.
+ synaptics_fw_updater -b PR1234567.img
+- Write UI config area from PR1234567.img (parsing UI config area from firmware image file)
+ synaptics_fw_updater -b PR1234567.img -ui
+- Write permanent config area from pmconfig.img (binary file containing permanent config data)
+ synaptics_fw_updater -b pmconfig.img -pm
+- Read UI config area
+ synaptics_fw_updater -r -ui
+- Read permanent config area
+ synaptics_fw_updater -r -pm \ No newline at end of file
diff --git a/kernel/arch/arm/configs/omap3_beagle_android_defconfig b/kernel/arch/arm/configs/omap3_beagle_android_defconfig
new file mode 100644
index 000000000000..4fc62c4fa440
--- /dev/null
+++ b/kernel/arch/arm/configs/omap3_beagle_android_defconfig
@@ -0,0 +1,2419 @@
+#
+# Automatically generated make config: don't edit
+# Linux/arm 2.6.37 Kernel Configuration
+# Mon Apr 16 13:58:06 2012
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_GPIO=y
+# CONFIG_ARCH_USES_GETTIMEOFFSET is not set
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_HAVE_PROC_CPU=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_ARCH_HAS_CPUFREQ=y
+CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_ARM_L1_CACHE_SHIFT_6=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_CONSTRUCTORS=y
+CONFIG_HAVE_IRQ_WORK=y
+CONFIG_IRQ_WORK=y
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_CROSS_COMPILE=""
+CONFIG_LOCALVERSION=""
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_HAVE_KERNEL_GZIP=y
+CONFIG_HAVE_KERNEL_LZMA=y
+CONFIG_HAVE_KERNEL_LZO=y
+CONFIG_KERNEL_GZIP=y
+# CONFIG_KERNEL_LZMA is not set
+# CONFIG_KERNEL_LZO is not set
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_POSIX_MQUEUE_SYSCTL=y
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+# CONFIG_HAVE_GENERIC_HARDIRQS is not set
+# CONFIG_SPARSE_IRQ is not set
+
+#
+# RCU Subsystem
+#
+CONFIG_TINY_RCU=y
+# CONFIG_PREEMPT_RCU is not set
+# CONFIG_TREE_RCU_TRACE is not set
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=16
+# CONFIG_CGROUPS is not set
+# CONFIG_NAMESPACES is not set
+# CONFIG_SYSFS_DEPRECATED is not set
+# CONFIG_RELAY is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_RD_GZIP=y
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_LZMA is not set
+# CONFIG_RD_LZO is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
+CONFIG_PANIC_TIMEOUT=0
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+# CONFIG_SYSCTL_SYSCALL is not set
+CONFIG_KALLSYMS=y
+CONFIG_KALLSYMS_EXTRA_PASS=y
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_ASHMEM=y
+CONFIG_AIO=y
+CONFIG_HAVE_PERF_EVENTS=y
+CONFIG_PERF_USE_VMALLOC=y
+
+#
+# Kernel Performance Events And Counters
+#
+CONFIG_PERF_EVENTS=y
+# CONFIG_PERF_COUNTERS is not set
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_COMPAT_BRK=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+CONFIG_PROFILING=y
+CONFIG_TRACEPOINTS=y
+CONFIG_OPROFILE=y
+CONFIG_HAVE_OPROFILE=y
+CONFIG_KPROBES=y
+CONFIG_KRETPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_HW_BREAKPOINT=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_GCOV_KERNEL is not set
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+CONFIG_BLOCK=y
+CONFIG_LBDAF=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_DEFAULT_DEADLINE is not set
+CONFIG_DEFAULT_CFQ=y
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="cfq"
+# CONFIG_INLINE_SPIN_TRYLOCK is not set
+# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set
+# CONFIG_INLINE_SPIN_LOCK is not set
+# CONFIG_INLINE_SPIN_LOCK_BH is not set
+# CONFIG_INLINE_SPIN_LOCK_IRQ is not set
+# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set
+CONFIG_INLINE_SPIN_UNLOCK=y
+# CONFIG_INLINE_SPIN_UNLOCK_BH is not set
+CONFIG_INLINE_SPIN_UNLOCK_IRQ=y
+# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set
+# CONFIG_INLINE_READ_TRYLOCK is not set
+# CONFIG_INLINE_READ_LOCK is not set
+# CONFIG_INLINE_READ_LOCK_BH is not set
+# CONFIG_INLINE_READ_LOCK_IRQ is not set
+# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set
+CONFIG_INLINE_READ_UNLOCK=y
+# CONFIG_INLINE_READ_UNLOCK_BH is not set
+CONFIG_INLINE_READ_UNLOCK_IRQ=y
+# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set
+# CONFIG_INLINE_WRITE_TRYLOCK is not set
+# CONFIG_INLINE_WRITE_LOCK is not set
+# CONFIG_INLINE_WRITE_LOCK_BH is not set
+# CONFIG_INLINE_WRITE_LOCK_IRQ is not set
+# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set
+CONFIG_INLINE_WRITE_UNLOCK=y
+# CONFIG_INLINE_WRITE_UNLOCK_BH is not set
+CONFIG_INLINE_WRITE_UNLOCK_IRQ=y
+# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set
+# CONFIG_MUTEX_SPIN_ON_OWNER is not set
+CONFIG_FREEZER=y
+
+#
+# System Type
+#
+CONFIG_MMU=y
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_VEXPRESS is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_BCMRING is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_CNS3XXX is not set
+# CONFIG_ARCH_GEMINI is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_STMP3XXX is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_DOVE is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_LOKI is not set
+# CONFIG_ARCH_LPC32XX is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_W90X900 is not set
+# CONFIG_ARCH_NUC93X is not set
+# CONFIG_ARCH_TEGRA is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_SHMOBILE is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_S3C64XX is not set
+# CONFIG_ARCH_S5P64X0 is not set
+# CONFIG_ARCH_S5P6442 is not set
+# CONFIG_ARCH_S5PC100 is not set
+# CONFIG_ARCH_S5PV210 is not set
+# CONFIG_ARCH_S5PV310 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_TCC_926 is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_U300 is not set
+# CONFIG_ARCH_U8500 is not set
+# CONFIG_ARCH_NOMADIK is not set
+# CONFIG_ARCH_DAVINCI is not set
+CONFIG_ARCH_OMAP=y
+# CONFIG_PLAT_SPEAR is not set
+
+#
+# TI OMAP Common Features
+#
+CONFIG_ARCH_OMAP_OTG=y
+# CONFIG_ARCH_OMAP1 is not set
+CONFIG_ARCH_OMAP2PLUS=y
+
+#
+# OMAP Feature Selections
+#
+CONFIG_OMAP_SMARTREFLEX=y
+CONFIG_OMAP_SMARTREFLEX_CLASS3=y
+CONFIG_OMAP_RESET_CLOCKS=y
+CONFIG_OMAP_MUX=y
+CONFIG_OMAP_MUX_DEBUG=y
+CONFIG_OMAP_MUX_WARNINGS=y
+CONFIG_OMAP_MCBSP=y
+# CONFIG_OMAP_MBOX_FWK is not set
+CONFIG_OMAP_IOMMU=y
+# CONFIG_OMAP_IOMMU_DEBUG is not set
+# CONFIG_OMAP_MPU_TIMER is not set
+CONFIG_OMAP_32K_TIMER=y
+# CONFIG_OMAP3_L2_AUX_SECURE_SAVE_RESTORE is not set
+CONFIG_OMAP_32K_TIMER_HZ=128
+CONFIG_OMAP_DM_TIMER=y
+# CONFIG_OMAP_PM_NONE is not set
+CONFIG_OMAP_PM_NOOP=y
+
+#
+# TI OMAP2/3/4 Specific Features
+#
+CONFIG_ARCH_OMAP2PLUS_TYPICAL=y
+# CONFIG_ARCH_OMAP2 is not set
+CONFIG_ARCH_OMAP3=y
+# CONFIG_ARCH_OMAP4 is not set
+# CONFIG_ARCH_TI81XX is not set
+CONFIG_ARCH_OMAP3430=y
+CONFIG_OMAP_PACKAGE_CBB=y
+
+#
+# OMAP Board Type
+#
+CONFIG_MACH_OMAP3_BEAGLE=y
+# CONFIG_MACH_DEVKIT8000 is not set
+# CONFIG_MACH_OMAP_LDP is not set
+# CONFIG_MACH_OMAP3530_LV_SOM is not set
+# CONFIG_MACH_OMAP3_TORPEDO is not set
+# CONFIG_MACH_OVERO is not set
+# CONFIG_MACH_OMAP3EVM is not set
+# CONFIG_MACH_FLASHBOARD is not set
+# CONFIG_MACH_OMAP3517EVM is not set
+# CONFIG_MACH_CRANEBOARD is not set
+# CONFIG_MACH_OMAP3_PANDORA is not set
+# CONFIG_MACH_OMAP3_TOUCHBOOK is not set
+# CONFIG_MACH_OMAP_3430SDP is not set
+# CONFIG_MACH_NOKIA_RM680 is not set
+# CONFIG_MACH_NOKIA_RX51 is not set
+# CONFIG_MACH_OMAP_ZOOM2 is not set
+# CONFIG_MACH_OMAP_ZOOM3 is not set
+# CONFIG_MACH_CM_T35 is not set
+# CONFIG_MACH_CM_T3517 is not set
+# CONFIG_MACH_IGEP0020 is not set
+# CONFIG_MACH_IGEP0030 is not set
+# CONFIG_MACH_SBC3530 is not set
+# CONFIG_MACH_OMAP_3630SDP is not set
+# CONFIG_OMAP3_EMU is not set
+CONFIG_OMAP3_PM_DISABLE_VT_SWITCH=y
+# CONFIG_OMAP3_SDRC_AC_TIMING is not set
+
+#
+# Processor Type
+#
+CONFIG_CPU_32v6K=y
+CONFIG_CPU_V7=y
+CONFIG_CPU_32v7=y
+CONFIG_CPU_ABRT_EV7=y
+CONFIG_CPU_PABRT_V7=y
+CONFIG_CPU_CACHE_V7=y
+CONFIG_CPU_CACHE_VIPT=y
+CONFIG_CPU_COPY_V6=y
+CONFIG_CPU_TLB_V7=y
+CONFIG_CPU_HAS_ASID=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+CONFIG_ARM_THUMBEE=y
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+CONFIG_ARM_L1_CACHE_SHIFT=6
+CONFIG_ARM_DMA_MEM_BUFFERABLE=y
+# CONFIG_ARM_ERRATA_430973 is not set
+# CONFIG_ARM_ERRATA_458693 is not set
+# CONFIG_ARM_ERRATA_460075 is not set
+# CONFIG_ARM_ERRATA_743622 is not set
+CONFIG_COMMON_CLKDEV=y
+# CONFIG_FIQ_DEBUGGER is not set
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PREEMPT is not set
+CONFIG_HZ=128
+# CONFIG_THUMB2_KERNEL is not set
+CONFIG_AEABI=y
+CONFIG_OABI_COMPAT=y
+CONFIG_ARCH_HAS_HOLES_MEMORYMODEL=y
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+# CONFIG_HIGHMEM is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_HAVE_MEMBLOCK=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_VIRT_TO_BUS=y
+# CONFIG_KSM is not set
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+CONFIG_NEED_PER_CPU_KM=y
+CONFIG_FORCE_MAX_ZONEORDER=11
+# CONFIG_LEDS is not set
+CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_UACCESS_WITH_MEMCPY is not set
+# CONFIG_SECCOMP is not set
+# CONFIG_CC_STACKPROTECTOR is not set
+# CONFIG_DEPRECATED_PARAM_STRUCT is not set
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="root=/dev/mmcblk0p2 rootwait console=ttyO2,115200"
+# CONFIG_CMDLINE_FORCE is not set
+# CONFIG_XIP_KERNEL is not set
+CONFIG_KEXEC=y
+CONFIG_ATAGS_PROC=y
+# CONFIG_AUTO_ZRELADDR is not set
+
+#
+# CPU Power Management
+#
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_TABLE=y
+# CONFIG_CPU_FREQ_DEBUG is not set
+CONFIG_CPU_FREQ_STAT=y
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+# CONFIG_CPU_FREQ_GOV_INTERACTIVE is not set
+# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
+CONFIG_CPU_IDLE=y
+CONFIG_CPU_IDLE_GOV_LADDER=y
+CONFIG_CPU_IDLE_GOV_MENU=y
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_FPE_NWFPE=y
+# CONFIG_FPE_NWFPE_XP is not set
+# CONFIG_FPE_FASTFPE is not set
+CONFIG_VFP=y
+CONFIG_VFPv3=y
+CONFIG_NEON=y
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
+CONFIG_HAVE_AOUT=y
+# CONFIG_BINFMT_AOUT is not set
+CONFIG_BINFMT_MISC=y
+
+#
+# Power management options
+#
+CONFIG_PM=y
+CONFIG_PM_DEBUG=y
+# CONFIG_PM_ADVANCED_DEBUG is not set
+# CONFIG_PM_VERBOSE is not set
+CONFIG_CAN_PM_TRACE=y
+CONFIG_PM_SLEEP=y
+CONFIG_SUSPEND_NVS=y
+CONFIG_SUSPEND=y
+# CONFIG_PM_TEST_SUSPEND is not set
+CONFIG_SUSPEND_FREEZER=y
+CONFIG_HAS_WAKELOCK=y
+CONFIG_HAS_EARLYSUSPEND=y
+CONFIG_WAKELOCK=y
+CONFIG_WAKELOCK_STAT=y
+CONFIG_USER_WAKELOCK=y
+CONFIG_EARLYSUSPEND=y
+# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set
+# CONFIG_CONSOLE_EARLYSUSPEND is not set
+CONFIG_FB_EARLYSUSPEND=y
+# CONFIG_APM_EMULATION is not set
+CONFIG_PM_RUNTIME=y
+CONFIG_PM_OPS=y
+CONFIG_ARCH_HAS_OPP=y
+CONFIG_PM_OPP=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=y
+# CONFIG_XFRM_SUB_POLICY is not set
+CONFIG_XFRM_MIGRATE=y
+# CONFIG_XFRM_STATISTICS is not set
+CONFIG_NET_KEY=y
+CONFIG_NET_KEY_MIGRATE=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE_DEMUX is not set
+# CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+CONFIG_INET_TUNNEL=y
+CONFIG_INET_XFRM_MODE_TRANSPORT=y
+CONFIG_INET_XFRM_MODE_TUNNEL=y
+CONFIG_INET_XFRM_MODE_BEET=y
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+CONFIG_IPV6=y
+# CONFIG_IPV6_PRIVACY is not set
+# CONFIG_IPV6_ROUTER_PREF is not set
+# CONFIG_IPV6_OPTIMISTIC_DAD is not set
+# CONFIG_INET6_AH is not set
+# CONFIG_INET6_ESP is not set
+# CONFIG_INET6_IPCOMP is not set
+# CONFIG_IPV6_MIP6 is not set
+# CONFIG_INET6_XFRM_TUNNEL is not set
+# CONFIG_INET6_TUNNEL is not set
+CONFIG_INET6_XFRM_MODE_TRANSPORT=y
+CONFIG_INET6_XFRM_MODE_TUNNEL=y
+CONFIG_INET6_XFRM_MODE_BEET=y
+# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
+CONFIG_IPV6_SIT=y
+# CONFIG_IPV6_SIT_6RD is not set
+CONFIG_IPV6_NDISC_NODETYPE=y
+# CONFIG_IPV6_TUNNEL is not set
+# CONFIG_IPV6_MULTIPLE_TABLES is not set
+# CONFIG_IPV6_MROUTE is not set
+# CONFIG_NETLABEL is not set
+CONFIG_ANDROID_PARANOID_NETWORK=y
+CONFIG_NET_ACTIVITY_STATS=y
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETWORK_PHY_TIMESTAMPING is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_RDS is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_L2TP is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_NET_DSA is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_PHONET is not set
+# CONFIG_IEEE802154 is not set
+# CONFIG_NET_SCHED is not set
+# CONFIG_DCB is not set
+CONFIG_DNS_RESOLVER=y
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_NET_TCPPROBE is not set
+# CONFIG_NET_DROP_MONITOR is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+CONFIG_WIRELESS=y
+CONFIG_WIRELESS_EXT=y
+CONFIG_WEXT_CORE=y
+CONFIG_WEXT_PROC=y
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_EXT_SYSFS=y
+# CONFIG_LIB80211 is not set
+
+#
+# CFG80211 needs to be enabled for MAC80211
+#
+
+#
+# Some wireless drivers require a rate control algorithm
+#
+# CONFIG_WIMAX is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+# CONFIG_CAIF is not set
+# CONFIG_CEPH_LIB is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+# CONFIG_DEVTMPFS is not set
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_TESTS is not set
+CONFIG_MTD_CONCAT=y
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_SM_FTL is not set
+CONFIG_MTD_OOPS=y
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+CONFIG_MTD_CFI_INTELEXT=y
+# CONFIG_MTD_CFI_AMDSTD is not set
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+# CONFIG_MTD_PHYSMAP is not set
+# CONFIG_MTD_ARM_INTEGRATOR is not set
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_DATAFLASH is not set
+# CONFIG_MTD_M25P80 is not set
+# CONFIG_MTD_SST25L is not set
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+CONFIG_MTD_NAND_IDS=y
+CONFIG_MTD_NAND_ECC=y
+# CONFIG_MTD_NAND_ECC_SMC is not set
+CONFIG_MTD_NAND=y
+# CONFIG_MTD_NAND_VERIFY_WRITE is not set
+# CONFIG_MTD_SM_COMMON is not set
+# CONFIG_MTD_NAND_MUSEUM_IDS is not set
+# CONFIG_MTD_NAND_GPIO is not set
+CONFIG_MTD_NAND_OMAP2=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
+CONFIG_MTD_ONENAND=y
+CONFIG_MTD_ONENAND_VERIFY_WRITE=y
+# CONFIG_MTD_ONENAND_GENERIC is not set
+CONFIG_MTD_ONENAND_OMAP2=y
+# CONFIG_MTD_ONENAND_OTP is not set
+# CONFIG_MTD_ONENAND_2X_PROGRAM is not set
+# CONFIG_MTD_ONENAND_SIM is not set
+
+#
+# LPDDR flash memory drivers
+#
+# CONFIG_MTD_LPDDR is not set
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+
+#
+# DRBD disabled because PROC_FS, INET or CONNECTOR not selected
+#
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_UB is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=16384
+# CONFIG_BLK_DEV_XIP is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_MG_DISK is not set
+# CONFIG_BLK_DEV_RBD is not set
+# CONFIG_MISC_DEVICES is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+CONFIG_SCSI_MOD=y
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=y
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+# CONFIG_BLK_DEV_SR is not set
+# CONFIG_CHR_DEV_SG is not set
+# CONFIG_CHR_DEV_SCH is not set
+CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+CONFIG_SCSI_LOWLEVEL=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_ISCSI_BOOT_SYSFS is not set
+# CONFIG_LIBFC is not set
+# CONFIG_LIBFCOE is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_DH is not set
+# CONFIG_SCSI_OSD_INITIATOR is not set
+# CONFIG_ATA is not set
+CONFIG_MD=y
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_DM=y
+# CONFIG_DM_DEBUG is not set
+CONFIG_DM_CRYPT=y
+# CONFIG_DM_SNAPSHOT is not set
+# CONFIG_DM_MIRROR is not set
+# CONFIG_DM_ZERO is not set
+# CONFIG_DM_MULTIPATH is not set
+# CONFIG_DM_DELAY is not set
+CONFIG_DM_UEVENT=y
+CONFIG_NETDEVICES=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_VETH is not set
+CONFIG_MII=y
+CONFIG_PHYLIB=y
+
+#
+# MII PHY device drivers
+#
+# CONFIG_MARVELL_PHY is not set
+# CONFIG_DAVICOM_PHY is not set
+# CONFIG_QSEMI_PHY is not set
+# CONFIG_LXT_PHY is not set
+# CONFIG_CICADA_PHY is not set
+# CONFIG_VITESSE_PHY is not set
+CONFIG_SMSC_PHY=y
+# CONFIG_BROADCOM_PHY is not set
+# CONFIG_BCM63XX_PHY is not set
+# CONFIG_ICPLUS_PHY is not set
+# CONFIG_REALTEK_PHY is not set
+# CONFIG_NATIONAL_PHY is not set
+# CONFIG_STE10XP is not set
+# CONFIG_LSI_ET1011C_PHY is not set
+# CONFIG_MICREL_PHY is not set
+# CONFIG_FIXED_PHY is not set
+# CONFIG_MDIO_BITBANG is not set
+CONFIG_NET_ETHERNET=y
+# CONFIG_AX88796 is not set
+# CONFIG_SMC91X is not set
+# CONFIG_DM9000 is not set
+# CONFIG_ENC28J60 is not set
+# CONFIG_ETHOC is not set
+CONFIG_SMC911X=y
+CONFIG_SMSC911X=y
+# CONFIG_SMSC911X_ARCH_HOOKS is not set
+# CONFIG_DNET is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
+# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
+# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
+# CONFIG_B44 is not set
+# CONFIG_KS8851 is not set
+# CONFIG_KS8851_MLL is not set
+CONFIG_NETDEV_1000=y
+CONFIG_TI_DAVINCI_EMAC=y
+CONFIG_TI_DAVINCI_MDIO=y
+CONFIG_TI_DAVINCI_CPDMA=y
+# CONFIG_STMMAC_ETH is not set
+CONFIG_NETDEV_10000=y
+CONFIG_WLAN=y
+# CONFIG_USB_ZD1201 is not set
+# CONFIG_BCM4329 is not set
+# CONFIG_HOSTAP is not set
+CONFIG_WL12XX_PLATFORM_DATA=y
+
+#
+# Enable WiMAX (Networking options) to see the WiMAX drivers
+#
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+CONFIG_USB_USBNET=y
+CONFIG_USB_NET_AX8817X=y
+CONFIG_USB_NET_CDCETHER=y
+# CONFIG_USB_NET_CDC_EEM is not set
+# CONFIG_USB_NET_DM9601 is not set
+# CONFIG_USB_NET_SMSC75XX is not set
+CONFIG_USB_NET_SMSC95XX=y
+# CONFIG_USB_NET_GL620A is not set
+CONFIG_USB_NET_NET1080=y
+# CONFIG_USB_NET_PLUSB is not set
+# CONFIG_USB_NET_MCS7830 is not set
+# CONFIG_USB_NET_RNDIS_HOST is not set
+CONFIG_USB_NET_CDC_SUBSET=y
+CONFIG_USB_ALI_M5632=y
+CONFIG_USB_AN2720=y
+CONFIG_USB_BELKIN=y
+CONFIG_USB_ARMLINUX=y
+CONFIG_USB_EPSON2888=y
+CONFIG_USB_KC2190=y
+CONFIG_USB_NET_ZAURUS=y
+# CONFIG_USB_NET_CX82310_ETH is not set
+# CONFIG_USB_NET_INT51X1 is not set
+# CONFIG_USB_IPHETH is not set
+# CONFIG_USB_SIERRA_NET is not set
+# CONFIG_WAN is not set
+
+#
+# CAIF transport drivers
+#
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+# CONFIG_INPUT_SPARSEKMAP is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_MOUSEDEV_PSAUX=y
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+CONFIG_INPUT_JOYDEV=y
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+# CONFIG_INPUT_KEYRESET is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ADP5588 is not set
+CONFIG_KEYBOARD_ATKBD=y
+# CONFIG_KEYBOARD_QT2160 is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+CONFIG_KEYBOARD_GPIO=y
+# CONFIG_KEYBOARD_GPIO_POLLED is not set
+# CONFIG_KEYBOARD_TCA6416 is not set
+# CONFIG_KEYBOARD_MATRIX is not set
+# CONFIG_KEYBOARD_MAX7359 is not set
+# CONFIG_KEYBOARD_MCS is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_OPENCORES is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+CONFIG_KEYBOARD_TWL4030=y
+# CONFIG_KEYBOARD_XTKBD is not set
+CONFIG_INPUT_MOUSE=y
+CONFIG_MOUSE_PS2=y
+CONFIG_MOUSE_PS2_ALPS=y
+CONFIG_MOUSE_PS2_LOGIPS2PP=y
+CONFIG_MOUSE_PS2_SYNAPTICS=y
+CONFIG_MOUSE_PS2_TRACKPOINT=y
+# CONFIG_MOUSE_PS2_ELANTECH is not set
+# CONFIG_MOUSE_PS2_SENTELIC is not set
+# CONFIG_MOUSE_PS2_TOUCHKIT is not set
+# CONFIG_MOUSE_SERIAL is not set
+# CONFIG_MOUSE_APPLETOUCH is not set
+# CONFIG_MOUSE_BCM5974 is not set
+# CONFIG_MOUSE_VSXXXAA is not set
+# CONFIG_MOUSE_GPIO is not set
+# CONFIG_MOUSE_SYNAPTICS_I2C is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+# CONFIG_TOUCHSCREEN_ADS7846 is not set
+# CONFIG_TOUCHSCREEN_AD7877 is not set
+# CONFIG_TOUCHSCREEN_AD7879 is not set
+# CONFIG_TOUCHSCREEN_BU21013 is not set
+# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set
+# CONFIG_TOUCHSCREEN_DYNAPRO is not set
+# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set
+# CONFIG_TOUCHSCREEN_EETI is not set
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
+# CONFIG_TOUCHSCREEN_MCS5000 is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_INEXIO is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_QT602240 is not set
+CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI4_DEV=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE=y
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
+# CONFIG_TOUCHSCREEN_TSC2007 is not set
+# CONFIG_TOUCHSCREEN_TSC2004 is not set
+# CONFIG_TOUCHSCREEN_W90X900 is not set
+# CONFIG_TOUCHSCREEN_TPS6507X is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_AD714X is not set
+# CONFIG_INPUT_ATI_REMOTE is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_KEYCHORD is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_YEALINK is not set
+# CONFIG_INPUT_CM109 is not set
+CONFIG_INPUT_TWL4030_PWRBUTTON=y
+# CONFIG_INPUT_TWL4030_VIBRA is not set
+# CONFIG_INPUT_UINPUT is not set
+# CONFIG_INPUT_GPIO is not set
+# CONFIG_INPUT_PCF8574 is not set
+# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set
+# CONFIG_INPUT_ADXL34X is not set
+
+#
+# Hardware I/O ports
+#
+CONFIG_SERIO=y
+CONFIG_SERIO_SERPORT=y
+CONFIG_SERIO_LIBPS2=y
+# CONFIG_SERIO_RAW is not set
+# CONFIG_SERIO_ALTERA_PS2 is not set
+# CONFIG_SERIO_PS2MULT is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_DEVMEM=y
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_N_GSM is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=32
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SERIAL_8250_DETECT_IRQ=y
+CONFIG_SERIAL_8250_RSA=y
+
+#
+# Non-8250 serial port support
+#
+# CONFIG_SERIAL_MAX3100 is not set
+# CONFIG_SERIAL_MAX3107 is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_SERIAL_OMAP=y
+CONFIG_SERIAL_OMAP_CONSOLE=y
+# CONFIG_SERIAL_TIMBERDALE is not set
+# CONFIG_SERIAL_ALTERA_JTAGUART is not set
+# CONFIG_SERIAL_ALTERA_UART is not set
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_TTY_PRINTK is not set
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_HW_RANDOM_TIMERIOMEM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_TI81XX_HDMI is not set
+# CONFIG_DCC_TTY is not set
+# CONFIG_RAMOOPS is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_COMPAT=y
+CONFIG_I2C_CHARDEV=y
+# CONFIG_I2C_MUX is not set
+CONFIG_I2C_HELPER_AUTO=y
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+# CONFIG_I2C_DESIGNWARE is not set
+# CONFIG_I2C_GPIO is not set
+# CONFIG_I2C_OCORES is not set
+CONFIG_I2C_OMAP=y
+# CONFIG_I2C_PCA_PLATFORM is not set
+# CONFIG_I2C_SIMTEC is not set
+# CONFIG_I2C_XILINX is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_TINY_USB is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+# CONFIG_SPI_BITBANG is not set
+# CONFIG_SPI_GPIO is not set
+CONFIG_SPI_OMAP24XX=y
+# CONFIG_SPI_XILINX is not set
+# CONFIG_SPI_DESIGNWARE is not set
+
+#
+# SPI Protocol Masters
+#
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_TLE62X0 is not set
+
+#
+# PPS support
+#
+# CONFIG_PPS is not set
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_SYSFS=y
+
+#
+# Memory mapped GPIO expanders:
+#
+# CONFIG_GPIO_BASIC_MMIO is not set
+# CONFIG_GPIO_IT8761E is not set
+# CONFIG_GPIO_VX855 is not set
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX7300 is not set
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+# CONFIG_GPIO_SX150X is not set
+CONFIG_GPIO_TWL4030=y
+# CONFIG_GPIO_ADP5588 is not set
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MCP23S08 is not set
+# CONFIG_GPIO_MC33880 is not set
+# CONFIG_GPIO_74X164 is not set
+
+#
+# AC97 GPIO expanders:
+#
+
+#
+# MODULbus GPIO expanders:
+#
+# CONFIG_W1 is not set
+CONFIG_POWER_SUPPLY=y
+# CONFIG_POWER_SUPPLY_DEBUG is not set
+# CONFIG_PDA_POWER is not set
+# CONFIG_TEST_POWER is not set
+# CONFIG_BATTERY_DS2782 is not set
+# CONFIG_BATTERY_BQ20Z75 is not set
+# CONFIG_BATTERY_BQ27x00 is not set
+# CONFIG_BATTERY_MAX17040 is not set
+# CONFIG_CHARGER_ISP1704 is not set
+# CONFIG_CHARGER_TWL4030 is not set
+CONFIG_HWMON=y
+# CONFIG_HWMON_VID is not set
+# CONFIG_HWMON_DEBUG_CHIP is not set
+
+#
+# Native drivers
+#
+# CONFIG_SENSORS_AD7414 is not set
+# CONFIG_SENSORS_AD7418 is not set
+# CONFIG_SENSORS_ADCXX is not set
+# CONFIG_SENSORS_ADM1021 is not set
+# CONFIG_SENSORS_ADM1025 is not set
+# CONFIG_SENSORS_ADM1026 is not set
+# CONFIG_SENSORS_ADM1029 is not set
+# CONFIG_SENSORS_ADM1031 is not set
+# CONFIG_SENSORS_ADM9240 is not set
+# CONFIG_SENSORS_ADT7411 is not set
+# CONFIG_SENSORS_ADT7462 is not set
+# CONFIG_SENSORS_ADT7470 is not set
+# CONFIG_SENSORS_ADT7475 is not set
+# CONFIG_SENSORS_ASC7621 is not set
+# CONFIG_SENSORS_ATXP1 is not set
+# CONFIG_SENSORS_DS1621 is not set
+# CONFIG_SENSORS_F71805F is not set
+# CONFIG_SENSORS_F71882FG is not set
+# CONFIG_SENSORS_F75375S is not set
+# CONFIG_SENSORS_G760A is not set
+# CONFIG_SENSORS_GL518SM is not set
+# CONFIG_SENSORS_GL520SM is not set
+# CONFIG_SENSORS_GPIO_FAN is not set
+# CONFIG_SENSORS_IT87 is not set
+# CONFIG_SENSORS_JC42 is not set
+# CONFIG_SENSORS_LM63 is not set
+# CONFIG_SENSORS_LM70 is not set
+# CONFIG_SENSORS_LM73 is not set
+# CONFIG_SENSORS_LM75 is not set
+# CONFIG_SENSORS_LM77 is not set
+# CONFIG_SENSORS_LM78 is not set
+# CONFIG_SENSORS_LM80 is not set
+# CONFIG_SENSORS_LM83 is not set
+# CONFIG_SENSORS_LM85 is not set
+# CONFIG_SENSORS_LM87 is not set
+# CONFIG_SENSORS_LM90 is not set
+# CONFIG_SENSORS_LM92 is not set
+# CONFIG_SENSORS_LM93 is not set
+# CONFIG_SENSORS_LTC4215 is not set
+# CONFIG_SENSORS_LTC4245 is not set
+# CONFIG_SENSORS_LTC4261 is not set
+# CONFIG_SENSORS_LM95241 is not set
+# CONFIG_SENSORS_MAX1111 is not set
+# CONFIG_SENSORS_MAX1619 is not set
+# CONFIG_SENSORS_MAX6650 is not set
+# CONFIG_SENSORS_PC87360 is not set
+# CONFIG_SENSORS_PC87427 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_SHT15 is not set
+# CONFIG_SENSORS_SMM665 is not set
+# CONFIG_SENSORS_DME1737 is not set
+# CONFIG_SENSORS_EMC1403 is not set
+# CONFIG_SENSORS_EMC2103 is not set
+# CONFIG_SENSORS_SMSC47M1 is not set
+# CONFIG_SENSORS_SMSC47M192 is not set
+# CONFIG_SENSORS_SMSC47B397 is not set
+# CONFIG_SENSORS_ADS7828 is not set
+# CONFIG_SENSORS_ADS7871 is not set
+# CONFIG_SENSORS_AMC6821 is not set
+# CONFIG_SENSORS_THMC50 is not set
+# CONFIG_SENSORS_TMP102 is not set
+# CONFIG_SENSORS_TMP401 is not set
+# CONFIG_SENSORS_TMP421 is not set
+# CONFIG_SENSORS_VT1211 is not set
+# CONFIG_SENSORS_W83781D is not set
+# CONFIG_SENSORS_W83791D is not set
+# CONFIG_SENSORS_W83792D is not set
+# CONFIG_SENSORS_W83793 is not set
+# CONFIG_SENSORS_W83795 is not set
+# CONFIG_SENSORS_W83L785TS is not set
+# CONFIG_SENSORS_W83L786NG is not set
+# CONFIG_SENSORS_W83627HF is not set
+# CONFIG_SENSORS_W83627EHF is not set
+# CONFIG_SENSORS_LIS3_SPI is not set
+# CONFIG_SENSORS_LIS3_I2C is not set
+# CONFIG_THERMAL is not set
+CONFIG_WATCHDOG=y
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+
+#
+# Watchdog Device Drivers
+#
+# CONFIG_SOFT_WATCHDOG is not set
+CONFIG_OMAP_WATCHDOG=y
+CONFIG_TWL4030_WATCHDOG=y
+# CONFIG_MAX63XX_WATCHDOG is not set
+
+#
+# USB-based Watchdog Cards
+#
+# CONFIG_USBPCWATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+CONFIG_MFD_SUPPORT=y
+CONFIG_MFD_CORE=y
+# CONFIG_MFD_88PM860X is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_HTC_I2CPLD is not set
+# CONFIG_TPS65010 is not set
+# CONFIG_TPS6507X is not set
+CONFIG_TWL4030_CORE=y
+CONFIG_TWL4030_POWER=y
+CONFIG_TWL4030_SCRIPT=y
+CONFIG_TWL4030_CODEC=y
+# CONFIG_TWL6030_PWM is not set
+# CONFIG_MFD_STMPE is not set
+# CONFIG_MFD_TC35892 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
+# CONFIG_MFD_TC6393XB is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_PMIC_ADP5520 is not set
+# CONFIG_MFD_MAX8925 is not set
+# CONFIG_MFD_MAX8998 is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM831X_I2C is not set
+# CONFIG_MFD_WM831X_SPI is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_MFD_WM8994 is not set
+# CONFIG_MFD_PCF50633 is not set
+# CONFIG_MFD_MC13XXX is not set
+# CONFIG_ABX500_CORE is not set
+# CONFIG_EZX_PCAP is not set
+# CONFIG_MFD_TPS6586X is not set
+CONFIG_REGULATOR=y
+# CONFIG_REGULATOR_DEBUG is not set
+CONFIG_REGULATOR_DUMMY=y
+# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set
+# CONFIG_REGULATOR_BQ24022 is not set
+# CONFIG_REGULATOR_MAX1586 is not set
+# CONFIG_REGULATOR_MAX8649 is not set
+# CONFIG_REGULATOR_MAX8660 is not set
+# CONFIG_REGULATOR_MAX8952 is not set
+CONFIG_REGULATOR_TWL4030=y
+# CONFIG_REGULATOR_LP3971 is not set
+# CONFIG_REGULATOR_LP3972 is not set
+# CONFIG_REGULATOR_TPS65023 is not set
+# CONFIG_REGULATOR_TPS6507X is not set
+# CONFIG_REGULATOR_ISL6271A is not set
+# CONFIG_REGULATOR_AD5398 is not set
+CONFIG_MEDIA_SUPPORT=y
+
+#
+# Multimedia core support
+#
+CONFIG_MEDIA_CONTROLLER=y
+CONFIG_VIDEO_DEV=y
+CONFIG_VIDEO_V4L2_COMMON=y
+CONFIG_VIDEO_ALLOW_V4L1=y
+CONFIG_VIDEO_V4L1_COMPAT=y
+CONFIG_VIDEO_V4L2_SUBDEV_API=y
+# CONFIG_DVB_CORE is not set
+CONFIG_VIDEO_MEDIA=y
+
+#
+# Multimedia drivers
+#
+# CONFIG_IR_CORE is not set
+# CONFIG_MEDIA_ATTACH is not set
+CONFIG_MEDIA_TUNER=y
+# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
+CONFIG_MEDIA_TUNER_SIMPLE=y
+CONFIG_MEDIA_TUNER_TDA8290=y
+CONFIG_MEDIA_TUNER_TDA827X=y
+CONFIG_MEDIA_TUNER_TDA18271=y
+CONFIG_MEDIA_TUNER_TDA9887=y
+CONFIG_MEDIA_TUNER_TEA5761=y
+CONFIG_MEDIA_TUNER_TEA5767=y
+CONFIG_MEDIA_TUNER_MT20XX=y
+CONFIG_MEDIA_TUNER_XC2028=y
+CONFIG_MEDIA_TUNER_XC5000=y
+CONFIG_MEDIA_TUNER_MC44S803=y
+CONFIG_VIDEO_V4L2=y
+CONFIG_VIDEO_V4L1=y
+CONFIG_VIDEOBUF_GEN=y
+CONFIG_VIDEOBUF_DMA_CONTIG=y
+CONFIG_VIDEO_CAPTURE_DRIVERS=y
+# CONFIG_VIDEO_ADV_DEBUG is not set
+# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set
+# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set
+
+#
+# Encoders/decoders and other helper chips
+#
+
+#
+# Audio decoders
+#
+# CONFIG_VIDEO_TVAUDIO is not set
+# CONFIG_VIDEO_TDA7432 is not set
+# CONFIG_VIDEO_TDA9840 is not set
+# CONFIG_VIDEO_TDA9875 is not set
+# CONFIG_VIDEO_TEA6415C is not set
+# CONFIG_VIDEO_TEA6420 is not set
+# CONFIG_VIDEO_MSP3400 is not set
+# CONFIG_VIDEO_CS5345 is not set
+# CONFIG_VIDEO_CS53L32A is not set
+# CONFIG_VIDEO_M52790 is not set
+# CONFIG_VIDEO_TLV320AIC23B is not set
+# CONFIG_VIDEO_WM8775 is not set
+# CONFIG_VIDEO_WM8739 is not set
+# CONFIG_VIDEO_VP27SMPX is not set
+
+#
+# RDS decoders
+#
+# CONFIG_VIDEO_SAA6588 is not set
+
+#
+# Video decoders
+#
+# CONFIG_VIDEO_ADV7180 is not set
+# CONFIG_VIDEO_BT819 is not set
+# CONFIG_VIDEO_BT856 is not set
+# CONFIG_VIDEO_BT866 is not set
+# CONFIG_VIDEO_KS0127 is not set
+# CONFIG_VIDEO_OV7670 is not set
+# CONFIG_VIDEO_MT9T001 is not set
+CONFIG_VIDEO_MT9V011=y
+# CONFIG_VIDEO_MT9V032 is not set
+CONFIG_VIDEO_MT9V113=y
+# CONFIG_VIDEO_MT9T111 is not set
+# CONFIG_VIDEO_TCM825X is not set
+# CONFIG_VIDEO_SAA7110 is not set
+# CONFIG_VIDEO_SAA711X is not set
+# CONFIG_VIDEO_SAA717X is not set
+# CONFIG_VIDEO_SAA7191 is not set
+# CONFIG_VIDEO_TVP514X is not set
+# CONFIG_VIDEO_TVP5150 is not set
+# CONFIG_VIDEO_TVP7002 is not set
+# CONFIG_VIDEO_VPX3220 is not set
+
+#
+# Video and audio decoders
+#
+# CONFIG_VIDEO_CX25840 is not set
+
+#
+# MPEG video encoders
+#
+# CONFIG_VIDEO_CX2341X is not set
+
+#
+# Video encoders
+#
+# CONFIG_VIDEO_SAA7127 is not set
+# CONFIG_VIDEO_SAA7185 is not set
+# CONFIG_VIDEO_ADV7170 is not set
+# CONFIG_VIDEO_ADV7175 is not set
+# CONFIG_VIDEO_THS7303 is not set
+# CONFIG_VIDEO_ADV7343 is not set
+# CONFIG_VIDEO_AK881X is not set
+
+#
+# Video improvement chips
+#
+# CONFIG_VIDEO_UPD64031A is not set
+# CONFIG_VIDEO_UPD64083 is not set
+# CONFIG_VIDEO_VPSS_SYSTEM is not set
+# CONFIG_VIDEO_VPFE_CAPTURE is not set
+CONFIG_VIDEO_OMAP2_VOUT=y
+# CONFIG_VIDEO_CPIA2 is not set
+# CONFIG_VIDEO_SR030PC30 is not set
+CONFIG_VIDEO_OMAP3=y
+CONFIG_VIDEO_OMAP3_DEBUG=y
+# CONFIG_SOC_CAMERA is not set
+CONFIG_V4L_USB_DRIVERS=y
+CONFIG_USB_VIDEO_CLASS=y
+CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y
+# CONFIG_USB_GSPCA is not set
+# CONFIG_VIDEO_PVRUSB2 is not set
+# CONFIG_VIDEO_HDPVR is not set
+# CONFIG_VIDEO_USBVISION is not set
+# CONFIG_USB_VICAM is not set
+# CONFIG_USB_IBMCAM is not set
+# CONFIG_USB_KONICAWC is not set
+# CONFIG_USB_ET61X251 is not set
+# CONFIG_USB_SE401 is not set
+# CONFIG_USB_SN9C102 is not set
+# CONFIG_USB_PWC is not set
+# CONFIG_USB_ZR364XX is not set
+# CONFIG_USB_STKWEBCAM is not set
+# CONFIG_USB_S2255 is not set
+# CONFIG_V4L_MEM2MEM_DRIVERS is not set
+# CONFIG_RADIO_ADAPTERS is not set
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_DRM is not set
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+CONFIG_FB=y
+CONFIG_FIRMWARE_EDID=y
+# CONFIG_FB_DDC is not set
+# CONFIG_FB_BOOT_VESA_SUPPORT is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+CONFIG_FB_MODE_HELPERS=y
+CONFIG_FB_TILEBLITTING=y
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_TMIO is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
+# CONFIG_FB_BROADSHEET is not set
+# CONFIG_FB_OMAP_BOOTLOADER_INIT is not set
+CONFIG_OMAP2_VRAM=y
+CONFIG_OMAP2_VRFB=y
+CONFIG_OMAP2_DSS=y
+CONFIG_OMAP2_VRAM_SIZE=4
+CONFIG_OMAP2_DSS_DEBUG_SUPPORT=y
+# CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS is not set
+CONFIG_OMAP2_DSS_DPI=y
+# CONFIG_OMAP2_DSS_RFBI is not set
+CONFIG_OMAP2_DSS_VENC=y
+CONFIG_OMAP2_VENC_OUT_TYPE_SVIDEO=y
+# CONFIG_OMAP2_VENC_OUT_TYPE_COMPOSITE is not set
+# CONFIG_OMAP2_DSS_SDI is not set
+CONFIG_OMAP2_DSS_DSI=y
+CONFIG_OMAP2_DSS_USE_DSI_PLL=y
+# CONFIG_OMAP2_DSS_FAKE_VSYNC is not set
+CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK=1
+CONFIG_FB_OMAP2=y
+CONFIG_FB_OMAP2_DEBUG_SUPPORT=y
+CONFIG_FB_OMAP2_NUM_FBS=1
+
+#
+# OMAP2/3 Display Device Drivers
+#
+CONFIG_PANEL_GENERIC=y
+# CONFIG_PANEL_LGPHILIPS_LB035Q02 is not set
+# CONFIG_PANEL_SAMSUNG_LTE430WQ_F0C is not set
+CONFIG_PANEL_SHARP_LS037V7DW01=y
+# CONFIG_PANEL_SHARP_LQ043T1DG01 is not set
+# CONFIG_PANEL_SAMSUNG_LMS700KF23 is not set
+# CONFIG_PANEL_TAAL is not set
+# CONFIG_PANEL_TOPPOLY_TDO35S is not set
+# CONFIG_PANEL_TPO_TD043MTEA1 is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+# CONFIG_LCD_L4F00242T03 is not set
+# CONFIG_LCD_LMS283GF05 is not set
+# CONFIG_LCD_LTV350QV is not set
+# CONFIG_LCD_TDO24M is not set
+# CONFIG_LCD_VGG2432A4 is not set
+CONFIG_LCD_PLATFORM=y
+# CONFIG_LCD_S6E63M0 is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_GENERIC=m
+# CONFIG_BACKLIGHT_ADP8860 is not set
+
+#
+# Display device support
+#
+CONFIG_DISPLAY_SUPPORT=y
+
+#
+# Display hardware drivers
+#
+
+#
+# Console display driver support
+#
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE is not set
+CONFIG_LOGO=y
+CONFIG_LOGO_LINUX_MONO=y
+CONFIG_LOGO_LINUX_VGA16=y
+CONFIG_LOGO_LINUX_CLUT224=y
+CONFIG_SOUND=y
+# CONFIG_SOUND_OSS_CORE is not set
+CONFIG_SND=y
+CONFIG_SND_TIMER=y
+CONFIG_SND_PCM=y
+CONFIG_SND_HWDEP=y
+CONFIG_SND_RAWMIDI=y
+CONFIG_SND_JACK=y
+# CONFIG_SND_SEQUENCER is not set
+# CONFIG_SND_MIXER_OSS is not set
+# CONFIG_SND_PCM_OSS is not set
+# CONFIG_SND_HRTIMER is not set
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+# CONFIG_SND_RAWMIDI_SEQ is not set
+# CONFIG_SND_OPL3_LIB_SEQ is not set
+# CONFIG_SND_OPL4_LIB_SEQ is not set
+# CONFIG_SND_SBAWE_SEQ is not set
+# CONFIG_SND_EMU10K1_SEQ is not set
+CONFIG_SND_DRIVERS=y
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_ALOOP is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+CONFIG_SND_ARM=y
+CONFIG_SND_SPI=y
+CONFIG_SND_USB=y
+CONFIG_SND_USB_AUDIO=y
+# CONFIG_SND_USB_UA101 is not set
+# CONFIG_SND_USB_CAIAQ is not set
+CONFIG_SND_SOC=y
+CONFIG_SND_OMAP_SOC=y
+CONFIG_SND_OMAP_SOC_MCBSP=y
+CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE=y
+CONFIG_SND_SOC_I2C_AND_SPI=y
+# CONFIG_SND_SOC_ALL_CODECS is not set
+CONFIG_SND_SOC_TWL4030=y
+# CONFIG_SND_SOC_WL1271BT is not set
+# CONFIG_SOUND_PRIME is not set
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=y
+# CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=y
+# CONFIG_HID_PID is not set
+# CONFIG_USB_HIDDEV is not set
+
+#
+# Special HID drivers
+#
+# CONFIG_HID_3M_PCT is not set
+# CONFIG_HID_A4TECH is not set
+# CONFIG_HID_ACRUX_FF is not set
+# CONFIG_HID_APPLE is not set
+# CONFIG_HID_BELKIN is not set
+# CONFIG_HID_CANDO is not set
+# CONFIG_HID_CHERRY is not set
+# CONFIG_HID_CHICONY is not set
+# CONFIG_HID_PRODIKEYS is not set
+# CONFIG_HID_CYPRESS is not set
+# CONFIG_HID_DRAGONRISE is not set
+# CONFIG_HID_EGALAX is not set
+# CONFIG_HID_EZKEY is not set
+# CONFIG_HID_KYE is not set
+# CONFIG_HID_UCLOGIC is not set
+# CONFIG_HID_WALTOP is not set
+# CONFIG_HID_GYRATION is not set
+# CONFIG_HID_TWINHAN is not set
+# CONFIG_HID_KENSINGTON is not set
+# CONFIG_HID_LOGITECH is not set
+# CONFIG_HID_MICROSOFT is not set
+# CONFIG_HID_MOSART is not set
+# CONFIG_HID_MONTEREY is not set
+# CONFIG_HID_NTRIG is not set
+# CONFIG_HID_ORTEK is not set
+# CONFIG_HID_PANTHERLORD is not set
+# CONFIG_HID_PETALYNX is not set
+# CONFIG_HID_PICOLCD is not set
+# CONFIG_HID_QUANTA is not set
+# CONFIG_HID_ROCCAT is not set
+# CONFIG_HID_ROCCAT_KONE is not set
+# CONFIG_HID_ROCCAT_PYRA is not set
+# CONFIG_HID_SAMSUNG is not set
+# CONFIG_HID_SONY is not set
+# CONFIG_HID_STANTUM is not set
+# CONFIG_HID_SUNPLUS is not set
+# CONFIG_HID_GREENASIA is not set
+# CONFIG_HID_SMARTJOYPLUS is not set
+# CONFIG_HID_TOPSEED is not set
+# CONFIG_HID_THRUSTMASTER is not set
+# CONFIG_HID_ZEROPLUS is not set
+# CONFIG_HID_ZYDACRON is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+CONFIG_USB_ARCH_HAS_EHCI=y
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+
+#
+# Miscellaneous USB options
+#
+# CONFIG_USB_DEVICEFS is not set
+# CONFIG_USB_DEVICE_CLASS is not set
+# CONFIG_USB_DYNAMIC_MINORS is not set
+CONFIG_USB_SUSPEND=y
+CONFIG_USB_OTG=y
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+CONFIG_USB_MON=y
+# CONFIG_USB_WUSB is not set
+# CONFIG_USB_WUSB_CBAF is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+CONFIG_USB_EHCI_TT_NEWSCHED=y
+# CONFIG_USB_OXU210HP_HCD is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1760_HCD is not set
+# CONFIG_USB_ISP1362_HCD is not set
+# CONFIG_USB_OHCI_HCD is not set
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+# CONFIG_USB_HWA_HCD is not set
+CONFIG_USB_MUSB_HDRC=y
+
+#
+# Platform Glue Layer
+#
+# CONFIG_USB_MUSB_TUSB6010_GLUE is not set
+CONFIG_USB_MUSB_OMAP2PLUS_GLUE=y
+# CONFIG_USB_MUSB_AM35X_GLUE is not set
+# CONFIG_USB_MUSB_DAVINCI is not set
+# CONFIG_USB_MUSB_DA8XX is not set
+# CONFIG_USB_MUSB_TUSB6010 is not set
+CONFIG_USB_MUSB_OMAP2PLUS=y
+# CONFIG_USB_MUSB_AM35X is not set
+# CONFIG_USB_MUSB_TI81XX is not set
+# CONFIG_USB_MUSB_BLACKFIN is not set
+# CONFIG_USB_MUSB_UX500 is not set
+# CONFIG_USB_MUSB_HOST is not set
+# CONFIG_USB_MUSB_PERIPHERAL is not set
+CONFIG_USB_MUSB_OTG=y
+CONFIG_USB_GADGET_MUSB_HDRC=y
+CONFIG_USB_MUSB_HDRC_HCD=y
+# CONFIG_MUSB_PIO_ONLY is not set
+CONFIG_USB_INVENTRA_DMA_HW=y
+# CONFIG_USB_TI_CPPI_DMA_HW is not set
+# CONFIG_USB_TI_CPPI41_DMA_HW is not set
+CONFIG_USB_INVENTRA_DMA=y
+CONFIG_MUSB_USE_SYSTEM_DMA_WORKAROUND=y
+# CONFIG_USB_TI_CPPI_DMA is not set
+# CONFIG_USB_TI_CPPI41_DMA is not set
+# CONFIG_USB_TUSB_OMAP_DMA is not set
+# CONFIG_USB_MUSB_DEBUG is not set
+
+#
+# USB Device Class drivers
+#
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_PRINTER is not set
+# CONFIG_USB_WDM is not set
+# CONFIG_USB_TMC is not set
+
+#
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may
+#
+
+#
+# also be needed; see USB_STORAGE Help for more info
+#
+CONFIG_USB_STORAGE=y
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_ONETOUCH is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set
+# CONFIG_USB_UAS is not set
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_MICROTEK is not set
+
+#
+# USB port drivers
+#
+# CONFIG_USB_SERIAL is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_ADUTUX is not set
+# CONFIG_USB_SEVSEG is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+# CONFIG_USB_CYTHERM is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_SISUSBVGA is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_YUREX is not set
+CONFIG_USB_GADGET=y
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+# CONFIG_USB_GADGET_DEBUG_FS is not set
+CONFIG_USB_GADGET_VBUS_DRAW=2
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_OMAP is not set
+# CONFIG_USB_GADGET_R8A66597 is not set
+# CONFIG_USB_GADGET_M66592 is not set
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+CONFIG_USB_GADGET_DUALSPEED=y
+# CONFIG_USB_ZERO is not set
+# CONFIG_USB_AUDIO is not set
+# CONFIG_USB_ETH is not set
+# CONFIG_USB_GADGETFS is not set
+# CONFIG_USB_FUNCTIONFS is not set
+# CONFIG_USB_FILE_STORAGE is not set
+# CONFIG_USB_MASS_STORAGE is not set
+# CONFIG_USB_G_SERIAL is not set
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_G_PRINTER is not set
+CONFIG_USB_ANDROID=y
+# CONFIG_USB_ANDROID_ACM is not set
+CONFIG_USB_ANDROID_ADB=y
+CONFIG_USB_ANDROID_MASS_STORAGE=y
+# CONFIG_USB_ANDROID_MTP is not set
+# CONFIG_USB_ANDROID_RNDIS is not set
+# CONFIG_USB_CDC_COMPOSITE is not set
+# CONFIG_USB_G_MULTI is not set
+# CONFIG_USB_G_HID is not set
+# CONFIG_USB_G_DBGP is not set
+# CONFIG_USB_G_WEBCAM is not set
+
+#
+# OTG and related infrastructure
+#
+CONFIG_USB_OTG_UTILS=y
+# CONFIG_USB_GPIO_VBUS is not set
+# CONFIG_ISP1301_OMAP is not set
+# CONFIG_USB_ULPI is not set
+CONFIG_TWL4030_USB=y
+# CONFIG_NOP_USB_XCEIV is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_UNSAFE_RESUME=y
+# CONFIG_MMC_EMBEDDED_SDIO is not set
+# CONFIG_MMC_PARANOID_SD_INIT is not set
+
+#
+# MMC/SD/SDIO Card Drivers
+#
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_MINORS=8
+CONFIG_MMC_BLOCK_BOUNCE=y
+# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set
+CONFIG_SDIO_UART=y
+# CONFIG_MMC_TEST is not set
+
+#
+# MMC/SD/SDIO Host Controller Drivers
+#
+# CONFIG_MMC_SDHCI is not set
+CONFIG_MMC_OMAP=y
+CONFIG_MMC_OMAP_HS=y
+# CONFIG_MMC_SPI is not set
+# CONFIG_MMC_USHC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_NEW_LEDS is not set
+CONFIG_SWITCH=y
+# CONFIG_SWITCH_GPIO is not set
+# CONFIG_ACCESSIBILITY is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+CONFIG_RTC_INTF_ALARM=y
+CONFIG_RTC_INTF_ALARM_DEV=y
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# I2C RTC drivers
+#
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_DS3232 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_ISL12022 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+# CONFIG_RTC_DRV_BQ32K is not set
+CONFIG_RTC_DRV_TWL4030=y
+# CONFIG_RTC_DRV_S35390A is not set
+# CONFIG_RTC_DRV_FM3130 is not set
+# CONFIG_RTC_DRV_RX8581 is not set
+# CONFIG_RTC_DRV_RX8025 is not set
+
+#
+# SPI RTC drivers
+#
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_DS3234 is not set
+# CONFIG_RTC_DRV_PCF2123 is not set
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1286 is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_MSM6242 is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
+# CONFIG_RTC_DRV_RP5C01 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+# CONFIG_DMADEVICES is not set
+# CONFIG_AUXDISPLAY is not set
+# CONFIG_UIO is not set
+CONFIG_STAGING=y
+# CONFIG_STAGING_EXCLUDE_BUILD is not set
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_USB_IP_COMMON is not set
+# CONFIG_ECHO is not set
+# CONFIG_BRCM80211 is not set
+# CONFIG_RT2870 is not set
+# CONFIG_COMEDI is not set
+# CONFIG_ASUS_OLED is not set
+# CONFIG_R8712U is not set
+# CONFIG_TRANZPORT is not set
+
+#
+# Android
+#
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_RAM_CONSOLE=y
+CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE=y
+CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y
+CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_DATA_SIZE=128
+CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_ECC_SIZE=16
+CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE=8
+CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL=0x11d
+# CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT is not set
+CONFIG_ANDROID_TIMED_OUTPUT=y
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+# CONFIG_POHMELFS is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_LINE6_USB is not set
+# CONFIG_VT6656 is not set
+# CONFIG_FB_UDL is not set
+# CONFIG_IIO is not set
+# CONFIG_ZRAM is not set
+# CONFIG_BATMAN_ADV is not set
+# CONFIG_FB_SM7XX is not set
+
+#
+# Texas Instruments shared transport line discipline
+#
+# CONFIG_ADIS16255 is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_EASYCAP is not set
+# CONFIG_TIDSPBRIDGE is not set
+# CONFIG_WESTBRIDGE is not set
+CONFIG_WESTBRIDGE_HAL_SELECTED=y
+CONFIG_MACH_OMAP3_WESTBRIDGE_AST_PNAND_HAL=y
+# CONFIG_MACH_NO_WESTBRIDGE is not set
+# CONFIG_ATH6K_LEGACY is not set
+# CONFIG_USB_ENESTORAGE is not set
+# CONFIG_BCM_WIMAX is not set
+# CONFIG_FT1000 is not set
+
+#
+# Speakup console speech
+#
+# CONFIG_SPEAKUP is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_DEFAULTS_TO_ORDERED=y
+# CONFIG_EXT3_FS_XATTR is not set
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_XATTR=y
+# CONFIG_EXT4_FS_POSIX_ACL is not set
+# CONFIG_EXT4_FS_SECURITY is not set
+# CONFIG_EXT4_DEBUG is not set
+CONFIG_JBD=y
+# CONFIG_JBD_DEBUG is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_BTRFS_FS is not set
+# CONFIG_NILFS2_FS is not set
+CONFIG_FILE_LOCKING=y
+CONFIG_FSNOTIFY=y
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_FANOTIFY is not set
+CONFIG_QUOTA=y
+# CONFIG_QUOTA_NETLINK_INTERFACE is not set
+CONFIG_PRINT_QUOTA_WARNING=y
+# CONFIG_QUOTA_DEBUG is not set
+CONFIG_QUOTA_TREE=y
+# CONFIG_QFMT_V1 is not set
+CONFIG_QFMT_V2=y
+CONFIG_QUOTACTL=y
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# Caches
+#
+# CONFIG_FSCACHE is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_ECRYPT_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_YAFFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+CONFIG_JFFS2_SUMMARY=y
+CONFIG_JFFS2_FS_XATTR=y
+CONFIG_JFFS2_FS_POSIX_ACL=y
+CONFIG_JFFS2_FS_SECURITY=y
+CONFIG_JFFS2_COMPRESSION_OPTIONS=y
+CONFIG_JFFS2_ZLIB=y
+CONFIG_JFFS2_LZO=y
+CONFIG_JFFS2_RTIME=y
+CONFIG_JFFS2_RUBIN=y
+# CONFIG_JFFS2_CMODE_NONE is not set
+CONFIG_JFFS2_CMODE_PRIORITY=y
+# CONFIG_JFFS2_CMODE_SIZE is not set
+# CONFIG_JFFS2_CMODE_FAVOURLZO is not set
+# CONFIG_LOGFS is not set
+CONFIG_CRAMFS=y
+# CONFIG_SQUASHFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+# CONFIG_NFS_V4_1 is not set
+CONFIG_ROOT_NFS=y
+CONFIG_NFS_USE_LEGACY_DNS=y
+# CONFIG_NFS_USE_NEW_IDMAPPER is not set
+# CONFIG_NFSD is not set
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_ACL_SUPPORT=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+CONFIG_SUNRPC_GSS=y
+CONFIG_RPCSEC_GSS_KRB5=y
+# CONFIG_CEPH_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+# CONFIG_EFI_PARTITION is not set
+# CONFIG_SYSV68_PARTITION is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+CONFIG_NLS_ISO8859_1=y
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_UTF8 is not set
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_STRIP_ASM_SYMS is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+CONFIG_DEBUG_FS=y
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_HARDLOCKUP_DETECTOR is not set
+CONFIG_BKL=y
+# CONFIG_SPARSE_RCU_POINTER is not set
+CONFIG_STACKTRACE=y
+CONFIG_DEBUG_BUGVERBOSE=y
+# CONFIG_DEBUG_MEMORY_INIT is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_LKDTM is not set
+# CONFIG_SYSCTL_SYSCALL_CHECK is not set
+CONFIG_NOP_TRACER=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+CONFIG_RING_BUFFER=y
+CONFIG_EVENT_TRACING=y
+CONFIG_CONTEXT_SWITCH_TRACER=y
+CONFIG_RING_BUFFER_ALLOW_SWAP=y
+CONFIG_TRACING=y
+CONFIG_TRACING_SUPPORT=y
+CONFIG_FTRACE=y
+# CONFIG_FUNCTION_TRACER is not set
+# CONFIG_IRQSOFF_TRACER is not set
+# CONFIG_SCHED_TRACER is not set
+# CONFIG_ENABLE_DEFAULT_TRACERS is not set
+CONFIG_BRANCH_PROFILE_NONE=y
+# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set
+# CONFIG_PROFILE_ALL_BRANCHES is not set
+# CONFIG_STACK_TRACER is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+CONFIG_KPROBE_EVENT=y
+# CONFIG_RING_BUFFER_BENCHMARK is not set
+# CONFIG_DYNAMIC_DEBUG is not set
+# CONFIG_ATOMIC64_SELFTEST is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_STRICT_DEVMEM is not set
+# CONFIG_ARM_UNWIND is not set
+# CONFIG_DEBUG_USER is not set
+# CONFIG_OC_ETM is not set
+
+#
+# Security options
+#
+CONFIG_KEYS=y
+# CONFIG_KEYS_DEBUG_PROC_KEYS is not set
+# CONFIG_SECURITY_DMESG_RESTRICT is not set
+CONFIG_SECURITY=y
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_NETWORK is not set
+# CONFIG_SECURITY_PATH is not set
+# CONFIG_SECURITY_TOMOYO is not set
+# CONFIG_SECURITY_APPARMOR is not set
+# CONFIG_IMA is not set
+CONFIG_DEFAULT_SECURITY_DAC=y
+CONFIG_DEFAULT_SECURITY=""
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_ALGAPI2=y
+CONFIG_CRYPTO_AEAD2=y
+CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_BLKCIPHER2=y
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_HASH2=y
+CONFIG_CRYPTO_RNG2=y
+CONFIG_CRYPTO_PCOMP2=y
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_MANAGER2=y
+CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+CONFIG_CRYPTO_WORKQUEUE=y
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+CONFIG_CRYPTO_CBC=y
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_XCBC is not set
+# CONFIG_CRYPTO_VMAC is not set
+
+#
+# Digest
+#
+CONFIG_CRYPTO_CRC32C=y
+# CONFIG_CRYPTO_GHASH is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRYPTO_TWOFISH_COMMON=y
+
+#
+# Compression
+#
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_ZLIB is not set
+# CONFIG_CRYPTO_LZO is not set
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_HW=y
+# CONFIG_CRYPTO_DEV_OMAP_SHAM is not set
+# CONFIG_CRYPTO_DEV_OMAP_AES is not set
+CONFIG_BINARY_PRINTF=y
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_GENERIC_FIND_LAST_BIT=y
+CONFIG_CRC_CCITT=y
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+CONFIG_LIBCRC32C=y
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+CONFIG_DECOMPRESS_GZIP=y
+CONFIG_REED_SOLOMON=y
+CONFIG_REED_SOLOMON_ENC8=y
+CONFIG_REED_SOLOMON_DEC8=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
+CONFIG_NLATTR=y
diff --git a/kernel/arch/arm/configs/panda_defconfig b/kernel/arch/arm/configs/panda_defconfig
new file mode 100644
index 000000000000..4c5e56c56cf6
--- /dev/null
+++ b/kernel/arch/arm/configs/panda_defconfig
@@ -0,0 +1,331 @@
+CONFIG_EXPERIMENTAL=y
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_ASHMEM=y
+# CONFIG_AIO is not set
+CONFIG_EMBEDDED=y
+# CONFIG_SLUB_DEBUG is not set
+CONFIG_MODULES=y
+CONFIG_MODULE_FORCE_LOAD=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_BLK_DEV_BSG is not set
+CONFIG_ARCH_OMAP=y
+CONFIG_OMAP_RESET_CLOCKS=y
+# CONFIG_ARCH_OMAP2 is not set
+# CONFIG_ARCH_OMAP3 is not set
+# CONFIG_MACH_OMAP_4430SDP is not set
+CONFIG_ARM_THUMBEE=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_SMP=y
+# CONFIG_SMP_ON_UP is not set
+CONFIG_NR_CPUS=2
+CONFIG_PREEMPT=y
+CONFIG_CMDLINE="console=ttyO2,115200n8 mem=1G androidboot.console=ttyO2"
+CONFIG_CMDLINE_EXTEND=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_IDLE=y
+CONFIG_OMAP_SMARTREFLEX=y
+CONFIG_OMAP_SMARTREFLEX_CLASS1P5=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_BINFMT_MISC=y
+CONFIG_WAKELOCK=y
+CONFIG_PM_DEBUG=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_INET_ESP=y
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+CONFIG_IPV6=y
+CONFIG_IPV6_PRIVACY=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_TUNNEL=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_NETFILTER=y
+CONFIG_NETFILTER_NETLINK_LOG=y
+CONFIG_NETFILTER_TPROXY=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
+CONFIG_NF_CONNTRACK_AMANDA=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_H323=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_NETBIOS_NS=y
+CONFIG_NF_CONNTRACK_PPTP=y
+CONFIG_NF_CONNTRACK_SANE=y
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_MATCH_COMMENT=y
+CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_HELPER=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
+CONFIG_NETFILTER_XT_MATCH_STRING=y
+CONFIG_NETFILTER_XT_MATCH_TIME=y
+CONFIG_NETFILTER_XT_MATCH_U32=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_TARGET_REJECT_SKERR=y
+CONFIG_IP_NF_TARGET_LOG=y
+CONFIG_NF_NAT=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
+CONFIG_IP_NF_RAW=y
+CONFIG_IP_NF_ARPTABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARP_MANGLE=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_TARGET_LOG=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_TARGET_REJECT_SKERR=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
+CONFIG_PHONET=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_SCH_INGRESS=y
+CONFIG_NET_CLS_U32=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_U32=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_POLICE=y
+CONFIG_NET_ACT_GACT=y
+CONFIG_NET_ACT_MIRRED=y
+CONFIG_BT=y
+CONFIG_BT_BNEP=y
+CONFIG_BT_L2CAP=y
+CONFIG_BT_SCO=y
+CONFIG_BT_RFCOMM=y
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_HCIUART=y
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_WILINK=y
+CONFIG_RFKILL=y
+CONFIG_RFKILL_INPUT=y
+CONFIG_MTD=y
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_NAND_IDS=y
+CONFIG_MTD_ONENAND=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_MISC_DEVICES=y
+# CONFIG_ANDROID_PMEM is not set
+CONFIG_KERNEL_DEBUGGER_CORE=y
+CONFIG_UID_STAT=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_DEBUG=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_UEVENT=y
+CONFIG_NETDEVICES=y
+CONFIG_IFB=y
+CONFIG_USB_USBNET=y
+CONFIG_USB_NET_SMSC95XX=y
+CONFIG_PPP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_KEYRESET=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI4_DEV=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_KEYCHORD=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_HW_RANDOM=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_GPIO=y
+CONFIG_SPI=y
+CONFIG_SPI_GPIO=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_TWL4030=y
+CONFIG_POWER_SUPPLY=y
+# CONFIG_HWMON is not set
+CONFIG_TWL6030_PWM=y
+CONFIG_REGULATOR_TWL4030=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_PVR_SGX=y
+CONFIG_PVR_NEED_PVR_DPF=y
+CONFIG_PVR_NEED_PVR_ASSERT=y
+CONFIG_PVR_USSE_EDM_STATUS_DEBUG=y
+CONFIG_FB=y
+CONFIG_OMAP2_DSS=y
+# CONFIG_OMAP2_DSS_VENC is not set
+CONFIG_FB_OMAP2=y
+CONFIG_FB_OMAP2_NUM_FBS=2
+CONFIG_OMAP2_VRAM_SIZE=16
+CONFIG_PANEL_GENERIC_DPI=y
+CONFIG_DISPLAY_SUPPORT=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_SUSPEND=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_MUSB_HDRC=y
+CONFIG_USB_MUSB_OMAP2PLUS=y
+CONFIG_USB_MUSB_PERIPHERAL=y
+CONFIG_USB_GADGET_MUSB_HDRC=y
+CONFIG_USB_ACM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_SERIAL=y
+CONFIG_USB_SERIAL_KEYSPAN=y
+CONFIG_USB_SERIAL_KEYSPAN_MPR=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28X=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19=y
+CONFIG_USB_SERIAL_KEYSPAN_USA18X=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19W=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y
+CONFIG_USB_SERIAL_KEYSPAN_USA49W=y
+CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_USB_G_ANDROID=y
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_EMBEDDED_SDIO=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_OMAP=y
+CONFIG_MMC_OMAP_HS=y
+CONFIG_SWITCH=y
+CONFIG_SWITCH_GPIO=y
+CONFIG_RTC_CLASS=y
+CONFIG_STAGING=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_RAM_CONSOLE=y
+CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT4_FS=y
+# CONFIG_EXT4_FS_XATTR is not set
+# CONFIG_DNOTIFY is not set
+CONFIG_FUSE_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+# CONFIG_NETWORK_FILESYSTEMS is not set
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_EFI_PARTITION=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_FS=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_DETECT_HUNG_TASK=y
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_DEBUG_RT_MUTEXES=y
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_SPINLOCK_SLEEP=y
+CONFIG_DEBUG_INFO=y
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+# CONFIG_ARM_UNWIND is not set
+CONFIG_DEBUG_USER=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRC_CCITT=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SND_OMAP_SOC=y
+CONFIG_SND_OMAP_SOC_SDP4430=y
+CONFIG_SND_OMAP_SOC_OMAP4_HDMI=y
+CONFIG_OMAP_HSI=y
+CONFIG_OMAP_HSI_DEVICE=y
+CONFIG_CFG80211=y
+CONFIG_NL80211_TESTMODE=y
+CONFIG_LIB80211=y
+CONFIG_MAC80211=y
+CONFIG_MAC80211_LEDS=y
+CONFIG_MAC80211_DEBUGFS=y
+CONFIG_USB_ZD1201=y
+CONFIG_WL12XX_MENU=y
+CONFIG_WL12XX=y
+CONFIG_WL12XX_SDIO=y
+CONFIG_CRYPTO_PCBC=y
+CONFIG_CRYPTO_MD4=y
+CONFIG_CRYPTO_MICHAEL_MIC=y
+CONFIG_CRYPTO_SHA256=y
+CONFIG_OMAP_TEMP_SENSOR=y
+CONFIG_OMAP_DIE_TEMP_SENSOR=y
+CONFIG_TI_ST=y
+CONFIG_KEYBOARD_GPIO=y
diff --git a/kernel/arch/arm/mach-omap2/board-omap3beagle.c b/kernel/arch/arm/mach-omap2/board-omap3beagle.c
new file mode 100644
index 000000000000..b3d1b81b2a2e
--- /dev/null
+++ b/kernel/arch/arm/mach-omap2/board-omap3beagle.c
@@ -0,0 +1,1038 @@
+/*
+ * linux/arch/arm/mach-omap2/board-omap3beagle.c
+ *
+ * Copyright (C) 2008 Texas Instruments
+ *
+ * Modified from mach-omap2/board-3430sdp.c
+ *
+ * Initial code: Syed Mohammed Khasim
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/leds.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/gpio_keys.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/nand.h>
+#include <linux/mmc/host.h>
+
+#include <linux/usb/android_composite.h>
+
+#include <linux/regulator/machine.h>
+#include <linux/i2c/twl.h>
+
+#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/flash.h>
+
+#include <plat/board.h>
+#include <plat/common.h>
+#include <plat/display.h>
+#include <plat/gpmc.h>
+#include <plat/nand.h>
+#include <plat/usb.h>
+
+#include "mux.h"
+#include "hsmmc.h"
+#include "timer-gp.h"
+#include "board-flash.h"
+
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4
+#include <linux/input/synaptics_dsx.h>
+
+#define TM_SAMPLE1 (1) // 2D only
+#define TM_SAMPLE2 (2) // 2D + 0D x 2
+#define TM_SAMPLE3 (3) // 2D + 0D x 4
+#define SYNAPTICS_MODULE TM_SAMPLE1
+#endif
+
+#define NAND_BLOCK_SIZE SZ_128K
+
+#ifdef CONFIG_USB_ANDROID
+#define GOOGLE_VENDOR_ID 0x18d1
+#define GOOGLE_PRODUCT_ID 0x9018
+#define GOOGLE_ADB_PRODUCT_ID 0x9015
+#endif
+
+/* Synaptics Thin Driver */
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4
+static int synaptics_gpio_setup(unsigned gpio, bool configure)
+{
+ int retval=0;
+ if (configure)
+ {
+ retval = gpio_request(gpio, "rmi4_attn");
+ if (retval) {
+ pr_err("%s: Failed to get attn gpio %d. Code: %d.",
+ __func__, gpio, retval);
+ return retval;
+ }
+ omap_mux_init_signal("sdmmc2_clk.gpio_130", OMAP_PIN_INPUT_PULLUP);
+
+ retval = gpio_direction_input(gpio);
+ if (retval) {
+ pr_err("%s: Failed to setup attn gpio %d. Code: %d.",
+ __func__, gpio, retval);
+ gpio_free(gpio);
+ }
+ } else {
+ pr_warn("%s: No way to deconfigure gpio %d.",
+ __func__, gpio);
+ }
+
+ return retval;
+}
+
+ #if (SYNAPTICS_MODULE == TM_SAMPLE1)
+#define TM_SAMPLE1_ADDR 0x20
+#define TM_SAMPLE1_ATTN 130
+
+static unsigned char TM_SAMPLE1_f1a_button_codes[] = {};
+
+static struct synaptics_rmi4_capacitance_button_map TM_SAMPLE1_capacitance_button_map = {
+ .nbuttons = ARRAY_SIZE(TM_SAMPLE1_f1a_button_codes),
+ .map = TM_SAMPLE1_f1a_button_codes,
+};
+
+static struct synaptics_rmi4_platform_data rmi4_platformdata = {
+ .irq_flags = IRQF_TRIGGER_FALLING,
+ .irq_gpio = TM_SAMPLE1_ATTN,
+ .gpio_config = synaptics_gpio_setup,
+ .capacitance_button_map = &TM_SAMPLE1_capacitance_button_map,
+};
+
+static struct i2c_board_info bus2_i2c_devices[] = {
+ {
+ I2C_BOARD_INFO("synaptics_rmi4_i2c", TM_SAMPLE1_ADDR),
+ .platform_data = &rmi4_platformdata,
+ },
+};
+
+#elif (SYNAPTICS_MODULE == TM_SAMPLE2)
+#define TM_SAMPLE2_ADDR 0x20
+#define TM_SAMPLE2_ATTN 130
+
+static unsigned char TM_SAMPLE2_f1a_button_codes[] = {KEY_MENU, KEY_BACK};
+
+static struct synaptics_rmi4_capacitance_button_map TM_SAMPLE2_capacitance_button_map = {
+ .nbuttons = ARRAY_SIZE(TM_SAMPLE2_f1a_button_codes),
+ .map = TM_SAMPLE2_f1a_button_codes,
+};
+
+static struct synaptics_rmi4_platform_data rmi4_platformdata = {
+ .irq_flags = IRQF_TRIGGER_FALLING,
+ .irq_gpio = TM_SAMPLE2_ATTN,
+ .gpio_config = synaptics_gpio_setup,
+ .capacitance_button_map = &TM_SAMPLE2_capacitance_button_map,
+};
+
+static struct i2c_board_info bus2_i2c_devices[] = {
+ {
+ I2C_BOARD_INFO("synaptics_rmi4_i2c", TM_SAMPLE2_ADDR),
+ .platform_data = &rmi4_platformdata,
+ },
+};
+
+#elif (SYNAPTICS_MODULE == TM_SAMPLE3)
+#define TM_SAMPLE3_ADDR 0x20
+#define TM_SAMPLE3_ATTN 130
+
+static unsigned char TM_SAMPLE3_f1a_button_codes[] = {KEY_MENU, KEY_HOME,KEY_BACK,KEY_SEARCH};
+
+static struct synaptics_rmi4_capacitance_button_map TM_SAMPLE3_capacitance_button_map = {
+ .nbuttons = ARRAY_SIZE(TM_SAMPLE3_f1a_button_codes),
+ .map = TM_SAMPLE3_f1a_button_codes,
+};
+
+static struct synaptics_rmi4_platform_data rmi4_platformdata = {
+ .irq_flags = IRQF_TRIGGER_FALLING,
+ .irq_gpio = TM_SAMPLE3_ATTN,
+ .gpio_config = synaptics_gpio_setup,
+ .capacitance_button_map = &TM_SAMPLE3_capacitance_button_map,
+};
+
+static struct i2c_board_info bus2_i2c_devices[] = {
+ {
+ I2C_BOARD_INFO("synaptics_rmi4_i2c", TM_SAMPLE3_ADDR),
+ .platform_data = &rmi4_platformdata,
+ },
+};
+#endif
+
+void __init i2c_device_setup(void)
+{
+ pr_info(">>>>I2C device setup.");
+ if (ARRAY_SIZE(bus2_i2c_devices)) {
+ i2c_register_board_info(2, bus2_i2c_devices,
+ ARRAY_SIZE(bus2_i2c_devices));
+ }
+}
+
+/* End of Synaptics change for beagle board */
+
+static char *usb_functions_adb[] = {
+ "adb",
+};
+
+static char *usb_functions_mass_storage[] = {
+ "usb_mass_storage",
+};
+static char *usb_functions_ums_adb[] = {
+ "usb_mass_storage",
+ "adb",
+};
+
+static char *usb_functions_all[] = {
+ "adb", "usb_mass_storage",
+};
+
+static struct android_usb_product usb_products[] = {
+ {
+ .product_id = GOOGLE_PRODUCT_ID,
+ .num_functions = ARRAY_SIZE(usb_functions_adb),
+ .functions = usb_functions_adb,
+ },
+ {
+ .product_id = GOOGLE_PRODUCT_ID,
+ .num_functions = ARRAY_SIZE(usb_functions_mass_storage),
+ .functions = usb_functions_mass_storage,
+ },
+ {
+ .product_id = GOOGLE_PRODUCT_ID,
+ .num_functions = ARRAY_SIZE(usb_functions_ums_adb),
+ .functions = usb_functions_ums_adb,
+ },
+};
+
+static struct usb_mass_storage_platform_data mass_storage_pdata = {
+ .nluns = 1,
+ .vendor = "rowboat",
+ .product = "rowboat gadget",
+ .release = 0x100,
+};
+
+static struct platform_device usb_mass_storage_device = {
+ .name = "usb_mass_storage",
+ .id = -1,
+ .dev = {
+ .platform_data = &mass_storage_pdata,
+ },
+};
+
+static struct android_usb_platform_data android_usb_pdata = {
+ .vendor_id = GOOGLE_VENDOR_ID,
+ .product_id = GOOGLE_PRODUCT_ID,
+ .functions = usb_functions_all,
+ .products = usb_products,
+ .num_products = ARRAY_SIZE(usb_products),
+ .version = 0x0100,
+ .product_name = "rowboat gadget",
+ .manufacturer_name = "rowboat",
+ .serial_number = "20100720",
+ .num_functions = ARRAY_SIZE(usb_functions_all),
+};
+
+static struct platform_device androidusb_device = {
+ .name = "android_usb",
+ .id = -1,
+ .dev = {
+ .platform_data = &android_usb_pdata,
+ },
+};
+
+static void omap3beagle_android_gadget_init(void)
+{
+ platform_device_register(&androidusb_device);
+}
+#endif
+/*
+ * OMAP3 Beagle revision
+ * Run time detection of Beagle revision is done by reading GPIO.
+ * GPIO ID -
+ * AXBX = GPIO173, GPIO172, GPIO171: 1 1 1
+ * C1_3 = GPIO173, GPIO172, GPIO171: 1 1 0
+ * C4 = GPIO173, GPIO172, GPIO171: 1 0 1
+ * XM = GPIO173, GPIO172, GPIO171: 0 0 0
+ */
+enum {
+ OMAP3BEAGLE_BOARD_UNKN = 0,
+ OMAP3BEAGLE_BOARD_AXBX,
+ OMAP3BEAGLE_BOARD_C1_3,
+ OMAP3BEAGLE_BOARD_C4,
+ OMAP3BEAGLE_BOARD_XM,
+ OMAP3BEAGLE_BOARD_XMC,
+};
+
+extern void omap_pm_sys_offmode_select(int);
+extern void omap_pm_sys_offmode_pol(int);
+extern void omap_pm_sys_clkreq_pol(int);
+extern void omap_pm_auto_off(int);
+extern void omap_pm_auto_ret(int);
+
+static u8 omap3_beagle_version;
+
+static u8 omap3_beagle_get_rev(void)
+{
+ return omap3_beagle_version;
+}
+
+/**
+ * Board specific initialization of PM components
+ */
+static void __init omap3_beagle_pm_init(void)
+{
+ /* Use sys_offmode signal */
+ omap_pm_sys_offmode_select(1);
+
+ /* sys_clkreq - active high */
+ omap_pm_sys_clkreq_pol(1);
+
+ /* sys_offmode - active low */
+ omap_pm_sys_offmode_pol(0);
+
+ /* Automatically send OFF command */
+ omap_pm_auto_off(1);
+
+ /* Automatically send RET command */
+ omap_pm_auto_ret(1);
+}
+
+static void __init omap3_beagle_init_rev(void)
+{
+ int ret;
+ u16 beagle_rev = 0;
+
+ omap_mux_init_gpio(171, OMAP_PIN_INPUT_PULLUP);
+ omap_mux_init_gpio(172, OMAP_PIN_INPUT_PULLUP);
+ omap_mux_init_gpio(173, OMAP_PIN_INPUT_PULLUP);
+
+ ret = gpio_request(171, "rev_id_0");
+ if (ret < 0)
+ goto fail0;
+
+ ret = gpio_request(172, "rev_id_1");
+ if (ret < 0)
+ goto fail1;
+
+ ret = gpio_request(173, "rev_id_2");
+ if (ret < 0)
+ goto fail2;
+
+ gpio_direction_input(171);
+ gpio_direction_input(172);
+ gpio_direction_input(173);
+
+ beagle_rev = gpio_get_value(171) | (gpio_get_value(172) << 1)
+ | (gpio_get_value(173) << 2);
+
+ switch (beagle_rev) {
+ case 7:
+ printk(KERN_INFO "OMAP3 Beagle Rev: Ax/Bx\n");
+ omap3_beagle_version = OMAP3BEAGLE_BOARD_AXBX;
+ break;
+ case 6:
+ printk(KERN_INFO "OMAP3 Beagle Rev: C1/C2/C3\n");
+ omap3_beagle_version = OMAP3BEAGLE_BOARD_C1_3;
+ break;
+ case 5:
+ printk(KERN_INFO "OMAP3 Beagle Rev: C4\n");
+ omap3_beagle_version = OMAP3BEAGLE_BOARD_C4;
+ break;
+ case 2:
+ printk(KERN_INFO "OMAP3 Beagle Rev: xM C\n");
+ omap3_beagle_version = OMAP3BEAGLE_BOARD_XMC;
+ break;
+ case 0:
+ printk(KERN_INFO "OMAP3 Beagle Rev: xM\n");
+ omap3_beagle_version = OMAP3BEAGLE_BOARD_XM;
+ break;
+ default:
+ printk(KERN_INFO "OMAP3 Beagle Rev: unknown %hd\n", beagle_rev);
+ omap3_beagle_version = OMAP3BEAGLE_BOARD_UNKN;
+ }
+
+ return;
+
+fail2:
+ gpio_free(172);
+fail1:
+ gpio_free(171);
+fail0:
+ printk(KERN_ERR "Unable to get revision detection GPIO pins\n");
+ omap3_beagle_version = OMAP3BEAGLE_BOARD_UNKN;
+
+ return;
+}
+
+static struct mtd_partition omap3beagle_nand_partitions[] = {
+ /* All the partition sizes are listed in terms of NAND block size */
+ {
+ .name = "X-Loader",
+ .offset = 0,
+ .size = 4 * NAND_BLOCK_SIZE,
+ .mask_flags = MTD_WRITEABLE, /* force read-only */
+ },
+ {
+ .name = "U-Boot",
+ .offset = MTDPART_OFS_APPEND, /* Offset = 0x80000 */
+ .size = 15 * NAND_BLOCK_SIZE,
+ .mask_flags = MTD_WRITEABLE, /* force read-only */
+ },
+ {
+ .name = "U-Boot Env",
+ .offset = MTDPART_OFS_APPEND, /* Offset = 0x260000 */
+ .size = 1 * NAND_BLOCK_SIZE,
+ },
+ {
+ .name = "Kernel",
+ .offset = MTDPART_OFS_APPEND, /* Offset = 0x280000 */
+ .size = 32 * NAND_BLOCK_SIZE,
+ },
+ {
+ .name = "File System",
+ .offset = MTDPART_OFS_APPEND, /* Offset = 0x680000 */
+ .size = MTDPART_SIZ_FULL,
+ },
+};
+
+/* DSS */
+
+static int beagle_enable_dvi(struct omap_dss_device *dssdev)
+{
+ if (gpio_is_valid(dssdev->reset_gpio))
+ gpio_set_value(dssdev->reset_gpio, 1);
+
+ return 0;
+}
+
+static void beagle_disable_dvi(struct omap_dss_device *dssdev)
+{
+ if (gpio_is_valid(dssdev->reset_gpio))
+ gpio_set_value(dssdev->reset_gpio, 0);
+}
+
+static struct omap_dss_device beagle_dvi_device = {
+ .type = OMAP_DISPLAY_TYPE_DPI,
+ .name = "dvi",
+ .driver_name = "generic_panel",
+ .phy.dpi.data_lines = 24,
+ .reset_gpio = -EINVAL,
+ .platform_enable = beagle_enable_dvi,
+ .platform_disable = beagle_disable_dvi,
+};
+
+static struct omap_dss_device beagle_tv_device = {
+ .name = "tv",
+ .driver_name = "venc",
+ .type = OMAP_DISPLAY_TYPE_VENC,
+ .phy.venc.type = OMAP_DSS_VENC_TYPE_SVIDEO,
+};
+
+static struct omap_dss_device *beagle_dss_devices[] = {
+ &beagle_dvi_device,
+ &beagle_tv_device,
+};
+
+static struct omap_dss_board_info beagle_dss_data = {
+ .num_devices = ARRAY_SIZE(beagle_dss_devices),
+ .devices = beagle_dss_devices,
+ .default_device = &beagle_dvi_device,
+};
+
+static struct platform_device beagle_dss_device = {
+ .name = "omapdss",
+ .id = -1,
+ .dev = {
+ .platform_data = &beagle_dss_data,
+ },
+};
+
+static struct regulator_consumer_supply beagle_vdac_supply =
+ REGULATOR_SUPPLY("vdda_dac", "omapdss");
+
+static struct regulator_consumer_supply beagle_vdvi_supply =
+ REGULATOR_SUPPLY("vdds_dsi", "omapdss");
+
+static void __init beagle_display_init(void)
+{
+ int r;
+
+ r = gpio_request(beagle_dvi_device.reset_gpio, "DVI reset");
+ if (r < 0) {
+ printk(KERN_ERR "Unable to get DVI reset GPIO\n");
+ return;
+ }
+
+ gpio_direction_output(beagle_dvi_device.reset_gpio, 0);
+}
+
+#include "sdram-micron-mt46h32m32lf-6.h"
+
+static struct omap2_hsmmc_info mmc[] = {
+ {
+ .mmc = 1,
+ .caps = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA,
+ .gpio_wp = 29,
+ },
+ {} /* Terminator */
+};
+
+static struct regulator_consumer_supply beagle_vmmc1_supply = {
+ .supply = "vmmc",
+};
+
+static struct regulator_consumer_supply beagle_vsim_supply = {
+ .supply = "vmmc_aux",
+};
+
+static struct regulator_consumer_supply beagle_vaux3_supply = {
+ .supply = "cam_1v8",
+};
+
+static struct regulator_consumer_supply beagle_vaux4_supply = {
+ .supply = "cam_2v8",
+};
+
+static struct gpio_led gpio_leds[];
+
+static int beagle_twl_gpio_setup(struct device *dev,
+ unsigned gpio, unsigned ngpio)
+{
+ if (omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XM || omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XMC) {
+ mmc[0].gpio_wp = -EINVAL;
+ } else if ((omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_C1_3) ||
+ (omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_C4)) {
+ omap_mux_init_gpio(23, OMAP_PIN_INPUT);
+ mmc[0].gpio_wp = 23;
+ } else {
+ omap_mux_init_gpio(29, OMAP_PIN_INPUT);
+ }
+ /* gpio + 0 is "mmc0_cd" (input/IRQ) */
+ mmc[0].gpio_cd = gpio + 0;
+ omap2_hsmmc_init(mmc);
+
+ /* link regulators to MMC adapters */
+ beagle_vmmc1_supply.dev = mmc[0].dev;
+ beagle_vsim_supply.dev = mmc[0].dev;
+
+ /* REVISIT: need ehci-omap hooks for external VBUS
+ * power switch and overcurrent detect
+ */
+ if (omap3_beagle_get_rev() != OMAP3BEAGLE_BOARD_XM || omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XMC) {
+ gpio_request(gpio + 1, "EHCI_nOC");
+ gpio_direction_input(gpio + 1);
+ }
+
+ /*
+ * TWL4030_GPIO_MAX + 0 == ledA, EHCI nEN_USB_PWR (out, XM active
+ * high / others active low)
+ */
+ gpio_request(gpio + TWL4030_GPIO_MAX, "nEN_USB_PWR");
+ gpio_direction_output(gpio + TWL4030_GPIO_MAX, 0);
+ if (omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XM)
+ gpio_direction_output(gpio + TWL4030_GPIO_MAX, 1);
+ else
+ gpio_direction_output(gpio + TWL4030_GPIO_MAX, 0);
+
+ /* DVI reset GPIO is different between beagle revisions */
+ if (omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XM || omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XMC)
+ beagle_dvi_device.reset_gpio = 129;
+ else
+ beagle_dvi_device.reset_gpio = 170;
+
+ if (omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XM) {
+ /* Power on camera interface */
+ gpio_request(gpio + 2, "CAM_EN");
+ gpio_direction_output(gpio + 2, 1);
+
+ /* TWL4030_GPIO_MAX + 0 == ledA, EHCI nEN_USB_PWR (out, active low) */
+ gpio_request(gpio + TWL4030_GPIO_MAX, "nEN_USB_PWR");
+ gpio_direction_output(gpio + TWL4030_GPIO_MAX, 1);
+ } else {
+ gpio_request(gpio + 1, "EHCI_nOC");
+ gpio_direction_input(gpio + 1);
+
+ /* TWL4030_GPIO_MAX + 0 == ledA, EHCI nEN_USB_PWR (out, active low) */
+ gpio_request(gpio + TWL4030_GPIO_MAX, "nEN_USB_PWR");
+ gpio_direction_output(gpio + TWL4030_GPIO_MAX, 0);
+ }
+ /* TWL4030_GPIO_MAX + 1 == ledB, PMU_STAT (out, active low LED) */
+ gpio_leds[2].gpio = gpio + TWL4030_GPIO_MAX + 1;
+
+ /*
+ * gpio + 1 on Xm controls the TFP410's enable line (active low)
+ * gpio + 2 control varies depending on the board rev as follows:
+ * P7/P8 revisions(prototype): Camera EN
+ * A2+ revisions (production): LDO (supplies DVI, serial, led blocks)
+ */
+ if (omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XM || omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XMC) {
+ gpio_request(gpio + 1, "nDVI_PWR_EN");
+ gpio_direction_output(gpio + 1, 0);
+ gpio_request(gpio + 2, "DVI_LDO_EN");
+ gpio_direction_output(gpio + 2, 1);
+ }
+
+ return 0;
+}
+
+static struct twl4030_gpio_platform_data beagle_gpio_data = {
+ .gpio_base = OMAP_MAX_GPIO_LINES,
+ .irq_base = TWL4030_GPIO_IRQ_BASE,
+ .irq_end = TWL4030_GPIO_IRQ_END,
+ .use_leds = true,
+ .pullups = BIT(1),
+ .pulldowns = BIT(2) | BIT(6) | BIT(7) | BIT(8) | BIT(13)
+ | BIT(15) | BIT(16) | BIT(17),
+ .setup = beagle_twl_gpio_setup,
+};
+
+/* VMMC1 for MMC1 pins CMD, CLK, DAT0..DAT3 (20 mA, plus card == max 220 mA) */
+static struct regulator_init_data beagle_vmmc1 = {
+ .constraints = {
+ .min_uV = 1850000,
+ .max_uV = 3150000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE
+ | REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = 1,
+ .consumer_supplies = &beagle_vmmc1_supply,
+};
+
+/* VSIM for MMC1 pins DAT4..DAT7 (2 mA, plus card == max 50 mA) */
+static struct regulator_init_data beagle_vsim = {
+ .constraints = {
+ .min_uV = 1800000,
+ .max_uV = 3000000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE
+ | REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = 1,
+ .consumer_supplies = &beagle_vsim_supply,
+};
+
+/* VDAC for DSS driving S-Video (8 mA unloaded, max 65 mA) */
+static struct regulator_init_data beagle_vdac = {
+ .constraints = {
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = 1,
+ .consumer_supplies = &beagle_vdac_supply,
+};
+
+/* VPLL2 for digital video outputs */
+static struct regulator_init_data beagle_vpll2 = {
+ .constraints = {
+ .name = "VDVI",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = 1,
+ .consumer_supplies = &beagle_vdvi_supply,
+};
+
+/* VAUX3 for CAM_1V8 */
+static struct regulator_init_data beagle_vaux3 = {
+ .constraints = {
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .apply_uV = true,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = 1,
+ .consumer_supplies = &beagle_vaux3_supply,
+};
+
+ /* VAUX4 for CAM_2V8 */
+static struct regulator_init_data beagle_vaux4 = {
+ .constraints = {
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .apply_uV = true,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = 1,
+ .consumer_supplies = &beagle_vaux4_supply,
+};
+
+static struct twl4030_usb_data beagle_usb_data = {
+ .usb_mode = T2_USB_MODE_ULPI,
+};
+
+/**
+ * Macro to configure resources
+ */
+#define TWL4030_RESCONFIG(res,grp,typ1,typ2,state) \
+ { \
+ .resource = res, \
+ .devgroup = grp, \
+ .type = typ1, \
+ .type2 = typ2, \
+ .remap_sleep = state \
+ }
+
+static struct twl4030_resconfig __initdata board_twl4030_rconfig[] = {
+ TWL4030_RESCONFIG(RES_VPLL1, DEV_GRP_P1, 3, 1, RES_STATE_OFF), /* ? */
+ TWL4030_RESCONFIG(RES_VINTANA1, DEV_GRP_ALL, 1, 2, RES_STATE_SLEEP),
+ TWL4030_RESCONFIG(RES_VINTANA2, DEV_GRP_ALL, 0, 2, RES_STATE_SLEEP),
+ TWL4030_RESCONFIG(RES_VINTDIG, DEV_GRP_ALL, 1, 2, RES_STATE_SLEEP),
+ TWL4030_RESCONFIG(RES_VIO, DEV_GRP_ALL, 2, 2, RES_STATE_SLEEP),
+ TWL4030_RESCONFIG(RES_VDD1, DEV_GRP_P1, 4, 1, RES_STATE_OFF), /* ? */
+ TWL4030_RESCONFIG(RES_VDD2, DEV_GRP_P1, 3, 1, RES_STATE_OFF), /* ? */
+ TWL4030_RESCONFIG(RES_REGEN, DEV_GRP_ALL, 2, 1, RES_STATE_SLEEP),
+ TWL4030_RESCONFIG(RES_NRES_PWRON, DEV_GRP_ALL, 0, 1, RES_STATE_SLEEP),
+ TWL4030_RESCONFIG(RES_CLKEN, DEV_GRP_ALL, 3, 2, RES_STATE_SLEEP),
+ TWL4030_RESCONFIG(RES_SYSEN, DEV_GRP_ALL, 6, 1, RES_STATE_SLEEP),
+ TWL4030_RESCONFIG(RES_HFCLKOUT, DEV_GRP_P3, 0, 2, RES_STATE_SLEEP), /* ? */
+ TWL4030_RESCONFIG(0, 0, 0, 0, 0),
+};
+
+/**
+ * Optimized 'Active to Sleep' sequence
+ */
+static struct twl4030_ins omap3beagle_sleep_seq[] __initdata = {
+ { MSG_SINGULAR(DEV_GRP_NULL, RES_HFCLKOUT, RES_STATE_SLEEP), 20},
+ { MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_ALL, RES_TYPE_R0, RES_TYPE2_R1, RES_STATE_SLEEP), 2 },
+ { MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_ALL, RES_TYPE_R0, RES_TYPE2_R2, RES_STATE_SLEEP), 2 },
+};
+
+static struct twl4030_script omap3beagle_sleep_script __initdata = {
+ .script = omap3beagle_sleep_seq,
+ .size = ARRAY_SIZE(omap3beagle_sleep_seq),
+ .flags = TWL4030_SLEEP_SCRIPT,
+};
+
+/**
+ * Optimized 'Sleep to Active (P12)' sequence
+ */
+static struct twl4030_ins omap3beagle_wake_p12_seq[] __initdata = {
+ { MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_ALL, RES_TYPE_R0, RES_TYPE2_R1, RES_STATE_ACTIVE), 2 }
+};
+
+static struct twl4030_script omap3beagle_wake_p12_script __initdata = {
+ .script = omap3beagle_wake_p12_seq,
+ .size = ARRAY_SIZE(omap3beagle_wake_p12_seq),
+ .flags = TWL4030_WAKEUP12_SCRIPT,
+};
+
+/**
+ * Optimized 'Sleep to Active' (P3) sequence
+ */
+static struct twl4030_ins omap3beagle_wake_p3_seq[] __initdata = {
+ { MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_ALL, RES_TYPE_R0, RES_TYPE2_R2, RES_STATE_ACTIVE), 2 }
+};
+
+static struct twl4030_script omap3beagle_wake_p3_script __initdata = {
+ .script = omap3beagle_wake_p3_seq,
+ .size = ARRAY_SIZE(omap3beagle_wake_p3_seq),
+ .flags = TWL4030_WAKEUP3_SCRIPT,
+};
+
+/**
+ * Optimized warm reset sequence (for less power surge)
+ */
+static struct twl4030_ins omap3beagle_wrst_seq[] __initdata = {
+ { MSG_SINGULAR(DEV_GRP_NULL, RES_RESET, RES_STATE_OFF), 0x2 },
+ { MSG_SINGULAR(DEV_GRP_NULL, RES_MAIN_REF, RES_STATE_WRST), 2 },
+ { MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_ALL, RES_TYPE_R0, RES_TYPE2_R2, RES_STATE_WRST), 0x2},
+ { MSG_SINGULAR(DEV_GRP_NULL, RES_VUSB_3V1, RES_STATE_WRST), 0x2 },
+ { MSG_SINGULAR(DEV_GRP_NULL, RES_VPLL1, RES_STATE_WRST), 0x2 },
+ { MSG_SINGULAR(DEV_GRP_NULL, RES_VDD2, RES_STATE_WRST), 0x7 },
+ { MSG_SINGULAR(DEV_GRP_NULL, RES_VDD1, RES_STATE_WRST), 0x25 },
+ { MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_RC, RES_TYPE_ALL, RES_TYPE2_R0, RES_STATE_WRST), 0x2 },
+ { MSG_SINGULAR(DEV_GRP_NULL, RES_RESET, RES_STATE_ACTIVE), 0x2 },
+
+};
+
+static struct twl4030_script omap3beagle_wrst_script __initdata = {
+ .script = omap3beagle_wrst_seq,
+ .size = ARRAY_SIZE(omap3beagle_wrst_seq),
+ .flags = TWL4030_WRST_SCRIPT,
+};
+
+static struct twl4030_script __initdata *board_twl4030_scripts[] = {
+ &omap3beagle_wake_p12_script,
+ &omap3beagle_wake_p3_script,
+ &omap3beagle_sleep_script,
+ &omap3beagle_wrst_script
+};
+
+static struct twl4030_power_data __initdata omap3beagle_script_data = {
+ .scripts = board_twl4030_scripts,
+ .num = ARRAY_SIZE(board_twl4030_scripts),
+ .resource_config = board_twl4030_rconfig,
+};
+
+static struct twl4030_codec_audio_data beagle_audio_data = {
+ .audio_mclk = 26000000,
+ .digimic_delay = 1,
+ .ramp_delay_value = 1,
+ .offset_cncl_path = 1,
+ .check_defaults = false,
+ .reset_registers = false,
+ .reset_registers = false,
+};
+
+static struct twl4030_codec_data beagle_codec_data = {
+ .audio_mclk = 26000000,
+ .audio = &beagle_audio_data,
+};
+
+static struct twl4030_platform_data beagle_twldata = {
+ .irq_base = TWL4030_IRQ_BASE,
+ .irq_end = TWL4030_IRQ_END,
+
+ /* platform_data for children goes here */
+ .usb = &beagle_usb_data,
+ .gpio = &beagle_gpio_data,
+ .codec = &beagle_codec_data,
+ .vmmc1 = &beagle_vmmc1,
+ .vsim = &beagle_vsim,
+ .vdac = &beagle_vdac,
+ .vpll2 = &beagle_vpll2,
+ .vaux3 = &beagle_vaux3,
+ .vaux4 = &beagle_vaux4,
+ .power = &omap3beagle_script_data,
+};
+
+static struct i2c_board_info __initdata beagle_i2c_boardinfo[] = {
+ {
+ I2C_BOARD_INFO("twl4030", 0x48),
+ .flags = I2C_CLIENT_WAKE,
+ .irq = INT_34XX_SYS_NIRQ,
+ .platform_data = &beagle_twldata,
+ },
+};
+
+static struct i2c_board_info __initdata beagle_i2c_eeprom[] = {
+ {
+ I2C_BOARD_INFO("eeprom", 0x50),
+ },
+};
+
+static int __init omap3_beagle_i2c_init(void)
+{
+ omap_register_i2c_bus(1, 2600, beagle_i2c_boardinfo,
+ ARRAY_SIZE(beagle_i2c_boardinfo));
+
+ /* Bus 2 is used for Camera/Sensor interface */
+ if (ARRAY_SIZE(bus2_i2c_devices))
+ omap_register_i2c_bus(2, 400, bus2_i2c_devices,
+ ARRAY_SIZE(bus2_i2c_devices));
+ else
+ omap_register_i2c_bus(2, 400, NULL, 0);
+
+ /* Bus 3 is attached to the DVI port where devices like the pico DLP
+ * projector don't work reliably with 400kHz */
+ omap_register_i2c_bus(3, 100, beagle_i2c_eeprom, ARRAY_SIZE(beagle_i2c_eeprom));
+
+ return 0;
+}
+
+static struct gpio_led gpio_leds[] = {
+ {
+ .name = "beagleboard::usr0",
+ .default_trigger = "heartbeat",
+ .gpio = 150,
+ },
+ {
+ .name = "beagleboard::usr1",
+ .default_trigger = "mmc0",
+ .gpio = 149,
+ },
+ {
+ .name = "beagleboard::pmu_stat",
+ .gpio = -EINVAL, /* gets replaced */
+ .active_low = true,
+ },
+};
+
+static struct gpio_led_platform_data gpio_led_info = {
+ .leds = gpio_leds,
+ .num_leds = ARRAY_SIZE(gpio_leds),
+};
+
+static struct platform_device leds_gpio = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &gpio_led_info,
+ },
+};
+
+static struct gpio_keys_button gpio_buttons[] = {
+ {
+ .code = KEY_POWER,
+ .gpio = 4,
+ .desc = "user",
+ .wakeup = 1,
+ },
+};
+
+static struct gpio_keys_platform_data gpio_key_info = {
+ .buttons = gpio_buttons,
+ .nbuttons = ARRAY_SIZE(gpio_buttons),
+};
+
+static struct platform_device keys_gpio = {
+ .name = "gpio-keys",
+ .id = -1,
+ .dev = {
+ .platform_data = &gpio_key_info,
+ },
+};
+
+static void __init omap3_beagle_init_irq(void)
+{
+ omap2_init_common_infrastructure();
+ omap2_init_common_devices(mt46h32m32lf6_sdrc_params,
+ mt46h32m32lf6_sdrc_params);
+ omap_init_irq();
+ gpmc_init();
+#ifdef CONFIG_OMAP_32K_TIMER
+ if (omap3_beagle_version == OMAP3BEAGLE_BOARD_AXBX)
+ omap2_gp_clockevent_set_gptimer(12);
+ else
+ omap2_gp_clockevent_set_gptimer(1);
+#endif
+}
+
+static struct platform_device *omap3_beagle_devices[] __initdata = {
+ &leds_gpio,
+ &keys_gpio,
+ &beagle_dss_device,
+ &usb_mass_storage_device,
+};
+
+static void __init omap3beagle_flash_init(void)
+{
+ u8 cs = 0;
+ u8 nandcs = GPMC_CS_NUM + 1;
+
+ /* find out the chip-select on which NAND exists */
+ while (cs < GPMC_CS_NUM) {
+ u32 ret = 0;
+ ret = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
+
+ if ((ret & 0xC00) == 0x800) {
+ printk(KERN_INFO "Found NAND on CS%d\n", cs);
+ if (nandcs > GPMC_CS_NUM)
+ nandcs = cs;
+ }
+ cs++;
+ }
+
+ if (nandcs > GPMC_CS_NUM) {
+ printk(KERN_INFO "NAND: Unable to find configuration "
+ "in GPMC\n ");
+ return;
+ }
+
+ if (nandcs < GPMC_CS_NUM) {
+ printk(KERN_INFO "Registering NAND on CS%d\n", nandcs);
+ board_nand_init(omap3beagle_nand_partitions,
+ ARRAY_SIZE(omap3beagle_nand_partitions),
+ nandcs, NAND_BUSWIDTH_16);
+ }
+}
+
+static const struct ehci_hcd_omap_platform_data ehci_pdata __initconst = {
+
+ .port_mode[0] = EHCI_HCD_OMAP_MODE_PHY,
+ .port_mode[1] = EHCI_HCD_OMAP_MODE_PHY,
+ .port_mode[2] = EHCI_HCD_OMAP_MODE_UNKNOWN,
+
+ .phy_reset = true,
+ .reset_gpio_port[0] = -EINVAL,
+ .reset_gpio_port[1] = 147,
+ .reset_gpio_port[2] = -EINVAL
+};
+
+#ifdef CONFIG_OMAP_MUX
+static struct omap_board_mux board_mux[] __initdata = {
+ OMAP3_MUX(SYS_NIRQ, OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP |
+ OMAP_PIN_OFF_INPUT_PULLUP | OMAP_PIN_OFF_OUTPUT_LOW |
+ OMAP_PIN_OFF_WAKEUPENABLE),
+ { .reg_offset = OMAP_MUX_TERMINATOR },
+};
+#endif
+
+static struct omap_musb_board_data musb_board_data = {
+ .interface_type = MUSB_INTERFACE_ULPI,
+ .mode = MUSB_OTG,
+ .power = 100,
+};
+
+static void __init omap3_beagle_init(void)
+{
+ omap3_mux_init(board_mux, OMAP_PACKAGE_CBB);
+ omap3_beagle_init_rev();
+ omap3_beagle_i2c_init();
+ platform_add_devices(omap3_beagle_devices,
+ ARRAY_SIZE(omap3_beagle_devices));
+ omap_serial_init();
+
+ omap_mux_init_gpio(170, OMAP_PIN_INPUT);
+ gpio_request(170, "DVI_nPD");
+ /* REVISIT leave DVI powered down until it's needed ... */
+ gpio_direction_output(170, true);
+
+ usb_musb_init(&musb_board_data);
+ usb_ehci_init(&ehci_pdata);
+ omap3beagle_flash_init();
+
+ /* Ensure SDRC pins are mux'd for self-refresh */
+ omap_mux_init_signal("sdrc_cke0", OMAP_PIN_OUTPUT);
+ omap_mux_init_signal("sdrc_cke1", OMAP_PIN_OUTPUT);
+
+ beagle_display_init();
+#ifdef CONFIG_USB_ANDROID
+ omap3beagle_android_gadget_init();
+#endif
+ omap3_beagle_pm_init();
+}
+
+MACHINE_START(OMAP3_BEAGLE, "OMAP3 Beagle Board")
+ /* Maintainer: Syed Mohammed Khasim - http://beagleboard.org */
+ .boot_params = 0x80000100,
+ .map_io = omap3_map_io,
+ .reserve = omap_reserve,
+ .init_irq = omap3_beagle_init_irq,
+ .init_machine = omap3_beagle_init,
+ .timer = &omap_timer,
+MACHINE_END
diff --git a/kernel/arch/arm/mach-omap2/board-omap4panda.c b/kernel/arch/arm/mach-omap2/board-omap4panda.c
new file mode 100644
index 000000000000..4f8c79ddd650
--- /dev/null
+++ b/kernel/arch/arm/mach-omap2/board-omap4panda.c
@@ -0,0 +1,1053 @@
+/*
+ * Board support file for OMAP4430 based PandaBoard.
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * Author: David Anders <x0132446@ti.com>
+ *
+ * Based on mach-omap2/board-4430sdp.c
+ *
+ * Author: Santosh Shilimkar <santosh.shilimkar@ti.com>
+ *
+ * Based on mach-omap2/board-3430sdp.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/input.h>
+#include <linux/io.h>
+#include <linux/leds.h>
+#include <linux/gpio.h>
+#include <linux/gpio_keys.h>
+#include <linux/omapfb.h>
+#include <linux/reboot.h>
+#include <linux/usb/otg.h>
+#include <linux/i2c/twl.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/fixed.h>
+#include <linux/wl12xx.h>
+#include <linux/memblock.h>
+#include <linux/skbuff.h>
+#include <linux/ti_wilink_st.h>
+#include <linux/platform_data/ram_console.h>
+
+#include <mach/hardware.h>
+#include <mach/omap4-common.h>
+#include <mach/emif.h>
+#include <mach/lpddr2-elpida.h>
+#include <mach/dmm.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <video/omapdss.h>
+
+#include <plat/board.h>
+#include <plat/common.h>
+#include <plat/usb.h>
+#include <plat/mmc.h>
+#include <plat/remoteproc.h>
+#include <plat/vram.h>
+#include <video/omap-panel-generic-dpi.h>
+#include "timer-gp.h"
+
+#include "hsmmc.h"
+#include "control.h"
+#include "mux.h"
+#include "common-board-devices.h"
+#include "prm-regbits-44xx.h"
+#include "prm44xx.h"
+#include "pm.h"
+#include "resetreason.h"
+
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4
+#include <linux/input/synaptics_dsx.h>
+#define TM_SAMPLE1 (1) // 2D only
+#define TM_SAMPLE2 (2) // 2D + 0D x 2
+#define TM_SAMPLE3 (3) // 2D + 0D x 4
+#define SYNAPTICS_MODULE TM_SAMPLE1
+#endif
+
+#define PANDA_RAMCONSOLE_START (PLAT_PHYS_OFFSET + SZ_512M)
+#define PANDA_RAMCONSOLE_SIZE SZ_2M
+
+#define GPIO_HUB_POWER 1
+#define GPIO_HUB_NRESET 62
+#define GPIO_WIFI_PMENA 43
+#define GPIO_WIFI_IRQ 53
+#define HDMI_GPIO_CT_CP_HPD 60
+#define HDMI_GPIO_HPD 63 /* Hot plug pin for HDMI */
+#define HDMI_GPIO_LS_OE 41 /* Level shifter for HDMI */
+#define TPS62361_GPIO 7 /* VCORE1 power control */
+#define PANDA_BT_GPIO 46
+
+
+#define PHYS_ADDR_SMC_SIZE (SZ_1M * 3)
+#define PHYS_ADDR_SMC_MEM (0x80000000 + SZ_1G - PHYS_ADDR_SMC_SIZE)
+#define OMAP_ION_HEAP_SECURE_INPUT_SIZE (SZ_1M * 90)
+#define PHYS_ADDR_DUCATI_SIZE (SZ_1M * 105)
+#define PHYS_ADDR_DUCATI_MEM (PHYS_ADDR_SMC_MEM - PHYS_ADDR_DUCATI_SIZE - \
+ OMAP_ION_HEAP_SECURE_INPUT_SIZE)
+
+#define WILINK_UART_DEV_NAME "/dev/ttyO1"
+
+
+/* Synaptics changes for PandaBoard */
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4
+static int synaptics_gpio_setup(unsigned gpio, bool configure)
+{
+ int retval = 0;
+
+ if (configure) {
+ retval = gpio_request(gpio, "rmi4_attn");
+ if (retval) {
+ pr_err("%s: Failed to get attn gpio %d (code: %d)",
+ __func__, gpio, retval);
+ return retval;
+ }
+ omap_mux_init_signal("gpmc_ad15.gpio_39", OMAP_PIN_INPUT_PULLUP);
+
+ retval = gpio_direction_input(gpio);
+ if (retval) {
+ pr_err("%s: Failed to setup attn gpio %d (code: %d)",
+ __func__, gpio, retval);
+ gpio_free(gpio);
+ }
+ } else {
+ pr_warn("%s: No way to deconfigure gpio %d",
+ __func__, gpio);
+ }
+
+ return retval;
+}
+
+ #if (SYNAPTICS_MODULE == TM_SAMPLE1)
+#define TM_SAMPLE1_ADDR 0x20
+#define TM_SAMPLE1_ATTN 130
+
+static unsigned char TM_SAMPLE1_f1a_button_codes[] = {};
+
+static struct synaptics_rmi4_capacitance_button_map TM_SAMPLE1_capacitance_button_map = {
+ .nbuttons = ARRAY_SIZE(TM_SAMPLE1_f1a_button_codes),
+ .map = TM_SAMPLE1_f1a_button_codes,
+};
+
+static struct synaptics_rmi4_platform_data rmi4_platformdata = {
+ .irq_flags = IRQF_TRIGGER_FALLING,
+ .irq_gpio = TM_SAMPLE1_ATTN,
+ .gpio_config = synaptics_gpio_setup,
+ .capacitance_button_map = &TM_SAMPLE1_capacitance_button_map,
+};
+
+static struct i2c_board_info bus4_i2c_devices[] = {
+ {
+ I2C_BOARD_INFO("synaptics_rmi4_i2c", TM_SAMPLE1_ADDR),
+ .platform_data = &rmi4_platformdata,
+ },
+};
+
+#elif (SYNAPTICS_MODULE == TM_SAMPLE2)
+#define TM_SAMPLE2_ADDR 0x20
+#define TM_SAMPLE2_ATTN 130
+
+static unsigned char TM_SAMPLE2_f1a_button_codes[] = {KEY_MENU, KEY_BACK};
+
+static struct synaptics_rmi4_capacitance_button_map TM_SAMPLE2_capacitance_button_map = {
+ .nbuttons = ARRAY_SIZE(TM_SAMPLE2_f1a_button_codes),
+ .map = TM_SAMPLE2_f1a_button_codes,
+};
+
+static struct synaptics_rmi4_platform_data rmi4_platformdata = {
+ .irq_flags = IRQF_TRIGGER_FALLING,
+ .irq_gpio = TM_SAMPLE2_ATTN,
+ .gpio_config = synaptics_gpio_setup,
+ .capacitance_button_map = &TM_SAMPLE2_capacitance_button_map,
+};
+
+static struct i2c_board_info bus4_i2c_devices[] = {
+ {
+ I2C_BOARD_INFO("synaptics_rmi4_i2c", TM_SAMPLE2_ADDR),
+ .platform_data = &rmi4_platformdata,
+ },
+};
+};
+
+#elif (SYNAPTICS_MODULE == TM_SAMPLE3)
+#define TM_SAMPLE3_ADDR 0x20
+#define TM_SAMPLE3_ATTN 130
+
+static unsigned char TM_SAMPLE3_f1a_button_codes[] = {KEY_MENU, KEY_HOME,KEY_BACK,KEY_SEARCH};
+
+static struct synaptics_rmi4_capacitance_button_map TM_SAMPLE3_capacitance_button_map = {
+ .nbuttons = ARRAY_SIZE(TM_SAMPLE3_f1a_button_codes),
+ .map = TM_SAMPLE3_f1a_button_codes,
+};
+
+static struct synaptics_rmi4_platform_data rmi4_platformdata = {
+ .irq_flags = IRQF_TRIGGER_FALLING,
+ .irq_gpio = TM_SAMPLE3_ATTN,
+ .gpio_config = synaptics_gpio_setup,
+ .capacitance_button_map = &TM_SAMPLE3_capacitance_button_map,
+};
+
+static struct i2c_board_info bus4_i2c_devices[] = {
+ {
+ I2C_BOARD_INFO("synaptics_rmi4_i2c", TM_SAMPLE3_ADDR),
+ .platform_data = &rmi4_platformdata,
+ },
+};
+#endif
+
+void __init i2c_device_setup(void)
+{
+ pr_info(">>>>I2C device setup");
+ if (ARRAY_SIZE(bus4_i2c_devices)) {
+ i2c_register_board_info(4, bus4_i2c_devices,
+ ARRAY_SIZE(bus4_i2c_devices));
+ }
+}
+#endif
+/* End of Synaptics changes for PandaBoard */
+
+static struct gpio_led gpio_leds[] = {
+ {
+ .name = "pandaboard::status1",
+ .default_trigger = "heartbeat",
+ .gpio = 7,
+ },
+ {
+ .name = "pandaboard::status2",
+ .default_trigger = "mmc0",
+ .gpio = 8,
+ },
+};
+
+static struct gpio_led_platform_data gpio_led_info = {
+ .leds = gpio_leds,
+ .num_leds = ARRAY_SIZE(gpio_leds),
+};
+
+static struct platform_device leds_gpio = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &gpio_led_info,
+ },
+};
+
+/* GPIO_KEY for the panda */
+static struct gpio_keys_button panda_gpio_keys_buttons[] = {
+ [0] = {
+ .code = KEY_HOME,
+ .gpio = 113,
+ .desc = "user_button",
+ .active_low = 1,
+ .debounce_interval = 5,
+ },
+};
+
+static struct gpio_keys_platform_data panda_gpio_keys = {
+ .buttons = panda_gpio_keys_buttons,
+ .nbuttons = ARRAY_SIZE(panda_gpio_keys_buttons),
+ .rep = 0,
+};
+
+static struct platform_device panda_gpio_keys_device = {
+ .name = "gpio-keys",
+ .id = -1,
+ .dev = {
+ .platform_data = &panda_gpio_keys,
+ },
+};
+
+/* TODO: handle suspend/resume here.
+ * Upon every suspend, make sure the wilink chip is
+ * capable enough to wake-up the OMAP host.
+ */
+static int plat_wlink_kim_suspend(struct platform_device *pdev, pm_message_t
+ state)
+{
+ return 0;
+}
+
+static int plat_wlink_kim_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+
+/* wl128x BT, FM, GPS connectivity chip */
+static struct ti_st_plat_data wilink_pdata = {
+ .nshutdown_gpio = PANDA_BT_GPIO,
+ .dev_name = WILINK_UART_DEV_NAME,
+ .flow_cntrl = 1,
+ .baud_rate = 3686400,
+ .suspend = plat_wlink_kim_suspend,
+ .resume = plat_wlink_kim_resume,
+};
+
+static struct platform_device btwilink_device = {
+ .name = "btwilink",
+ .id = -1,
+};
+
+/* wl127x BT, FM, GPS connectivity chip */
+static struct platform_device wl1271_device = {
+ .name = "kim",
+ .id = -1,
+ .dev.platform_data = &wilink_pdata,
+};
+
+
+static struct platform_device *panda_devices[] __initdata = {
+ &leds_gpio,
+ &wl1271_device,
+ &btwilink_device,
+ &panda_gpio_keys_device,
+};
+
+static void __init omap4_panda_init_early(void)
+{
+ omap2_init_common_infrastructure();
+ omap2_init_common_devices(NULL, NULL);
+}
+
+static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
+ .port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
+ .port_mode[1] = OMAP_USBHS_PORT_MODE_UNUSED,
+ .port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
+ .phy_reset = false,
+ .reset_gpio_port[0] = -EINVAL,
+ .reset_gpio_port[1] = -EINVAL,
+ .reset_gpio_port[2] = -EINVAL
+};
+
+static struct gpio panda_ehci_gpios[] __initdata = {
+ { GPIO_HUB_POWER, GPIOF_OUT_INIT_LOW, "hub_power" },
+ { GPIO_HUB_NRESET, GPIOF_OUT_INIT_LOW, "hub_nreset" },
+};
+
+static void __init omap4_ehci_init(void)
+{
+ int ret;
+ struct clk *phy_ref_clk;
+
+ /* FREF_CLK3 provides the 19.2 MHz reference clock to the PHY */
+ phy_ref_clk = clk_get(NULL, "auxclk3_ck");
+ if (IS_ERR(phy_ref_clk)) {
+ pr_err("Cannot request auxclk3\n");
+ return;
+ }
+ clk_set_rate(phy_ref_clk, 19200000);
+ clk_enable(phy_ref_clk);
+
+ /* disable the power to the usb hub prior to init and reset phy+hub */
+ ret = gpio_request_array(panda_ehci_gpios,
+ ARRAY_SIZE(panda_ehci_gpios));
+ if (ret) {
+ pr_err("Unable to initialize EHCI power/reset\n");
+ return;
+ }
+
+ gpio_export(GPIO_HUB_POWER, 0);
+ gpio_export(GPIO_HUB_NRESET, 0);
+ gpio_set_value(GPIO_HUB_NRESET, 1);
+
+ usbhs_init(&usbhs_bdata);
+
+ /* enable power to hub */
+ gpio_set_value(GPIO_HUB_POWER, 1);
+}
+
+static struct omap_musb_board_data musb_board_data = {
+ .interface_type = MUSB_INTERFACE_UTMI,
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+ .mode = MUSB_PERIPHERAL,
+#else
+ .mode = MUSB_OTG,
+#endif
+ .power = 100,
+};
+
+static struct twl4030_usb_data omap4_usbphy_data = {
+ .phy_init = omap4430_phy_init,
+ .phy_exit = omap4430_phy_exit,
+ .phy_power = omap4430_phy_power,
+ .phy_set_clock = omap4430_phy_set_clk,
+ .phy_suspend = omap4430_phy_suspend,
+};
+
+static struct omap2_hsmmc_info mmc[] = {
+ {
+ .mmc = 1,
+ .caps = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA,
+ .gpio_wp = -EINVAL,
+ .gpio_cd = -EINVAL,
+ },
+ {
+ .name = "wl1271",
+ .mmc = 5,
+ .caps = MMC_CAP_4_BIT_DATA | MMC_CAP_POWER_OFF_CARD,
+ .gpio_wp = -EINVAL,
+ .gpio_cd = -EINVAL,
+ .ocr_mask = MMC_VDD_165_195,
+ .nonremovable = true,
+ },
+ {} /* Terminator */
+};
+
+static struct regulator_consumer_supply omap4_panda_vmmc_supply[] = {
+ {
+ .supply = "vmmc",
+ .dev_name = "omap_hsmmc.0",
+ },
+};
+
+static struct regulator_consumer_supply omap4_panda_vmmc5_supply = {
+ .supply = "vmmc",
+ .dev_name = "omap_hsmmc.4",
+};
+
+static struct regulator_init_data panda_vmmc5 = {
+ .constraints = {
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = 1,
+ .consumer_supplies = &omap4_panda_vmmc5_supply,
+};
+
+static struct fixed_voltage_config panda_vwlan = {
+ .supply_name = "vwl1271",
+ .microvolts = 1800000, /* 1.8V */
+ .gpio = GPIO_WIFI_PMENA,
+ .startup_delay = 70000, /* 70msec */
+ .enable_high = 1,
+ .enabled_at_boot = 0,
+ .init_data = &panda_vmmc5,
+};
+
+static struct platform_device omap_vwlan_device = {
+ .name = "reg-fixed-voltage",
+ .id = 1,
+ .dev = {
+ .platform_data = &panda_vwlan,
+ },
+};
+
+struct wl12xx_platform_data omap_panda_wlan_data __initdata = {
+ .irq = OMAP_GPIO_IRQ(GPIO_WIFI_IRQ),
+ /* PANDA ref clock is 38.4 MHz */
+ .board_ref_clock = 2,
+};
+
+static int omap4_twl6030_hsmmc_late_init(struct device *dev)
+{
+ int ret = 0;
+ struct platform_device *pdev = container_of(dev,
+ struct platform_device, dev);
+ struct omap_mmc_platform_data *pdata = dev->platform_data;
+
+ if (!pdata) {
+ dev_err(dev, "%s: NULL platform data\n", __func__);
+ return -EINVAL;
+ }
+ /* Setting MMC1 Card detect Irq */
+ if (pdev->id == 0) {
+ ret = twl6030_mmc_card_detect_config();
+ if (ret)
+ dev_err(dev, "%s: Error card detect config(%d)\n",
+ __func__, ret);
+ else
+ pdata->slots[0].card_detect = twl6030_mmc_card_detect;
+ }
+ return ret;
+}
+
+static __init void omap4_twl6030_hsmmc_set_late_init(struct device *dev)
+{
+ struct omap_mmc_platform_data *pdata;
+
+ /* dev can be null if CONFIG_MMC_OMAP_HS is not set */
+ if (!dev) {
+ pr_err("Failed omap4_twl6030_hsmmc_set_late_init\n");
+ return;
+ }
+ pdata = dev->platform_data;
+
+ pdata->init = omap4_twl6030_hsmmc_late_init;
+}
+
+static int __init omap4_twl6030_hsmmc_init(struct omap2_hsmmc_info *controllers)
+{
+ struct omap2_hsmmc_info *c;
+
+ omap2_hsmmc_init(controllers);
+ for (c = controllers; c->mmc; c++)
+ omap4_twl6030_hsmmc_set_late_init(c->dev);
+
+ return 0;
+}
+
+static struct regulator_init_data omap4_panda_vaux2 = {
+ .constraints = {
+ .min_uV = 1200000,
+ .max_uV = 2800000,
+ .apply_uV = true,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE
+ | REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ },
+};
+
+static struct regulator_init_data omap4_panda_vaux3 = {
+ .constraints = {
+ .min_uV = 1000000,
+ .max_uV = 3000000,
+ .apply_uV = true,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE
+ | REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ },
+};
+
+/* VMMC1 for MMC1 card */
+static struct regulator_init_data omap4_panda_vmmc = {
+ .constraints = {
+ .min_uV = 1200000,
+ .max_uV = 3000000,
+ .apply_uV = true,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE
+ | REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = 1,
+ .consumer_supplies = omap4_panda_vmmc_supply,
+};
+
+static struct regulator_init_data omap4_panda_vpp = {
+ .constraints = {
+ .min_uV = 1800000,
+ .max_uV = 2500000,
+ .apply_uV = true,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE
+ | REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ },
+};
+
+static struct regulator_init_data omap4_panda_vana = {
+ .constraints = {
+ .min_uV = 2100000,
+ .max_uV = 2100000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ },
+};
+
+static struct regulator_init_data omap4_panda_vcxio = {
+ .constraints = {
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ },
+};
+
+static struct regulator_consumer_supply panda_vdac_supply[] = {
+ {
+ .supply = "hdmi_vref",
+ },
+};
+
+static struct regulator_init_data omap4_panda_vdac = {
+ .constraints = {
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(panda_vdac_supply),
+ .consumer_supplies = panda_vdac_supply,
+};
+
+static struct regulator_init_data omap4_panda_vusb = {
+ .constraints = {
+ .min_uV = 3300000,
+ .max_uV = 3300000,
+ .apply_uV = true,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ },
+};
+
+static struct regulator_init_data omap4_panda_clk32kg = {
+ .constraints = {
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ .always_on = true,
+ },
+};
+
+static void omap4_audio_conf(void)
+{
+ /* twl6040 naudint */
+ omap_mux_init_signal("sys_nirq2.sys_nirq2", \
+ OMAP_PIN_INPUT_PULLUP);
+}
+
+static struct twl4030_codec_audio_data twl6040_audio = {
+ /* single-step ramp for headset and handsfree */
+ .hs_left_step = 0x0f,
+ .hs_right_step = 0x0f,
+ .hf_left_step = 0x1d,
+ .hf_right_step = 0x1d,
+ .hs_switch_dev = 0x1,
+ .hs_forced_hs_state = 0x1
+};
+
+static struct twl4030_codec_data twl6040_codec = {
+ .audio = &twl6040_audio,
+ .audpwron_gpio = 127,
+ .naudint_irq = OMAP44XX_IRQ_SYS_2N,
+ .irq_base = TWL6040_CODEC_IRQ_BASE,
+};
+
+static struct twl4030_platform_data omap4_panda_twldata = {
+ .irq_base = TWL6030_IRQ_BASE,
+ .irq_end = TWL6030_IRQ_END,
+
+ /* Regulators */
+ .vmmc = &omap4_panda_vmmc,
+ .vpp = &omap4_panda_vpp,
+ .vana = &omap4_panda_vana,
+ .vcxio = &omap4_panda_vcxio,
+ .vdac = &omap4_panda_vdac,
+ .vusb = &omap4_panda_vusb,
+ .vaux2 = &omap4_panda_vaux2,
+ .vaux3 = &omap4_panda_vaux3,
+ .clk32kg = &omap4_panda_clk32kg,
+ .usb = &omap4_usbphy_data,
+
+ /* children */
+ .codec = &twl6040_codec,
+};
+
+/*
+ * Display monitor features are burnt in their EEPROM as EDID data. The EEPROM
+ * is connected as I2C slave device, and can be accessed at address 0x50
+ */
+static struct i2c_board_info __initdata panda_i2c_eeprom[] = {
+ {
+ I2C_BOARD_INFO("eeprom", 0x50),
+ },
+};
+
+static int __init omap4_panda_i2c_init(void)
+{
+ omap4_pmic_init("twl6030", &omap4_panda_twldata);
+ omap_register_i2c_bus(2, 400, NULL, 0);
+ /*
+ * Bus 3 is attached to the DVI port where devices like the pico DLP
+ * projector don't work reliably with 400kHz
+ */
+ omap_register_i2c_bus(3, 100, panda_i2c_eeprom,
+ ARRAY_SIZE(panda_i2c_eeprom));
+ if(ARRAY_SIZE(bus4_i2c_devices))
+ omap_register_i2c_bus(4, 400, bus4_i2c_devices, ARRAY_SIZE(bus4_i2c_devices));
+ else
+ omap_register_i2c_bus(4, 400, NULL, 0);
+ return 0;
+}
+
+#ifdef CONFIG_OMAP_MUX
+static struct omap_board_mux board_mux[] __initdata = {
+ /* WLAN IRQ - GPIO 53 */
+ OMAP4_MUX(GPMC_NCS3, OMAP_MUX_MODE3 | OMAP_PIN_INPUT),
+ /* WLAN POWER ENABLE - GPIO 43 */
+ OMAP4_MUX(GPMC_A19, OMAP_MUX_MODE3 | OMAP_PIN_OUTPUT),
+ /* WLAN SDIO: MMC5 CMD */
+ OMAP4_MUX(SDMMC5_CMD, OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP),
+ /* WLAN SDIO: MMC5 CLK */
+ OMAP4_MUX(SDMMC5_CLK, OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP),
+ /* WLAN SDIO: MMC5 DAT[0-3] */
+ OMAP4_MUX(SDMMC5_DAT0, OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP),
+ OMAP4_MUX(SDMMC5_DAT1, OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP),
+ OMAP4_MUX(SDMMC5_DAT2, OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP),
+ OMAP4_MUX(SDMMC5_DAT3, OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP),
+ /* gpio 0 - TFP410 PD */
+ OMAP4_MUX(KPD_COL1, OMAP_PIN_OUTPUT | OMAP_MUX_MODE3),
+ /* dispc2_data23 */
+ OMAP4_MUX(USBB2_ULPITLL_STP, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_data22 */
+ OMAP4_MUX(USBB2_ULPITLL_DIR, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_data21 */
+ OMAP4_MUX(USBB2_ULPITLL_NXT, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_data20 */
+ OMAP4_MUX(USBB2_ULPITLL_DAT0, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_data19 */
+ OMAP4_MUX(USBB2_ULPITLL_DAT1, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_data18 */
+ OMAP4_MUX(USBB2_ULPITLL_DAT2, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_data15 */
+ OMAP4_MUX(USBB2_ULPITLL_DAT3, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_data14 */
+ OMAP4_MUX(USBB2_ULPITLL_DAT4, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_data13 */
+ OMAP4_MUX(USBB2_ULPITLL_DAT5, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_data12 */
+ OMAP4_MUX(USBB2_ULPITLL_DAT6, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_data11 */
+ OMAP4_MUX(USBB2_ULPITLL_DAT7, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_data10 */
+ OMAP4_MUX(DPM_EMU3, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_data9 */
+ OMAP4_MUX(DPM_EMU4, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_data16 */
+ OMAP4_MUX(DPM_EMU5, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_data17 */
+ OMAP4_MUX(DPM_EMU6, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_hsync */
+ OMAP4_MUX(DPM_EMU7, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_pclk */
+ OMAP4_MUX(DPM_EMU8, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_vsync */
+ OMAP4_MUX(DPM_EMU9, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_de */
+ OMAP4_MUX(DPM_EMU10, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_data8 */
+ OMAP4_MUX(DPM_EMU11, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_data7 */
+ OMAP4_MUX(DPM_EMU12, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_data6 */
+ OMAP4_MUX(DPM_EMU13, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_data5 */
+ OMAP4_MUX(DPM_EMU14, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_data4 */
+ OMAP4_MUX(DPM_EMU15, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_data3 */
+ OMAP4_MUX(DPM_EMU16, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_data2 */
+ OMAP4_MUX(DPM_EMU17, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_data1 */
+ OMAP4_MUX(DPM_EMU18, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ /* dispc2_data0 */
+ OMAP4_MUX(DPM_EMU19, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5),
+ { .reg_offset = OMAP_MUX_TERMINATOR },
+};
+
+static inline void __init board_serial_init(void)
+{
+ omap_serial_init();
+}
+#else
+#define board_mux NULL
+
+static inline void __init board_serial_init(void)
+{
+ omap_serial_init();
+}
+#endif
+
+/* Display DVI */
+#define PANDA_DVI_TFP410_POWER_DOWN_GPIO 0
+
+static int omap4_panda_enable_dvi(struct omap_dss_device *dssdev)
+{
+ gpio_set_value(dssdev->reset_gpio, 1);
+ return 0;
+}
+
+static void omap4_panda_disable_dvi(struct omap_dss_device *dssdev)
+{
+ gpio_set_value(dssdev->reset_gpio, 0);
+}
+
+/* Using generic display panel */
+static struct panel_generic_dpi_data omap4_dvi_panel = {
+ .name = "generic_720p",
+ .platform_enable = omap4_panda_enable_dvi,
+ .platform_disable = omap4_panda_disable_dvi,
+};
+
+struct omap_dss_device omap4_panda_dvi_device = {
+ .type = OMAP_DISPLAY_TYPE_DPI,
+ .name = "dvi",
+ .driver_name = "generic_dpi_panel",
+ .data = &omap4_dvi_panel,
+ .phy.dpi.data_lines = 24,
+ .reset_gpio = PANDA_DVI_TFP410_POWER_DOWN_GPIO,
+ .channel = OMAP_DSS_CHANNEL_LCD2,
+};
+
+int __init omap4_panda_dvi_init(void)
+{
+ int r;
+
+ /* Requesting TFP410 DVI GPIO and disabling it, at bootup */
+ r = gpio_request_one(omap4_panda_dvi_device.reset_gpio,
+ GPIOF_OUT_INIT_LOW, "DVI PD");
+ if (r)
+ pr_err("Failed to get DVI powerdown GPIO\n");
+
+ return r;
+}
+
+static struct gpio panda_hdmi_gpios[] = {
+ { HDMI_GPIO_CT_CP_HPD, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_hpd" },
+ { HDMI_GPIO_LS_OE, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_ls_oe" },
+};
+
+static void omap4_panda_hdmi_mux_init(void)
+{
+ u32 r;
+ int status;
+ /* PAD0_HDMI_HPD_PAD1_HDMI_CEC */
+ omap_mux_init_signal("hdmi_hpd.hdmi_hpd",
+ OMAP_PIN_INPUT_PULLUP);
+ omap_mux_init_signal("gpmc_wait2.gpio_100",
+ OMAP_PIN_INPUT_PULLDOWN);
+ omap_mux_init_signal("hdmi_cec.hdmi_cec",
+ OMAP_PIN_INPUT_PULLUP);
+ /* PAD0_HDMI_DDC_SCL_PAD1_HDMI_DDC_SDA */
+ omap_mux_init_signal("hdmi_ddc_scl.hdmi_ddc_scl",
+ OMAP_PIN_INPUT_PULLUP);
+ omap_mux_init_signal("hdmi_ddc_sda.hdmi_ddc_sda",
+ OMAP_PIN_INPUT_PULLUP);
+
+ /* strong pullup on DDC lines using unpublished register */
+ r = ((1 << 24) | (1 << 28)) ;
+ omap4_ctrl_pad_writel(r, OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_I2C_1);
+
+ gpio_request(HDMI_GPIO_HPD, NULL);
+ omap_mux_init_gpio(HDMI_GPIO_HPD, OMAP_PIN_INPUT | OMAP_PULL_ENA);
+ gpio_direction_input(HDMI_GPIO_HPD);
+
+ status = gpio_request_array(panda_hdmi_gpios,
+ ARRAY_SIZE(panda_hdmi_gpios));
+ if (status)
+ pr_err("%s: Cannot request HDMI GPIOs %x \n", __func__, status);
+}
+
+static struct omap_dss_device omap4_panda_hdmi_device = {
+ .name = "hdmi",
+ .driver_name = "hdmi_panel",
+ .type = OMAP_DISPLAY_TYPE_HDMI,
+ .clocks = {
+ .dispc = {
+ .dispc_fclk_src = OMAP_DSS_CLK_SRC_FCK,
+ },
+ .hdmi = {
+ .regn = 15,
+ .regm2 = 1,
+ },
+ },
+ .hpd_gpio = HDMI_GPIO_HPD,
+ .channel = OMAP_DSS_CHANNEL_DIGIT,
+};
+
+static struct omap_dss_device *omap4_panda_dss_devices[] = {
+ &omap4_panda_dvi_device,
+ &omap4_panda_hdmi_device,
+};
+
+static struct omap_dss_board_info omap4_panda_dss_data = {
+ .num_devices = ARRAY_SIZE(omap4_panda_dss_devices),
+ .devices = omap4_panda_dss_devices,
+ .default_device = &omap4_panda_dvi_device,
+};
+
+/*
+ * LPDDR2 Configeration Data:
+ * The memory organisation is as below :
+ * EMIF1 - CS0 - 2 Gb
+ * CS1 - 2 Gb
+ * EMIF2 - CS0 - 2 Gb
+ * CS1 - 2 Gb
+ * --------------------
+ * TOTAL - 8 Gb
+ *
+ * Same devices installed on EMIF1 and EMIF2
+ */
+static __initdata struct emif_device_details emif_devices = {
+ .cs0_device = &lpddr2_elpida_2G_S4_dev,
+ .cs1_device = &lpddr2_elpida_2G_S4_dev
+};
+
+void omap4_panda_display_init(void)
+{
+ int r;
+
+ r = omap4_panda_dvi_init();
+ if (r)
+ pr_err("error initializing panda DVI\n");
+
+ omap4_panda_hdmi_mux_init();
+ omap_display_init(&omap4_panda_dss_data);
+}
+
+static int panda_notifier_call(struct notifier_block *this,
+ unsigned long code, void *cmd)
+{
+ void __iomem *sar_base;
+ u32 v = OMAP4430_RST_GLOBAL_COLD_SW_MASK;
+
+ sar_base = omap4_get_sar_ram_base();
+
+ if (!sar_base)
+ return notifier_from_errno(-ENOMEM);
+
+ if ((code == SYS_RESTART) && (cmd != NULL)) {
+ /* cmd != null; case: warm boot */
+ if (!strcmp(cmd, "bootloader")) {
+ /* Save reboot mode in scratch memory */
+ strcpy(sar_base + 0xA0C, cmd);
+ v |= OMAP4430_RST_GLOBAL_WARM_SW_MASK;
+ } else if (!strcmp(cmd, "recovery")) {
+ /* Save reboot mode in scratch memory */
+ strcpy(sar_base + 0xA0C, cmd);
+ v |= OMAP4430_RST_GLOBAL_WARM_SW_MASK;
+ } else {
+ v |= OMAP4430_RST_GLOBAL_COLD_SW_MASK;
+ }
+ }
+
+ omap4_prm_write_inst_reg(0xfff, OMAP4430_PRM_DEVICE_INST,
+ OMAP4_RM_RSTST);
+ omap4_prm_write_inst_reg(v, OMAP4430_PRM_DEVICE_INST, OMAP4_RM_RSTCTRL);
+ v = omap4_prm_read_inst_reg(WKUP_MOD, OMAP4_RM_RSTCTRL);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block panda_reboot_notifier = {
+ .notifier_call = panda_notifier_call,
+};
+
+#define PANDA_FB_RAM_SIZE SZ_16M /* 1920?1080*4 * 2 */
+static struct omapfb_platform_data panda_fb_pdata = {
+ .mem_desc = {
+ .region_cnt = 1,
+ .region = {
+ [0] = {
+ .size = PANDA_FB_RAM_SIZE,
+ },
+ },
+ },
+};
+
+static struct resource ramconsole_resources[] = {
+ {
+ .flags = IORESOURCE_MEM,
+ .start = PANDA_RAMCONSOLE_START,
+ .end = PANDA_RAMCONSOLE_START + PANDA_RAMCONSOLE_SIZE - 1,
+ },
+};
+
+static struct ram_console_platform_data ramconsole_pdata;
+
+static struct platform_device ramconsole_device = {
+ .name = "ram_console",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(ramconsole_resources),
+ .resource = ramconsole_resources,
+ .dev = {
+ .platform_data = &ramconsole_pdata,
+ },
+};
+
+extern void __init omap4_panda_android_init(void);
+
+static void __init omap4_panda_init(void)
+{
+ int package = OMAP_PACKAGE_CBS;
+ int status;
+
+ omap_emif_setup_device_details(&emif_devices, &emif_devices);
+
+ if (omap_rev() == OMAP4430_REV_ES1_0)
+ package = OMAP_PACKAGE_CBL;
+ omap4_mux_init(board_mux, NULL, package);
+
+ if (wl12xx_set_platform_data(&omap_panda_wlan_data))
+ pr_err("error setting wl12xx data\n");
+
+ register_reboot_notifier(&panda_reboot_notifier);
+ ramconsole_pdata.bootinfo = omap4_get_resetreason();
+ platform_device_register(&ramconsole_device);
+ omap4_panda_i2c_init();
+ omap4_audio_conf();
+
+ if (cpu_is_omap4430())
+ panda_gpio_keys_buttons[0].gpio = 121;
+
+ platform_add_devices(panda_devices, ARRAY_SIZE(panda_devices));
+ platform_device_register(&omap_vwlan_device);
+ board_serial_init();
+ omap4_twl6030_hsmmc_init(mmc);
+ omap4_ehci_init();
+ usb_musb_init(&musb_board_data);
+
+ omap_dmm_init();
+ omap_vram_set_sdram_vram(PANDA_FB_RAM_SIZE, 0);
+ omapfb_set_platform_data(&panda_fb_pdata);
+ omap4_panda_display_init();
+
+ if (cpu_is_omap446x()) {
+ /* Vsel0 = gpio, vsel1 = gnd */
+ status = omap_tps6236x_board_setup(true, TPS62361_GPIO, -1,
+ OMAP_PIN_OFF_OUTPUT_HIGH, -1);
+ if (status)
+ pr_err("TPS62361 initialization failed: %d\n", status);
+ }
+ omap_enable_smartreflex_on_init();
+}
+
+static void __init omap4_panda_map_io(void)
+{
+ omap2_set_globals_443x();
+ omap44xx_map_common_io();
+}
+
+static void __init omap4_panda_reserve(void)
+{
+ /* do the static reservations first */
+ memblock_remove(PANDA_RAMCONSOLE_START, PANDA_RAMCONSOLE_SIZE);
+ memblock_remove(PHYS_ADDR_SMC_MEM, PHYS_ADDR_SMC_SIZE);
+ memblock_remove(PHYS_ADDR_DUCATI_MEM, PHYS_ADDR_DUCATI_SIZE);
+ /* ipu needs to recognize secure input buffer area as well */
+ omap_ipu_set_static_mempool(PHYS_ADDR_DUCATI_MEM, PHYS_ADDR_DUCATI_SIZE +
+ OMAP_ION_HEAP_SECURE_INPUT_SIZE);
+
+ omap_reserve();
+}
+
+MACHINE_START(OMAP4_PANDA, "OMAP4 Panda board")
+ /* Maintainer: David Anders - Texas Instruments Inc */
+ .boot_params = 0x80000100,
+ .reserve = omap4_panda_reserve,
+ .map_io = omap4_panda_map_io,
+ .init_early = omap4_panda_init_early,
+ .init_irq = gic_init_irq,
+ .init_machine = omap4_panda_init,
+ .timer = &omap_timer,
+MACHINE_END
diff --git a/kernel/drivers/input/touchscreen/Kconfig b/kernel/drivers/input/touchscreen/Kconfig
new file mode 100644
index 000000000000..18655c0b3997
--- /dev/null
+++ b/kernel/drivers/input/touchscreen/Kconfig
@@ -0,0 +1,721 @@
+#
+# Touchscreen driver configuration
+#
+menuconfig INPUT_TOUCHSCREEN
+ bool "Touchscreens"
+ help
+ Say Y here, and a list of supported touchscreens will be displayed.
+ This option doesn't affect the kernel.
+
+ If unsure, say Y.
+
+if INPUT_TOUCHSCREEN
+
+config TOUCHSCREEN_88PM860X
+ tristate "Marvell 88PM860x touchscreen"
+ depends on MFD_88PM860X
+ help
+ Say Y here if you have a 88PM860x PMIC and want to enable
+ support for the built-in touchscreen.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called 88pm860x-ts.
+
+config TOUCHSCREEN_ADS7846
+ tristate "ADS7846/TSC2046/AD7873 and AD(S)7843 based touchscreens"
+ depends on SPI_MASTER
+ depends on HWMON = n || HWMON
+ help
+ Say Y here if you have a touchscreen interface using the
+ ADS7846/TSC2046/AD7873 or ADS7843/AD7843 controller,
+ and your board-specific setup code includes that in its
+ table of SPI devices.
+
+ If HWMON is selected, and the driver is told the reference voltage
+ on your board, you will also get hwmon interfaces for the voltage
+ (and on ads7846/tsc2046/ad7873, temperature) sensors of this chip.
+
+ If unsure, say N (but it's safe to say "Y").
+
+ To compile this driver as a module, choose M here: the
+ module will be called ads7846.
+
+config TOUCHSCREEN_AD7877
+ tristate "AD7877 based touchscreens"
+ depends on SPI_MASTER
+ help
+ Say Y here if you have a touchscreen interface using the
+ AD7877 controller, and your board-specific initialization
+ code includes that in its table of SPI devices.
+
+ If unsure, say N (but it's safe to say "Y").
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7877.
+
+config TOUCHSCREEN_AD7879
+ tristate "Analog Devices AD7879-1/AD7889-1 touchscreen interface"
+ help
+ Say Y here if you want to support a touchscreen interface using
+ the AD7879-1/AD7889-1 controller.
+
+ You should select a bus connection too.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7879.
+
+config TOUCHSCREEN_AD7879_I2C
+ tristate "support I2C bus connection"
+ depends on TOUCHSCREEN_AD7879 && I2C
+ help
+ Say Y here if you have AD7879-1/AD7889-1 hooked to an I2C bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7879-i2c.
+
+config TOUCHSCREEN_AD7879_SPI
+ tristate "support SPI bus connection"
+ depends on TOUCHSCREEN_AD7879 && SPI_MASTER
+ help
+ Say Y here if you have AD7879-1/AD7889-1 hooked to a SPI bus.
+
+ If unsure, say N (but it's safe to say "Y").
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7879-spi.
+
+config TOUCHSCREEN_BITSY
+ tristate "Compaq iPAQ H3600 (Bitsy) touchscreen"
+ depends on SA1100_BITSY
+ select SERIO
+ help
+ Say Y here if you have the h3600 (Bitsy) touchscreen.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called h3600_ts_input.
+
+config TOUCHSCREEN_BU21013
+ tristate "BU21013 based touch panel controllers"
+ depends on I2C
+ help
+ Say Y here if you have a bu21013 touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bu21013_ts.
+
+config TOUCHSCREEN_CY8CTMG110
+ tristate "cy8ctmg110 touchscreen"
+ depends on I2C
+ depends on GPIOLIB
+
+ help
+ Say Y here if you have a cy8ctmg110 capacitive touchscreen on
+ an AAVA device.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cy8ctmg110_ts.
+
+config TOUCHSCREEN_DA9034
+ tristate "Touchscreen support for Dialog Semiconductor DA9034"
+ depends on PMIC_DA903X
+ default y
+ help
+ Say Y here to enable the support for the touchscreen found
+ on Dialog Semiconductor DA9034 PMIC.
+
+config TOUCHSCREEN_DYNAPRO
+ tristate "Dynapro serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Dynapro serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called dynapro.
+
+config TOUCHSCREEN_HAMPSHIRE
+ tristate "Hampshire serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Hampshire serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called hampshire.
+
+config TOUCHSCREEN_EETI
+ tristate "EETI touchscreen panel support"
+ depends on I2C
+ help
+ Say Y here to enable support for I2C connected EETI touch panels.
+
+ To compile this driver as a module, choose M here: the
+ module will be called eeti_ts.
+
+config TOUCHSCREEN_FUJITSU
+ tristate "Fujitsu serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have the Fujitsu touchscreen (such as one
+ installed in Lifebook P series laptop) connected to your
+ system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called fujitsu-ts.
+
+config TOUCHSCREEN_S3C2410
+ tristate "Samsung S3C2410/generic touchscreen input driver"
+ depends on ARCH_S3C2410 || SAMSUNG_DEV_TS
+ select S3C_ADC
+ help
+ Say Y here if you have the s3c2410 touchscreen.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called s3c2410_ts.
+
+config TOUCHSCREEN_GUNZE
+ tristate "Gunze AHL-51S touchscreen"
+ select SERIO
+ help
+ Say Y here if you have the Gunze AHL-51 touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gunze.
+
+config TOUCHSCREEN_ELO
+ tristate "Elo serial touchscreens"
+ select SERIO
+ help
+ Say Y here if you have an Elo serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called elo.
+
+config TOUCHSCREEN_WACOM_W8001
+ tristate "Wacom W8001 penabled serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have an Wacom W8001 penabled serial touchscreen
+ connected to your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wacom_w8001.
+
+config TOUCHSCREEN_LPC32XX
+ tristate "LPC32XX touchscreen controller"
+ depends on ARCH_LPC32XX
+ help
+ Say Y here if you have a LPC32XX device and want
+ to support the built-in touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called lpc32xx_ts.
+
+config TOUCHSCREEN_MCS5000
+ tristate "MELFAS MCS-5000 touchscreen"
+ depends on I2C
+ help
+ Say Y here if you have the MELFAS MCS-5000 touchscreen controller
+ chip in your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mcs5000_ts.
+
+config TOUCHSCREEN_MTOUCH
+ tristate "MicroTouch serial touchscreens"
+ select SERIO
+ help
+ Say Y here if you have a MicroTouch (3M) serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mtouch.
+
+config TOUCHSCREEN_INEXIO
+ tristate "iNexio serial touchscreens"
+ select SERIO
+ help
+ Say Y here if you have an iNexio serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called inexio.
+
+config TOUCHSCREEN_INTEL_MID
+ tristate "Intel MID platform resistive touchscreen"
+ depends on INTEL_SCU_IPC
+ help
+ Say Y here if you have a Intel MID based touchscreen in
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called intel_mid_touch.
+
+config TOUCHSCREEN_MK712
+ tristate "ICS MicroClock MK712 touchscreen"
+ help
+ Say Y here if you have the ICS MicroClock MK712 touchscreen
+ controller chip in your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mk712.
+
+config TOUCHSCREEN_HP600
+ tristate "HP Jornada 6xx touchscreen"
+ depends on SH_HP6XX && SH_ADC
+ help
+ Say Y here if you have a HP Jornada 620/660/680/690 and want to
+ support the built-in touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called hp680_ts_input.
+
+config TOUCHSCREEN_HP7XX
+ tristate "HP Jornada 7xx touchscreen"
+ depends on SA1100_JORNADA720_SSP
+ help
+ Say Y here if you have a HP Jornada 710/720/728 and want
+ to support the built-in touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called jornada720_ts.
+
+config TOUCHSCREEN_HTCPEN
+ tristate "HTC Shift X9500 touchscreen"
+ depends on ISA
+ help
+ Say Y here if you have an HTC Shift UMPC also known as HTC X9500
+ Clio / Shangrila and want to support the built-in touchscreen.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called htcpen.
+
+config TOUCHSCREEN_PENMOUNT
+ tristate "Penmount serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Penmount serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called penmount.
+
+config TOUCHSCREEN_QT602240
+ tristate "QT602240 I2C Touchscreen"
+ depends on I2C
+ help
+ Say Y here if you have the AT42QT602240/ATMXT224 I2C touchscreen
+ connected to your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called qt602240_ts.
+
+config TOUCHSCREEN_MIGOR
+ tristate "Renesas MIGO-R touchscreen"
+ depends on SH_MIGOR && I2C
+ help
+ Say Y here to enable MIGO-R touchscreen support.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called migor_ts.
+
+config TOUCHSCREEN_TNETV107X
+ tristate "TI TNETV107X touchscreen support"
+ depends on ARCH_DAVINCI_TNETV107X
+ help
+ Say Y here if you want to use the TNETV107X touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tnetv107x-ts.
+
+config TOUCHSCREEN_SYNAPTICS_I2C_RMI4
+ tristate "Synaptics DSX I2C touchscreen"
+ depends on I2C
+ help
+ Say Y here if you have a Synaptics DSX I2C touchscreen
+ connected to your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called synaptics_i2c_rmi4.
+
+config TOUCHSCREEN_SYNAPTICS_DSX_RMI4_DEV
+ tristate "Synaptics I2C touchscreen rmi device"
+ depends on TOUCHSCREEN_SYNAPTICS_I2C_RMI4
+ help
+ This enables support for character device channel for Synaptics RMI
+ touchscreens.
+
+config TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE
+ tristate "Synaptics I2C touchscreen firmware update"
+ depends on TOUCHSCREEN_SYNAPTICS_I2C_RMI4
+ help
+ This enables support for firmware update for Synaptics RMI
+ touchscreens.
+
+config TOUCHSCREEN_TOUCHRIGHT
+ tristate "Touchright serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Touchright serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called touchright.
+
+config TOUCHSCREEN_TOUCHWIN
+ tristate "Touchwin serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Touchwin serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called touchwin.
+
+config TOUCHSCREEN_ATMEL_TSADCC
+ tristate "Atmel Touchscreen Interface"
+ depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
+ help
+ Say Y here if you have a 4-wire touchscreen connected to the
+ ADC Controller on your Atmel SoC (such as the AT91SAM9RL).
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called atmel_tsadcc.
+
+config TOUCHSCREEN_UCB1400
+ tristate "Philips UCB1400 touchscreen"
+ depends on AC97_BUS
+ depends on UCB1400_CORE
+ help
+ This enables support for the Philips UCB1400 touchscreen interface.
+ The UCB1400 is an AC97 audio codec. The touchscreen interface
+ will be initialized only after the ALSA subsystem has been
+ brought up and the UCB1400 detected. You therefore have to
+ configure ALSA support as well (either built-in or modular,
+ independently of whether this driver is itself built-in or
+ modular) for this driver to work.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ucb1400_ts.
+
+config TOUCHSCREEN_WM97XX
+ tristate "Support for WM97xx AC97 touchscreen controllers"
+ depends on AC97_BUS
+ help
+ Say Y here if you have a Wolfson Microelectronics WM97xx
+ touchscreen connected to your system. Note that this option
+ only enables core driver, you will also need to select
+ support for appropriate chip below.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wm97xx-ts.
+
+config TOUCHSCREEN_WM9705
+ bool "WM9705 Touchscreen interface support"
+ depends on TOUCHSCREEN_WM97XX
+ default y
+ help
+ Say Y here to enable support for the Wolfson Microelectronics
+ WM9705 touchscreen controller.
+
+config TOUCHSCREEN_WM9712
+ bool "WM9712 Touchscreen interface support"
+ depends on TOUCHSCREEN_WM97XX
+ default y
+ help
+ Say Y here to enable support for the Wolfson Microelectronics
+ WM9712 touchscreen controller.
+
+config TOUCHSCREEN_WM9713
+ bool "WM9713 Touchscreen interface support"
+ depends on TOUCHSCREEN_WM97XX
+ default y
+ help
+ Say Y here to enable support for the Wolfson Microelectronics
+ WM9713 touchscreen controller.
+
+config TOUCHSCREEN_WM97XX_ATMEL
+ tristate "WM97xx Atmel accelerated touch"
+ depends on TOUCHSCREEN_WM97XX && (AVR32 || ARCH_AT91)
+ help
+ Say Y here for support for streaming mode with WM97xx touchscreens
+ on Atmel AT91 or AVR32 systems with an AC97C module.
+
+ Be aware that this will use channel B in the controller for
+ streaming data, this must not conflict with other AC97C drivers.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the module will
+ be called atmel-wm97xx.
+
+config TOUCHSCREEN_WM97XX_MAINSTONE
+ tristate "WM97xx Mainstone/Palm accelerated touch"
+ depends on TOUCHSCREEN_WM97XX && ARCH_PXA
+ help
+ Say Y here for support for streaming mode with WM97xx touchscreens
+ on Mainstone, Palm Tungsten T5, TX and LifeDrive systems.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mainstone-wm97xx.
+
+config TOUCHSCREEN_WM97XX_ZYLONITE
+ tristate "Zylonite accelerated touch"
+ depends on TOUCHSCREEN_WM97XX && MACH_ZYLONITE
+ select TOUCHSCREEN_WM9713
+ help
+ Say Y here for support for streaming mode with the touchscreen
+ on Zylonite systems.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called zylonite-wm97xx.
+
+config TOUCHSCREEN_USB_COMPOSITE
+ tristate "USB Touchscreen Driver"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ USB Touchscreen driver for:
+ - eGalax Touchkit USB (also includes eTurboTouch CT-410/510/700)
+ - PanJit TouchSet USB
+ - 3M MicroTouch USB (EX II series)
+ - ITM
+ - some other eTurboTouch
+ - Gunze AHL61
+ - DMC TSC-10/25
+ - IRTOUCHSYSTEMS/UNITOP
+ - IdealTEK URTC1000
+ - GoTop Super_Q2/GogoPen/PenPower tablets
+ - JASTEC USB Touch Controller/DigiTech DTR-02U
+ - Zytronic controllers
+
+ Have a look at <http://linux.chapter7.ch/touchkit/> for
+ a usage description and the required user-space stuff.
+
+ To compile this driver as a module, choose M here: the
+ module will be called usbtouchscreen.
+
+config TOUCHSCREEN_MC13783
+ tristate "Freescale MC13783 touchscreen input driver"
+ depends on MFD_MC13783
+ help
+ Say Y here if you have an Freescale MC13783 PMIC on your
+ board and want to use its touchscreen
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mc13783_ts.
+
+config TOUCHSCREEN_USB_EGALAX
+ default y
+ bool "eGalax, eTurboTouch CT-410/510/700 device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_PANJIT
+ default y
+ bool "PanJit device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_3M
+ default y
+ bool "3M/Microtouch EX II series device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_ITM
+ default y
+ bool "ITM device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_ETURBO
+ default y
+ bool "eTurboTouch (non-eGalax compatible) device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_GUNZE
+ default y
+ bool "Gunze AHL61 device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_DMC_TSC10
+ default y
+ bool "DMC TSC-10/25 device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_IRTOUCH
+ default y
+ bool "IRTOUCHSYSTEMS/UNITOP device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_IDEALTEK
+ default y
+ bool "IdealTEK URTC1000 device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_GENERAL_TOUCH
+ default y
+ bool "GeneralTouch Touchscreen device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_GOTOP
+ default y
+ bool "GoTop Super_Q2/GogoPen/PenPower tablet device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_JASTEC
+ default y
+ bool "JASTEC/DigiTech DTR-02U USB touch controller device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_E2I
+ default y
+ bool "e2i Touchscreen controller (e.g. from Mimo 740)"
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_ZYTRONIC
+ default y
+ bool "Zytronic controller" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_ETT_TC45USB
+ default y
+ bool "ET&T USB series TC4UM/TC5UH touchscreen controler support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_NEXIO
+ default y
+ bool "NEXIO/iNexio device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_TOUCHIT213
+ tristate "Sahara TouchIT-213 touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Sahara TouchIT-213 Tablet PC.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called touchit213.
+
+config TOUCHSCREEN_TSC2007
+ tristate "TSC2007 based touchscreens"
+ depends on I2C
+ help
+ Say Y here if you have a TSC2007 based touchscreen.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tsc2007.
+
+config TOUCHSCREEN_TSC2004
+ tristate "TSC2004 based touchscreens"
+ depends on I2C
+ help
+ Say Y here if you have a TSC2004 based touchscreen.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tsc2004.
+
+config TOUCHSCREEN_W90X900
+ tristate "W90P910 touchscreen driver"
+ depends on HAVE_CLK
+ help
+ Say Y here if you have a W90P910 based touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called w90p910_ts.
+
+config TOUCHSCREEN_PCAP
+ tristate "Motorola PCAP touchscreen"
+ depends on EZX_PCAP
+ help
+ Say Y here if you have a Motorola EZX telephone and
+ want to enable support for the built-in touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pcap_ts.
+
+config TOUCHSCREEN_TPS6507X
+ tristate "TPS6507x based touchscreens"
+ depends on I2C
+ help
+ Say Y here if you have a TPS6507x based touchscreen
+ controller.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tps6507x_ts.
+
+config TOUCHSCREEN_STMPE
+ tristate "STMicroelectronics STMPE touchscreens"
+ depends on MFD_STMPE
+ help
+ Say Y here if you want support for STMicroelectronics
+ STMPE touchscreen controllers.
+
+ To compile this driver as a module, choose M here: the
+ module will be called stmpe-ts.
+
+endif
diff --git a/kernel/drivers/input/touchscreen/Makefile b/kernel/drivers/input/touchscreen/Makefile
new file mode 100644
index 000000000000..a6c7d9f388a6
--- /dev/null
+++ b/kernel/drivers/input/touchscreen/Makefile
@@ -0,0 +1,68 @@
+#
+# Makefile for the touchscreen drivers.
+#
+
+# Each configuration option enables a list of files.
+
+wm97xx-ts-y := wm97xx-core.o
+
+obj-$(CONFIG_TOUCHSCREEN_88PM860X) += 88pm860x-ts.o
+obj-$(CONFIG_TOUCHSCREEN_AD7877) += ad7877.o
+obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o
+obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C) += ad7879-i2c.o
+obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o
+obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o
+obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o
+obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o
+obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o
+obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o
+obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o
+obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o
+obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o
+obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o
+obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
+obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
+obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
+obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
+obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o
+obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
+obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
+obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
+obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o
+obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o
+obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o
+obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o
+obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
+obj-$(CONFIG_TOUCHSCREEN_QT602240) += qt602240_ts.o
+obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
+obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o
+obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += synaptics_i2c_rmi4.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI4_DEV) += synaptics_rmi_dev.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE) += synaptics_fw_update.o
+obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
+obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
+obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
+obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o
+obj-$(CONFIG_TOUCHSCREEN_TSC2004) += tsc2004.o
+obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
+obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o
+obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o
+wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o
+wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o
+wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o
+obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o
+obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o
+obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o
+obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o
+obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o
+
+all:
+make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
+
+clean:
+make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
diff --git a/kernel/drivers/input/touchscreen/msg21xx/msg21xx_ts.c b/kernel/drivers/input/touchscreen/msg21xx/msg21xx_ts.c
new file mode 100644
index 000000000000..4eb7fd4b1cc9
--- /dev/null
+++ b/kernel/drivers/input/touchscreen/msg21xx/msg21xx_ts.c
@@ -0,0 +1,1757 @@
+/*
+ * MStar MSG21XX touchscreen driver
+ *
+ * Copyright (c) 2006-2012 MStar Semiconductor, Inc.
+ *
+ * Copyright (C) 2012 Bruce Ding <bruce.ding@mstarsemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/timer.h>
+#include <linux/gpio.h>
+
+#include <linux/sysfs.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <mach/gpio.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <linux/syscalls.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+#include <linux/string.h>
+#include <asm/unistd.h>
+#include <linux/cdev.h>
+#include <asm/uaccess.h>
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#endif
+#include <linux/input.h>
+#if defined(CONFIG_FB)
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#endif
+#ifdef CONFIG_TOUCHSCREEN_PROXIMITY_SENSOR
+#include <linux/input/vir_ps.h>
+#endif
+
+/*=============================================================*/
+// Macro Definition
+/*=============================================================*/
+
+#define TOUCH_DRIVER_DEBUG 0
+#if (TOUCH_DRIVER_DEBUG == 1)
+#define DBG(fmt, arg...) pr_err(fmt, ##arg) //pr_info(fmt, ##arg)
+#else
+#define DBG(fmt, arg...)
+#endif
+
+/*=============================================================*/
+// Constant Value & Variable Definition
+/*=============================================================*/
+
+#define U8 unsigned char
+#define U16 unsigned short
+#define U32 unsigned int
+#define S8 signed char
+#define S16 signed short
+#define S32 signed int
+
+#define TOUCH_SCREEN_X_MIN (0)
+#define TOUCH_SCREEN_Y_MIN (0)
+/*
+ * Note.
+ * Please change the below touch screen resolution according to the touch panel that you are using.
+ */
+#define TOUCH_SCREEN_X_MAX (480)
+#define TOUCH_SCREEN_Y_MAX (800)
+/*
+ * Note.
+ * Please do not change the below setting.
+ */
+#define TPD_WIDTH (2048)
+#define TPD_HEIGHT (2048)
+
+/*
+ * Note.
+ * Please change the below GPIO pin setting to follow the platform that you are using
+ */
+static int int_gpio = 1;
+static int reset_gpio = 0;
+#define MS_TS_MSG21XX_GPIO_RST reset_gpio
+#define MS_TS_MSG21XX_GPIO_INT int_gpio
+//---------------------------------------------------------------------//
+
+//#define SYSFS_AUTHORITY_CHANGE_FOR_CTS_TEST
+
+#ifdef SYSFS_AUTHORITY_CHANGE_FOR_CTS_TEST
+#define SYSFS_AUTHORITY (0644)
+#else
+#define SYSFS_AUTHORITY (0777)
+#endif
+
+#define FIRMWARE_AUTOUPDATE
+#ifdef FIRMWARE_AUTOUPDATE
+typedef enum {
+ SWID_START = 1,
+ SWID_TRULY = SWID_START,
+ SWID_NULL,
+} SWID_ENUM;
+
+unsigned char MSG_FIRMWARE[1][33*1024] =
+{
+ {
+ #include "msg21xx_truly_update_bin.h"
+ }
+};
+#endif
+
+#define CONFIG_TP_HAVE_KEY
+
+/*
+ * Note.
+ * If the below virtual key value definition are not consistent with those that defined in key layout file of platform,
+ * please change the below virtual key value to follow the platform that you are using.
+ */
+#ifdef CONFIG_TP_HAVE_KEY
+#define TOUCH_KEY_MENU (139) //229
+#define TOUCH_KEY_HOME (172) //102
+#define TOUCH_KEY_BACK (158)
+#define TOUCH_KEY_SEARCH (217)
+
+const U16 tp_key_array[] = {TOUCH_KEY_MENU, TOUCH_KEY_HOME, TOUCH_KEY_BACK, TOUCH_KEY_SEARCH};
+#define MAX_KEY_NUM (sizeof(tp_key_array)/sizeof(tp_key_array[0]))
+#endif
+
+#define SLAVE_I2C_ID_DBBUS (0xC4>>1)
+#define SLAVE_I2C_ID_DWI2C (0x4C>>1)
+
+#define DEMO_MODE_PACKET_LENGTH (8)
+#define MAX_TOUCH_NUM (2) //5
+
+#define TP_PRINT
+#ifdef TP_PRINT
+static int tp_print_proc_read(void);
+static void tp_print_create_entry(void);
+#endif
+
+static char *fw_version = NULL; // customer firmware version
+static U16 fw_version_major = 0;
+static U16 fw_version_minor = 0;
+static U8 temp[94][1024];
+static U32 crc32_table[256];
+static int FwDataCnt = 0;
+static U8 bFwUpdating = 0;
+static struct class *firmware_class = NULL;
+static struct device *firmware_cmd_dev = NULL;
+
+static struct i2c_client *i2c_client = NULL;
+
+#if defined(CONFIG_FB)
+static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data);
+static struct notifier_block msg21xx_fb_notif;
+#elif defined (CONFIG_HAS_EARLYSUSPEND)
+static struct early_suspend mstar_ts_early_suspend;
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_PROXIMITY_SENSOR
+static U8 bEnableTpProximity = 0;
+static U8 bFaceClosingTp = 0;
+#endif
+static U8 bTpInSuspend = 0;
+
+static int irq_msg21xx = -1;
+static struct work_struct msg21xx_wk;
+static struct mutex msg21xx_mutex;
+static struct input_dev *input_dev = NULL;
+
+/*=============================================================*/
+// Data Type Definition
+/*=============================================================*/
+
+typedef struct
+{
+ U16 x;
+ U16 y;
+} touchPoint_t;
+
+/// max 80+1+1 = 82 bytes
+typedef struct
+{
+ touchPoint_t point[MAX_TOUCH_NUM];
+ U8 count;
+ U8 keycode;
+} touchInfo_t;
+
+enum i2c_speed
+{
+ I2C_SLOW = 0,
+ I2C_NORMAL = 1, /* Enable erasing/writing for 10 msec. */
+ I2C_FAST = 2, /* Disable EWENB before 10 msec timeout. */
+};
+
+typedef enum
+{
+ EMEM_ALL = 0,
+ EMEM_MAIN,
+ EMEM_INFO,
+} EMEM_TYPE_t;
+
+/*=============================================================*/
+// Function Definition
+/*=============================================================*/
+
+/// CRC
+static U32 _CRC_doReflect(U32 ref, S8 ch)
+{
+ U32 value = 0;
+ U32 i = 0;
+
+ for (i = 1; i < (ch + 1); i ++)
+ {
+ if (ref & 1)
+ {
+ value |= 1 << (ch - i);
+ }
+ ref >>= 1;
+ }
+
+ return value;
+}
+
+U32 _CRC_getValue(U32 text, U32 prevCRC)
+{
+ U32 ulCRC = prevCRC;
+
+ ulCRC = (ulCRC >> 8) ^ crc32_table[(ulCRC & 0xFF) ^ text];
+
+ return ulCRC;
+}
+
+static void _CRC_initTable(void)
+{
+ U32 magic_number = 0x04c11db7;
+ U32 i, j;
+
+ for (i = 0; i <= 0xFF; i ++)
+ {
+ crc32_table[i] = _CRC_doReflect (i, 8) << 24;
+ for (j = 0; j < 8; j ++)
+ {
+ crc32_table[i] = (crc32_table[i] << 1) ^ (crc32_table[i] & (0x80000000L) ? magic_number : 0);
+ }
+ crc32_table[i] = _CRC_doReflect(crc32_table[i], 32);
+ }
+}
+
+static void reset_hw(void)
+{
+ DBG("reset_hw()\n");
+
+ gpio_direction_output(MS_TS_MSG21XX_GPIO_RST, 1);
+ gpio_set_value(MS_TS_MSG21XX_GPIO_RST, 0);
+ mdelay(100); /* Note that the RST must be in LOW 10ms at least */
+ gpio_set_value(MS_TS_MSG21XX_GPIO_RST, 1);
+ mdelay(100); /* Enable the interrupt service thread/routine for INT after 50ms */
+}
+
+static int read_i2c_seq(U8 addr, U8* buf, U16 size)
+{
+ int rc = 0;
+ struct i2c_msg msgs[] =
+ {
+ {
+ .addr = addr,
+ .flags = I2C_M_RD, // read flag
+ .len = size,
+ .buf = buf,
+ },
+ };
+
+ /* If everything went ok (i.e. 1 msg transmitted), return #bytes
+ transmitted, else error code. */
+ if (i2c_client != NULL)
+ {
+ rc = i2c_transfer(i2c_client->adapter, msgs, 1);
+ if (rc < 0)
+ {
+ DBG("read_i2c_seq() error %d\n", rc);
+ }
+ }
+ else
+ {
+ DBG("i2c_client is NULL\n");
+ }
+
+ return rc;
+}
+
+static int write_i2c_seq(U8 addr, U8* buf, U16 size)
+{
+ int rc = 0;
+ struct i2c_msg msgs[] =
+ {
+ {
+ .addr = addr,
+ .flags = 0, // if read flag is undefined, then it means write flag.
+ .len = size,
+ .buf = buf,
+ },
+ };
+
+ /* If everything went ok (i.e. 1 msg transmitted), return #bytes
+ transmitted, else error code. */
+ if (i2c_client != NULL)
+ {
+ rc = i2c_transfer(i2c_client->adapter, msgs, 1);
+ if ( rc < 0 )
+ {
+ DBG("write_i2c_seq() error %d\n", rc);
+ }
+ }
+ else
+ {
+ DBG("i2c_client is NULL\n");
+ }
+
+ return rc;
+}
+
+static U16 read_reg(U8 bank, U8 addr)
+{
+ U8 tx_data[3] = {0x10, bank, addr};
+ U8 rx_data[2] = {0};
+
+ write_i2c_seq(SLAVE_I2C_ID_DBBUS, &tx_data[0], 3);
+ read_i2c_seq(SLAVE_I2C_ID_DBBUS, &rx_data[0], 2);
+
+ return (rx_data[1] << 8 | rx_data[0]);
+}
+
+static void write_reg(U8 bank, U8 addr, U16 data)
+{
+ U8 tx_data[5] = {0x10, bank, addr, data & 0xFF, data >> 8};
+ write_i2c_seq(SLAVE_I2C_ID_DBBUS, &tx_data[0], 5);
+}
+
+static void write_reg_8bit(U8 bank, U8 addr, U8 data)
+{
+ U8 tx_data[4] = {0x10, bank, addr, data};
+ write_i2c_seq(SLAVE_I2C_ID_DBBUS, &tx_data[0], 4);
+}
+
+void dbbusDWIICEnterSerialDebugMode(void)
+{
+ U8 data[5];
+
+ // Enter the Serial Debug Mode
+ data[0] = 0x53;
+ data[1] = 0x45;
+ data[2] = 0x52;
+ data[3] = 0x44;
+ data[4] = 0x42;
+
+ write_i2c_seq(SLAVE_I2C_ID_DBBUS, data, 5);
+}
+
+void dbbusDWIICStopMCU(void)
+{
+ U8 data[1];
+
+ // Stop the MCU
+ data[0] = 0x37;
+
+ write_i2c_seq(SLAVE_I2C_ID_DBBUS, data, 1);
+}
+
+void dbbusDWIICIICUseBus(void)
+{
+ U8 data[1];
+
+ // IIC Use Bus
+ data[0] = 0x35;
+
+ write_i2c_seq(SLAVE_I2C_ID_DBBUS, data, 1);
+}
+
+void dbbusDWIICIICReshape(void)
+{
+ U8 data[1];
+
+ // IIC Re-shape
+ data[0] = 0x71;
+
+ write_i2c_seq(SLAVE_I2C_ID_DBBUS, data, 1);
+}
+
+void dbbusDWIICIICNotUseBus(void)
+{
+ U8 data[1];
+
+ // IIC Not Use Bus
+ data[0] = 0x34;
+
+ write_i2c_seq(SLAVE_I2C_ID_DBBUS, data, 1);
+}
+
+void dbbusDWIICNotStopMCU(void)
+{
+ U8 data[1];
+
+ // Not Stop the MCU
+ data[0] = 0x36;
+
+ write_i2c_seq(SLAVE_I2C_ID_DBBUS, data, 1);
+}
+
+void dbbusDWIICExitSerialDebugMode(void)
+{
+ U8 data[1];
+
+ // Exit the Serial Debug Mode
+ data[0] = 0x45;
+
+ write_i2c_seq(SLAVE_I2C_ID_DBBUS, data, 1);
+
+ // Delay some interval to guard the next transaction
+ //udelay ( 200 ); // delay about 0.2ms
+}
+
+//---------------------------------------------------------------------//
+
+static U8 get_ic_type(void)
+{
+ U8 ic_type = 0;
+
+ reset_hw();
+ dbbusDWIICEnterSerialDebugMode();
+ dbbusDWIICStopMCU();
+ dbbusDWIICIICUseBus();
+ dbbusDWIICIICReshape();
+ mdelay ( 300 );
+
+ // stop mcu
+ write_reg_8bit ( 0x0F, 0xE6, 0x01 );
+ // disable watch dog
+ write_reg ( 0x3C, 0x60, 0xAA55 );
+ // get ic type
+ ic_type = (0xff)&(read_reg(0x1E, 0xCC));
+
+ if (ic_type != 1 //msg2133
+ && ic_type != 2 //msg21xxA
+ && ic_type != 3) //msg26xxM
+ {
+ ic_type = 0;
+ }
+
+ reset_hw();
+
+ return ic_type;
+}
+
+static int get_customer_firmware_version(void)
+{
+ U8 dbbus_tx_data[3] = {0};
+ U8 dbbus_rx_data[4] = {0};
+ int ret = 0;
+
+ DBG("get_customer_firmware_version()\n");
+
+ dbbus_tx_data[0] = 0x53;
+ dbbus_tx_data[1] = 0x00;
+ dbbus_tx_data[2] = 0x2A;
+ mutex_lock(&msg21xx_mutex);
+ write_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_tx_data[0], 3);
+ read_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_rx_data[0], 4);
+ mutex_unlock(&msg21xx_mutex);
+ fw_version_major = (dbbus_rx_data[1]<<8) + dbbus_rx_data[0];
+ fw_version_minor = (dbbus_rx_data[3]<<8) + dbbus_rx_data[2];
+
+ DBG("*** major = %d ***\n", fw_version_major);
+ DBG("*** minor = %d ***\n", fw_version_minor);
+
+ if (fw_version == NULL)
+ {
+ fw_version = kzalloc(sizeof(char), GFP_KERNEL);
+ }
+
+ sprintf(fw_version, "%03d%03d", fw_version_major, fw_version_minor);
+
+ return ret;
+}
+
+static int firmware_erase_c33 ( EMEM_TYPE_t emem_type )
+{
+ // stop mcu
+ write_reg ( 0x0F, 0xE6, 0x0001 );
+
+ //disable watch dog
+ write_reg_8bit ( 0x3C, 0x60, 0x55 );
+ write_reg_8bit ( 0x3C, 0x61, 0xAA );
+
+ // set PROGRAM password
+ write_reg_8bit ( 0x16, 0x1A, 0xBA );
+ write_reg_8bit ( 0x16, 0x1B, 0xAB );
+
+ write_reg_8bit ( 0x16, 0x18, 0x80 );
+
+ if ( emem_type == EMEM_ALL )
+ {
+ write_reg_8bit ( 0x16, 0x08, 0x10 ); //mark
+ }
+
+ write_reg_8bit ( 0x16, 0x18, 0x40 );
+ mdelay ( 10 );
+
+ // clear pce
+ write_reg_8bit ( 0x16, 0x18, 0x80 );
+
+ // erase trigger
+ if ( emem_type == EMEM_MAIN )
+ {
+ write_reg_8bit ( 0x16, 0x0E, 0x04 ); //erase main
+ }
+ else
+ {
+ write_reg_8bit ( 0x16, 0x0E, 0x08 ); //erase all block
+ }
+
+ return ( 1 );
+}
+
+static ssize_t firmware_update_c33 ( struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size, EMEM_TYPE_t emem_type )
+{
+ U32 i, j;
+ U32 crc_main, crc_main_tp;
+ U32 crc_info, crc_info_tp;
+ U16 reg_data = 0;
+ int update_pass = 1;
+
+ crc_main = 0xffffffff;
+ crc_info = 0xffffffff;
+
+ reset_hw();
+ dbbusDWIICEnterSerialDebugMode();
+ dbbusDWIICStopMCU();
+ dbbusDWIICIICUseBus();
+ dbbusDWIICIICReshape();
+ mdelay ( 300 );
+
+ //erase main
+ firmware_erase_c33 ( EMEM_MAIN );
+ mdelay ( 1000 );
+
+ reset_hw();
+ dbbusDWIICEnterSerialDebugMode();
+ dbbusDWIICStopMCU();
+ dbbusDWIICIICUseBus();
+ dbbusDWIICIICReshape();
+ mdelay ( 300 );
+
+ /////////////////////////
+ // Program
+ /////////////////////////
+
+ //polling 0x3CE4 is 0x1C70
+ if ( ( emem_type == EMEM_ALL ) || ( emem_type == EMEM_MAIN ) )
+ {
+ do
+ {
+ reg_data = read_reg ( 0x3C, 0xE4 );
+ }
+ while ( reg_data != 0x1C70 );
+ }
+
+ switch ( emem_type )
+ {
+ case EMEM_ALL:
+ write_reg ( 0x3C, 0xE4, 0xE38F ); // for all-blocks
+ break;
+ case EMEM_MAIN:
+ write_reg ( 0x3C, 0xE4, 0x7731 ); // for main block
+ break;
+ case EMEM_INFO:
+ write_reg ( 0x3C, 0xE4, 0x7731 ); // for info block
+
+ write_reg_8bit ( 0x0F, 0xE6, 0x01 );
+
+ write_reg_8bit ( 0x3C, 0xE4, 0xC5 );
+ write_reg_8bit ( 0x3C, 0xE5, 0x78 );
+
+ write_reg_8bit ( 0x1E, 0x04, 0x9F );
+ write_reg_8bit ( 0x1E, 0x05, 0x82 );
+
+ write_reg_8bit ( 0x0F, 0xE6, 0x00 );
+ mdelay ( 100 );
+ break;
+ }
+
+ // polling 0x3CE4 is 0x2F43
+ do
+ {
+ reg_data = read_reg ( 0x3C, 0xE4 );
+ }
+ while ( reg_data != 0x2F43 );
+
+ // calculate CRC 32
+ _CRC_initTable ();
+
+ for ( i = 0; i < 32; i++ ) // total 32 KB : 2 byte per R/W
+ {
+ if ( i == 31 )
+ {
+ temp[i][1014] = 0x5A;
+ temp[i][1015] = 0xA5;
+
+ for ( j = 0; j < 1016; j++ )
+ {
+ crc_main = _CRC_getValue ( temp[i][j], crc_main);
+ }
+ }
+ else
+ {
+ for ( j = 0; j < 1024; j++ )
+ {
+ crc_main = _CRC_getValue ( temp[i][j], crc_main);
+ }
+ }
+
+ //write_i2c_seq(SLAVE_I2C_ID_DWI2C, temp[i], 1024);
+ for (j = 0; j < 8; j++)
+ {
+ write_i2c_seq(SLAVE_I2C_ID_DWI2C, &temp[i][j*128], 128 );
+ }
+ msleep (100);
+
+ // polling 0x3CE4 is 0xD0BC
+ do
+ {
+ reg_data = read_reg ( 0x3C, 0xE4 );
+ }
+ while ( reg_data != 0xD0BC );
+
+ write_reg ( 0x3C, 0xE4, 0x2F43 );
+ }
+
+ if ( ( emem_type == EMEM_ALL ) || ( emem_type == EMEM_MAIN ) )
+ {
+ // write file done and check crc
+ write_reg ( 0x3C, 0xE4, 0x1380 );
+ }
+ mdelay ( 10 );
+
+ if ( ( emem_type == EMEM_ALL ) || ( emem_type == EMEM_MAIN ) )
+ {
+ // polling 0x3CE4 is 0x9432
+ do
+ {
+ reg_data = read_reg ( 0x3C, 0xE4 );
+ }while ( reg_data != 0x9432 );
+ }
+
+ crc_main = crc_main ^ 0xffffffff;
+ crc_info = crc_info ^ 0xffffffff;
+
+ if ( ( emem_type == EMEM_ALL ) || ( emem_type == EMEM_MAIN ) )
+ {
+ // CRC Main from TP
+ crc_main_tp = read_reg ( 0x3C, 0x80 );
+ crc_main_tp = ( crc_main_tp << 16 ) | read_reg ( 0x3C, 0x82 );
+
+ // CRC Info from TP
+ crc_info_tp = read_reg ( 0x3C, 0xA0 );
+ crc_info_tp = ( crc_info_tp << 16 ) | read_reg ( 0x3C, 0xA2 );
+ }
+
+ update_pass = 1;
+ if ( ( emem_type == EMEM_ALL ) || ( emem_type == EMEM_MAIN ) )
+ {
+ if ( crc_main_tp != crc_main )
+ update_pass = 0;
+
+ /*
+ if ( crc_info_tp != crc_info )
+ update_pass = 0;
+ */
+ }
+
+ if ( !update_pass )
+ {
+ DBG( "update_C33 failed\n" );
+ reset_hw();
+ FwDataCnt = 0;
+ return 0;
+ }
+
+ DBG( "update_C33 OK\n" );
+ reset_hw();
+ FwDataCnt = 0;
+ return size;
+}
+
+#ifdef FIRMWARE_AUTOUPDATE
+unsigned short main_sw_id = 0x7FF, info_sw_id = 0x7FF;
+U32 bin_conf_crc32 = 0;
+
+static U32 _CalMainCRC32(void)
+{
+ U32 ret=0;
+ U16 reg_data=0;
+
+ reset_hw();
+
+ dbbusDWIICEnterSerialDebugMode();
+ dbbusDWIICStopMCU();
+ dbbusDWIICIICUseBus();
+ dbbusDWIICIICReshape();
+ msleep ( 100 );
+
+ //Stop MCU
+ write_reg ( 0x0F, 0xE6, 0x0001 );
+
+ // Stop Watchdog
+ write_reg_8bit ( 0x3C, 0x60, 0x55 );
+ write_reg_8bit ( 0x3C, 0x61, 0xAA );
+
+ //cmd
+ write_reg ( 0x3C, 0xE4, 0xDF4C );
+ write_reg ( 0x1E, 0x04, 0x7d60 );
+ // TP SW reset
+ write_reg ( 0x1E, 0x04, 0x829F );
+
+ //MCU run
+ write_reg ( 0x0F, 0xE6, 0x0000 );
+
+ //polling 0x3CE4
+ do
+ {
+ reg_data = read_reg ( 0x3C, 0xE4 );
+ }while ( reg_data != 0x9432 );
+
+ // Cal CRC Main from TP
+ ret = read_reg ( 0x3C, 0x80 );
+ ret = ( ret << 16 ) | read_reg ( 0x3C, 0x82 );
+
+ DBG("[21xxA]:Current main crc32=0x%x\n",ret);
+ return (ret);
+}
+
+static void _ReadBinConfig ( void )
+{
+ U8 dbbus_tx_data[5]={0};
+ U8 dbbus_rx_data[4]={0};
+ U16 reg_data=0;
+
+ reset_hw();
+
+ dbbusDWIICEnterSerialDebugMode();
+ dbbusDWIICStopMCU();
+ dbbusDWIICIICUseBus();
+ dbbusDWIICIICReshape();
+ msleep ( 100 );
+
+ //Stop MCU
+ write_reg ( 0x0F, 0xE6, 0x0001 );
+
+ // Stop Watchdog
+ write_reg_8bit ( 0x3C, 0x60, 0x55 );
+ write_reg_8bit ( 0x3C, 0x61, 0xAA );
+
+ //cmd
+ write_reg ( 0x3C, 0xE4, 0xA4AB );
+ write_reg ( 0x1E, 0x04, 0x7d60 );
+
+ // TP SW reset
+ write_reg ( 0x1E, 0x04, 0x829F );
+
+ //MCU run
+ write_reg ( 0x0F, 0xE6, 0x0000 );
+
+ //polling 0x3CE4
+ do
+ {
+ reg_data = read_reg ( 0x3C, 0xE4 );
+ }
+ while ( reg_data != 0x5B58 );
+
+ dbbus_tx_data[0] = 0x72;
+ dbbus_tx_data[1] = 0x7F;
+ dbbus_tx_data[2] = 0x55;
+ dbbus_tx_data[3] = 0x00;
+ dbbus_tx_data[4] = 0x04;
+ write_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_tx_data[0], 5 );
+ read_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_rx_data[0], 4 );
+ if ((dbbus_rx_data[0]>=0x30 && dbbus_rx_data[0]<=0x39)
+ &&(dbbus_rx_data[1]>=0x30 && dbbus_rx_data[1]<=0x39)
+ &&(dbbus_rx_data[2]>=0x31 && dbbus_rx_data[2]<=0x39))
+ {
+ main_sw_id = (dbbus_rx_data[0]-0x30)*100+(dbbus_rx_data[1]-0x30)*10+(dbbus_rx_data[2]-0x30);
+ }
+
+ dbbus_tx_data[0] = 0x72;
+ dbbus_tx_data[1] = 0x7F;
+ dbbus_tx_data[2] = 0xFC;
+ dbbus_tx_data[3] = 0x00;
+ dbbus_tx_data[4] = 0x04;
+ write_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_tx_data[0], 5 );
+ read_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_rx_data[0], 4 );
+ bin_conf_crc32 = dbbus_rx_data[0];
+ bin_conf_crc32 = (bin_conf_crc32<<8)|dbbus_rx_data[1];
+ bin_conf_crc32 = (bin_conf_crc32<<8)|dbbus_rx_data[2];
+ bin_conf_crc32 = (bin_conf_crc32<<8)|dbbus_rx_data[3];
+
+ dbbus_tx_data[0] = 0x72;
+ dbbus_tx_data[1] = 0x83;
+ dbbus_tx_data[2] = 0x00;
+ dbbus_tx_data[3] = 0x00;
+ dbbus_tx_data[4] = 0x04;
+ write_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_tx_data[0], 5 );
+ read_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_rx_data[0], 4 );
+ if ((dbbus_rx_data[0]>=0x30 && dbbus_rx_data[0]<=0x39)
+ &&(dbbus_rx_data[1]>=0x30 && dbbus_rx_data[1]<=0x39)
+ &&(dbbus_rx_data[2]>=0x31 && dbbus_rx_data[2]<=0x39))
+ {
+ info_sw_id = (dbbus_rx_data[0]-0x30)*100+(dbbus_rx_data[1]-0x30)*10+(dbbus_rx_data[2]-0x30);
+ }
+
+ DBG("[21xxA]:main_sw_id = %d, info_sw_id = %d, bin_conf_crc32=0x%x\n", main_sw_id, info_sw_id, bin_conf_crc32);
+}
+
+static int fwAutoUpdate(void *unused)
+{
+ int time = 0;
+ ssize_t ret = 0;
+
+ for (time = 0; time < 5; time++)
+ {
+ DBG("fwAutoUpdate time = %d\n",time);
+ ret = firmware_update_c33(NULL, NULL, NULL, 1, EMEM_MAIN);
+ if (ret == 1)
+ {
+ DBG("AUTO_UPDATE OK!!!");
+ break;
+ }
+ }
+ if (time == 5)
+ {
+ DBG("AUTO_UPDATE failed!!!");
+ }
+ enable_irq(irq_msg21xx);
+ return 0;
+}
+#endif
+
+//------------------------------------------------------------------------------//
+static ssize_t firmware_update_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ DBG("*** firmware_update_show() fw_version = %s ***\n", fw_version);
+
+ return sprintf(buf, "%s\n", fw_version);
+}
+
+static ssize_t firmware_update_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ bFwUpdating = 1;
+ disable_irq(irq_msg21xx);
+
+ DBG("*** update fw size = %d ***\n", FwDataCnt);
+ size = firmware_update_c33 (dev, attr, buf, size, EMEM_MAIN);
+
+ enable_irq(irq_msg21xx);
+ bFwUpdating = 0;
+
+ return size;
+}
+
+static DEVICE_ATTR(update, SYSFS_AUTHORITY, firmware_update_show, firmware_update_store);
+
+static ssize_t firmware_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ DBG("*** firmware_version_show() fw_version = %s ***\n", fw_version);
+
+ return sprintf(buf, "%s\n", fw_version);
+}
+
+static ssize_t firmware_version_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ get_customer_firmware_version();
+
+ DBG("*** firmware_version_store() fw_version = %s ***\n", fw_version);
+
+ return size;
+}
+
+static DEVICE_ATTR(version, SYSFS_AUTHORITY, firmware_version_show, firmware_version_store);
+
+static ssize_t firmware_data_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ DBG("*** firmware_data_show() FwDataCnt = %d ***\n", FwDataCnt);
+
+ return FwDataCnt;
+}
+
+static ssize_t firmware_data_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int count = size / 1024;
+ int i;
+
+ for (i = 0; i < count; i ++)
+ {
+ memcpy(temp[FwDataCnt], buf+(i*1024), 1024);
+
+ FwDataCnt ++;
+ }
+
+ DBG("***FwDataCnt = %d ***\n", FwDataCnt);
+
+ if (buf != NULL)
+ {
+ DBG("*** buf[0] = %c ***\n", buf[0]);
+ }
+
+ return size;
+}
+
+static DEVICE_ATTR(data, SYSFS_AUTHORITY, firmware_data_show, firmware_data_store);
+
+#ifdef TP_PRINT
+static ssize_t tp_print_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ tp_print_proc_read();
+
+ return sprintf(buf, "%d\n", bTpInSuspend);
+}
+
+static ssize_t tp_print_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ DBG("*** tp_print_store() ***\n");
+
+ return size;
+}
+
+static DEVICE_ATTR(tpp, SYSFS_AUTHORITY, tp_print_show, tp_print_store);
+#endif
+
+//------------------------------------------------------------------------------//
+#ifdef CONFIG_TOUCHSCREEN_PROXIMITY_SENSOR
+static void _msg_enable_proximity(void)
+{
+ U8 tx_data[4] = {0};
+
+ DBG("_msg_enable_proximity!");
+ tx_data[0] = 0x52;
+ tx_data[1] = 0x00;
+ tx_data[2] = 0x47;
+ tx_data[3] = 0xa0;
+ mutex_lock(&msg21xx_mutex);
+ write_i2c_seq(SLAVE_I2C_ID_DWI2C, &tx_data[0], 4);
+ mutex_unlock(&msg21xx_mutex);
+
+ bEnableTpProximity = 1;
+}
+
+static void _msg_disable_proximity(void)
+{
+ U8 tx_data[4] = {0};
+
+ DBG("_msg_disable_proximity!");
+ tx_data[0] = 0x52;
+ tx_data[1] = 0x00;
+ tx_data[2] = 0x47;
+ tx_data[3] = 0xa1;
+ mutex_lock(&msg21xx_mutex);
+ write_i2c_seq(SLAVE_I2C_ID_DWI2C, &tx_data[0], 4);
+ mutex_unlock(&msg21xx_mutex);
+
+ bEnableTpProximity = 0;
+ bFaceClosingTp = 0;
+}
+
+void tsps_msg21xx_enable(int en)
+{
+ if (en)
+ {
+ _msg_enable_proximity();
+ }
+ else
+ {
+ _msg_disable_proximity();
+ }
+}
+
+int tsps_msg21xx_data(void)
+{
+ return bFaceClosingTp;
+}
+#endif
+
+static U8 calculate_checksum(U8 *msg, S32 length)
+{
+ S32 Checksum = 0;
+ S32 i;
+
+ for (i = 0; i < length; i ++)
+ {
+ Checksum += msg[i];
+ }
+
+ return (U8)((-Checksum) & 0xFF);
+}
+
+static S32 parse_info(touchInfo_t *info)
+{
+ U8 data[DEMO_MODE_PACKET_LENGTH] = {0};
+ U8 checksum = 0;
+ U32 x = 0, y = 0;
+ U32 x2 = 0, y2 = 0;
+ U32 delta_x = 0, delta_y = 0;
+
+ mutex_lock(&msg21xx_mutex);
+ read_i2c_seq(SLAVE_I2C_ID_DWI2C, &data[0], DEMO_MODE_PACKET_LENGTH);
+ mutex_unlock(&msg21xx_mutex);
+ checksum = calculate_checksum(&data[0], (DEMO_MODE_PACKET_LENGTH-1));
+ DBG("check sum: [%x] == [%x]? \n", data[DEMO_MODE_PACKET_LENGTH-1], checksum);
+
+ if(data[DEMO_MODE_PACKET_LENGTH-1] != checksum)
+ {
+ DBG("WRONG CHECKSUM\n");
+ return -1;
+ }
+
+ if(data[0] != 0x52)
+ {
+ DBG("WRONG HEADER\n");
+ return -1;
+ }
+
+ info->keycode = 0xFF;
+ if ((data[1] == 0xFF) && (data[2] == 0xFF) && (data[3] == 0xFF) && (data[4] == 0xFF) && (data[6] == 0xFF))
+ {
+ if ((data[5] == 0xFF) || (data[5] == 0))
+ {
+ info->keycode = 0xFF;
+ }
+ else if ((data[5] == 1) || (data[5] == 2) || (data[5] == 4) || (data[5] == 8))
+ {
+ if (data[5] == 1)
+ {
+ info->keycode = 0;
+ }
+ else if (data[5] == 2)
+ {
+ info->keycode = 1;
+ }
+ else if (data[5] == 4)
+ {
+ info->keycode = 2;
+ }
+ else if (data[5] == 8)
+ {
+ info->keycode = 3;
+ }
+ }
+ #ifdef CONFIG_TOUCHSCREEN_PROXIMITY_SENSOR
+ else if (bEnableTpProximity &&((data[5] == 0x80) || (data[5] == 0x40)))
+ {
+ if (data[5] == 0x80)
+ {
+ bFaceClosingTp = 1;
+ }
+ else if (data[5] == 0x40)
+ {
+ bFaceClosingTp = 0;
+ }
+ DBG("bEnableTpProximity=%d; bFaceClosingTp=%d; data[5]=%x;\n", bEnableTpProximity, bFaceClosingTp, data[5]);
+ return -1;
+ }
+ #endif
+ else
+ {
+ DBG("WRONG KEY\n");
+ return -1;
+ }
+ }
+ else
+ {
+ x = (((data[1] & 0xF0 ) << 4) | data[2]);
+ y = ((( data[1] & 0x0F) << 8) | data[3]);
+ delta_x = (((data[4] & 0xF0) << 4 ) | data[5]);
+ delta_y = (((data[4] & 0x0F) << 8 ) | data[6]);
+
+ if ((delta_x == 0) && (delta_y == 0))
+ {
+ info->point[0].x = x * TOUCH_SCREEN_X_MAX / TPD_WIDTH;
+ info->point[0].y = y * TOUCH_SCREEN_Y_MAX/ TPD_HEIGHT;
+ info->count = 1;
+ }
+ else
+ {
+ if (delta_x > 2048)
+ {
+ delta_x -= 4096;
+ }
+ if (delta_y > 2048)
+ {
+ delta_y -= 4096;
+ }
+ x2 = (U32)((S16)x + (S16)delta_x);
+ y2 = (U32)((S16)y + (S16)delta_y);
+ info->point[0].x = x * TOUCH_SCREEN_X_MAX / TPD_WIDTH;
+ info->point[0].y = y * TOUCH_SCREEN_Y_MAX/ TPD_HEIGHT;
+ info->point[1].x = x2 * TOUCH_SCREEN_X_MAX / TPD_WIDTH;
+ info->point[1].y = y2 * TOUCH_SCREEN_Y_MAX/ TPD_HEIGHT;
+ info->count = 2;
+ }
+ }
+
+ return 0;
+}
+
+static void touch_driver_touch_pressed(int x, int y)
+{
+ DBG("point touch pressed");
+
+ input_report_key(input_dev, BTN_TOUCH, 1);
+ input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, 1);
+ input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+ input_mt_sync(input_dev);
+}
+
+static void touch_driver_touch_released(void)
+{
+ DBG("point touch released");
+
+ input_report_key(input_dev, BTN_TOUCH, 0);
+ input_mt_sync(input_dev);
+}
+
+/* read data through I2C then report data to input sub-system when interrupt occurred */
+void touch_driver_do_work(struct work_struct *work)
+{
+ touchInfo_t info;
+ int i = 0;
+ static int last_keycode = 0xFF;
+ static int last_count = 0;
+
+ DBG("touch_driver_do_work()\n");
+
+ memset(&info, 0x0, sizeof(info));
+ if (0 == parse_info(&info))
+ {
+ #ifdef CONFIG_TP_HAVE_KEY
+ if (info.keycode != 0xFF) //key touch pressed
+ {
+ DBG("touch_driver_do_work() info.keycode=%x, last_keycode=%x, tp_key_array[%d]=%d\n", info.keycode, last_keycode, info.keycode, tp_key_array[info.keycode]);
+ if (info.keycode < MAX_KEY_NUM)
+ {
+ if (info.keycode != last_keycode)
+ {
+ DBG("key touch pressed");
+
+ input_report_key(input_dev, BTN_TOUCH, 1);
+ input_report_key(input_dev, tp_key_array[info.keycode], 1);
+
+ last_keycode = info.keycode;
+ }
+ else
+ {
+ /// pass duplicate key-pressing
+ DBG("REPEATED KEY\n");
+ }
+ }
+ else
+ {
+ DBG("WRONG KEY\n");
+ }
+ }
+ else //key touch released
+ {
+ if (last_keycode != 0xFF)
+ {
+ DBG("key touch released");
+
+ input_report_key(input_dev, BTN_TOUCH, 0);
+ input_report_key(input_dev, tp_key_array[last_keycode], 0);
+
+ last_keycode = 0xFF;
+ }
+ }
+ #endif //CONFIG_TP_HAVE_KEY
+
+ if (info.count > 0) //point touch pressed
+ {
+ for (i = 0; i < info.count; i ++)
+ {
+ touch_driver_touch_pressed(info.point[i].x, info.point[i].y);
+ }
+ last_count = info.count;
+ }
+ else if (last_count > 0) //point touch released
+ {
+ touch_driver_touch_released();
+ last_count = 0;
+ }
+
+ input_sync(input_dev);
+ }
+
+ enable_irq(irq_msg21xx);
+}
+
+/* The interrupt service routine will be triggered when interrupt occurred */
+irqreturn_t touch_driver_isr(int irq, void *dev_id)
+{
+ DBG("touch_driver_isr()\n");
+
+ disable_irq_nosync(irq_msg21xx);
+ schedule_work(&msg21xx_wk);
+
+ return IRQ_HANDLED;
+}
+
+#if defined(CONFIG_FB)
+static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data)
+{
+ struct fb_event *evdata = data;
+ int *blank;
+
+ if (evdata && evdata->data && event == FB_EVENT_BLANK )
+ {
+ blank = evdata->data;
+ if (*blank == FB_BLANK_UNBLANK)
+ {
+ if (bTpInSuspend)
+ {
+ gpio_direction_output(MS_TS_MSG21XX_GPIO_RST, 1);
+ mdelay(10);
+ gpio_set_value(MS_TS_MSG21XX_GPIO_RST, 0);
+ mdelay(10);
+ gpio_set_value(MS_TS_MSG21XX_GPIO_RST, 1);
+ mdelay(200);
+
+ touch_driver_touch_released();
+ input_sync(input_dev);
+
+ enable_irq(irq_msg21xx);
+ }
+ bTpInSuspend = 0;
+ }
+ else if (*blank == FB_BLANK_POWERDOWN)
+ {
+ if (bFwUpdating)
+ {
+ DBG("suspend bFwUpdating=%d\n", bFwUpdating);
+ return 0;
+ }
+
+ #ifdef CONFIG_TOUCHSCREEN_PROXIMITY_SENSOR
+ if (bEnableTpProximity)
+ {
+ DBG("suspend bEnableTpProximity=%d\n", bEnableTpProximity);
+ return 0;
+ }
+ #endif
+
+ if (bTpInSuspend == 0)
+ {
+ disable_irq(irq_msg21xx);
+ gpio_set_value(MS_TS_MSG21XX_GPIO_RST, 0);
+ }
+ bTpInSuspend = 1;
+ }
+ }
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+void touch_driver_early_suspend(struct early_suspend *p)
+{
+ DBG("touch_driver_early_suspend()\n");
+
+ if (bFwUpdating)
+ {
+ DBG("suspend bFwUpdating=%d\n", bFwUpdating);
+ return;
+ }
+
+#ifdef CONFIG_TOUCHSCREEN_PROXIMITY_SENSOR
+ if (bEnableTpProximity)
+ {
+ DBG("suspend bEnableTpProximity=%d\n", bEnableTpProximity);
+ return;
+ }
+#endif
+
+ if (bTpInSuspend == 0)
+ {
+ disable_irq(irq_msg21xx);
+ gpio_set_value(MS_TS_MSG21XX_GPIO_RST, 0);
+ }
+ bTpInSuspend = 1;
+}
+
+void touch_driver_early_resume(struct early_suspend *p)
+{
+ DBG("touch_driver_early_resume() bTpInSuspend=%d\n", bTpInSuspend);
+
+ if (bTpInSuspend)
+ {
+ gpio_direction_output(MS_TS_MSG21XX_GPIO_RST, 1);
+ mdelay(10);
+ gpio_set_value(MS_TS_MSG21XX_GPIO_RST, 0);
+ mdelay(10);
+ gpio_set_value(MS_TS_MSG21XX_GPIO_RST, 1);
+ mdelay(200);
+
+ touch_driver_touch_released();
+ input_sync(input_dev);
+
+ enable_irq(irq_msg21xx);
+ }
+ bTpInSuspend = 0;
+}
+#endif
+
+/* probe function is used for matching and initializing input device */
+static int touch_driver_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+#ifdef FIRMWARE_AUTOUPDATE
+ unsigned short update_bin_major = 0, update_bin_minor = 0;
+ int i, update_flag = 0;
+#endif
+ int ret = 0;
+
+ if (input_dev != NULL)
+ {
+ DBG("input device has found\n");
+ return -1;
+ }
+
+ DBG("*** %s ***\n", __FUNCTION__);
+
+ i2c_client = client;
+
+ ret = gpio_request(MS_TS_MSG21XX_GPIO_RST, "reset");
+ if (ret < 0)
+ {
+ pr_err("*** Failed to request GPIO %d, error %d ***\n", MS_TS_MSG21XX_GPIO_RST, ret);
+ goto err0;
+ }
+
+ // power on TP
+ gpio_direction_output(MS_TS_MSG21XX_GPIO_RST, 1);
+ mdelay(100);
+ gpio_set_value(MS_TS_MSG21XX_GPIO_RST, 0);
+ mdelay(10);
+ gpio_set_value(MS_TS_MSG21XX_GPIO_RST, 1);
+ mdelay(200);
+ if (0 == get_ic_type())
+ {
+ pr_err("the currnet ic is not Mstar\n");
+ ret = -1;
+ goto err0;
+ }
+
+ mutex_init(&msg21xx_mutex);
+
+ /* allocate an input device */
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ {
+ ret = -ENOMEM;
+ pr_err("*** input device allocation failed ***\n");
+ goto err1;
+ }
+
+ input_dev->name = client->name;
+ input_dev->phys = "I2C";
+ input_dev->dev.parent = &client->dev;
+ input_dev->id.bustype = BUS_I2C;
+
+ /* set the supported event type for input device */
+ set_bit(EV_ABS, input_dev->evbit);
+ set_bit(EV_SYN, input_dev->evbit);
+ set_bit(EV_KEY, input_dev->evbit);
+ set_bit(BTN_TOUCH, input_dev->keybit);
+ set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+#ifdef CONFIG_TP_HAVE_KEY
+ {
+ int i;
+ for (i = 0; i < MAX_KEY_NUM; i ++)
+ {
+ input_set_capability(input_dev, EV_KEY, tp_key_array[i]);
+ }
+ }
+#endif
+
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 2, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, TOUCH_SCREEN_X_MIN, TOUCH_SCREEN_X_MAX, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, TOUCH_SCREEN_Y_MIN, TOUCH_SCREEN_Y_MAX, 0, 0);
+
+ /* register the input device to input sub-system */
+ ret = input_register_device(input_dev);
+ if (ret < 0)
+ {
+ pr_err("*** Unable to register ms-touchscreen input device ***\n");
+ goto err1;
+ }
+
+ /* set sysfs for firmware */
+ firmware_class = class_create(THIS_MODULE, "ms-touchscreen-msg20xx"); //client->name
+ if (IS_ERR(firmware_class))
+ pr_err("Failed to create class(firmware)!\n");
+
+ firmware_cmd_dev = device_create(firmware_class, NULL, 0, NULL, "device");
+ if (IS_ERR(firmware_cmd_dev))
+ pr_err("Failed to create device(firmware_cmd_dev)!\n");
+
+ // version
+ if (device_create_file(firmware_cmd_dev, &dev_attr_version) < 0)
+ pr_err("Failed to create device file(%s)!\n", dev_attr_version.attr.name);
+ // update
+ if (device_create_file(firmware_cmd_dev, &dev_attr_update) < 0)
+ pr_err("Failed to create device file(%s)!\n", dev_attr_update.attr.name);
+ // data
+ if (device_create_file(firmware_cmd_dev, &dev_attr_data) < 0)
+ pr_err("Failed to create device file(%s)!\n", dev_attr_data.attr.name);
+
+#ifdef TP_PRINT
+ tp_print_create_entry();
+#endif
+
+ dev_set_drvdata(firmware_cmd_dev, NULL);
+
+ /* initialize the work queue */
+ INIT_WORK(&msg21xx_wk, touch_driver_do_work);
+
+ ret = gpio_request(MS_TS_MSG21XX_GPIO_INT, "interrupt");
+ if (ret < 0)
+ {
+ pr_err("*** Failed to request GPIO %d, error %d ***\n", MS_TS_MSG21XX_GPIO_INT, ret);
+ goto err2;
+ }
+ gpio_direction_input(MS_TS_MSG21XX_GPIO_INT);
+ gpio_set_value(MS_TS_MSG21XX_GPIO_INT, 1);
+
+ irq_msg21xx = gpio_to_irq(MS_TS_MSG21XX_GPIO_INT);
+
+ /* request an irq and register the isr */
+ ret = request_irq(irq_msg21xx, touch_driver_isr, IRQF_TRIGGER_RISING, "msg21xx", NULL);
+ if (ret != 0)
+ {
+ pr_err("*** Unable to claim irq %d; error %d ***\n", MS_TS_MSG21XX_GPIO_INT, ret);
+ goto err3;
+ }
+
+ disable_irq(irq_msg21xx);
+
+#if defined(CONFIG_FB)
+ msg21xx_fb_notif.notifier_call = fb_notifier_callback;
+ ret = fb_register_client(&msg21xx_fb_notif);
+#elif defined (CONFIG_HAS_EARLYSUSPEND)
+ mstar_ts_early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;
+ mstar_ts_early_suspend.suspend = touch_driver_early_suspend;
+ mstar_ts_early_suspend.resume = touch_driver_early_resume;
+ register_early_suspend(&mstar_ts_early_suspend);
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_PROXIMITY_SENSOR
+ tsps_assist_register_callback("msg21xx", &tsps_msg21xx_enable, &tsps_msg21xx_data);
+#endif
+
+#ifdef FIRMWARE_AUTOUPDATE
+ get_customer_firmware_version();
+ _ReadBinConfig();
+
+ if (main_sw_id == info_sw_id)
+ {
+ if (_CalMainCRC32() == bin_conf_crc32)
+ {
+ if ((main_sw_id >= SWID_START) && (main_sw_id < SWID_NULL))
+ {
+ update_bin_major= (MSG_FIRMWARE[main_sw_id-SWID_START][0x7f4f] << 8) + MSG_FIRMWARE[main_sw_id-SWID_START][0x7f4e];
+ update_bin_minor= (MSG_FIRMWARE[main_sw_id-SWID_START][0x7f51] << 8) + MSG_FIRMWARE[main_sw_id-SWID_START][0x7f50];
+
+ //check upgrading
+ if ((update_bin_major == fw_version_major) && (update_bin_minor > fw_version_minor))
+ {
+ update_flag = 1;
+ }
+ }
+ DBG("MAIN sw_id=%d,update_flag=%d,update_bin_major=%d,update_bin_minor=%d\n",main_sw_id,update_flag,update_bin_major,update_bin_minor);
+ }
+ else
+ {
+ if ((info_sw_id >= SWID_START) && (info_sw_id < SWID_NULL))
+ {
+ update_bin_major= (MSG_FIRMWARE[info_sw_id-SWID_START][0x7f4f] << 8) + MSG_FIRMWARE[info_sw_id-SWID_START][0x7f4e];
+ update_bin_minor= (MSG_FIRMWARE[info_sw_id-SWID_START][0x7f51] << 8) + MSG_FIRMWARE[info_sw_id-SWID_START][0x7f50];
+ update_flag = 1;
+ }
+ DBG("INFO1 sw_id=%d,update_flag=%d,update_bin_major=%d,update_bin_minor=%d\n",info_sw_id,update_flag,update_bin_major,update_bin_minor);
+ }
+ }
+ else
+ {
+ if ((info_sw_id >= SWID_START) && (info_sw_id < SWID_NULL))
+ {
+ update_bin_major= (MSG_FIRMWARE[info_sw_id-SWID_START][0x7f4f] << 8) + MSG_FIRMWARE[info_sw_id-SWID_START][0x7f4e];
+ update_bin_minor= (MSG_FIRMWARE[info_sw_id-SWID_START][0x7f51] << 8) + MSG_FIRMWARE[info_sw_id-SWID_START][0x7f50];
+ update_flag = 1;
+ }
+ DBG("INFO2 sw_id=%d,update_flag=%d,update_bin_major=%d,update_bin_minor=%d\n",info_sw_id,update_flag,update_bin_major,update_bin_minor);
+ }
+
+ if (update_flag == 1)
+ {
+ DBG("MSG21XX_fw_auto_update begin....\n");
+ //transfer data
+ for (i = 0; i < 33; i++)
+ {
+ firmware_data_store(NULL, NULL, &(MSG_FIRMWARE[info_sw_id-SWID_START][i*1024]), 1024);
+ }
+
+ kthread_run(fwAutoUpdate, 0, "MSG21XX_fw_auto_update");
+ DBG("*** mstar touch screen registered ***\n");
+ return 0;
+ }
+
+ reset_hw();
+#endif
+
+ DBG("*** mstar touch screen registered ***\n");
+ enable_irq(irq_msg21xx);
+ return 0;
+
+err3:
+ free_irq(irq_msg21xx, input_dev);
+
+err2:
+ gpio_free(MS_TS_MSG21XX_GPIO_INT);
+
+err1:
+ mutex_destroy(&msg21xx_mutex);
+ input_unregister_device(input_dev);
+ input_free_device(input_dev);
+ input_dev = NULL;
+
+err0:
+ gpio_free(MS_TS_MSG21XX_GPIO_RST);
+
+ return ret;
+}
+
+/* remove function is triggered when the input device is removed from input sub-system */
+static int touch_driver_remove(struct i2c_client *client)
+{
+ DBG("touch_driver_remove()\n");
+
+ free_irq(irq_msg21xx, input_dev);
+ gpio_free(MS_TS_MSG21XX_GPIO_INT);
+ gpio_free(MS_TS_MSG21XX_GPIO_RST);
+ input_unregister_device(input_dev);
+ mutex_destroy(&msg21xx_mutex);
+
+ return 0;
+}
+
+/* The I2C device list is used for matching I2C device and I2C device driver. */
+static const struct i2c_device_id touch_device_id[] =
+{
+ {"msg21xx", 0},
+ {}, /* should not omitted */
+};
+
+MODULE_DEVICE_TABLE(i2c, touch_device_id);
+
+static struct i2c_driver touch_device_driver =
+{
+ .driver = {
+ .name = "msg21xx",
+ .owner = THIS_MODULE,
+ },
+ .probe = touch_driver_probe,
+ .remove = touch_driver_remove,
+ .id_table = touch_device_id,
+};
+
+static int __init touch_driver_init(void)
+{
+ int ret;
+
+ /* register driver */
+ ret = i2c_add_driver(&touch_device_driver);
+ if (ret < 0)
+ {
+ DBG("add touch_device_driver i2c driver failed.\n");
+ return -ENODEV;
+ }
+ DBG("add touch_device_driver i2c driver.\n");
+
+ return ret;
+}
+
+static void __exit touch_driver_exit(void)
+{
+ DBG("remove touch_device_driver i2c driver.\n");
+
+ i2c_del_driver(&touch_device_driver);
+}
+
+#ifdef TP_PRINT
+#include <linux/proc_fs.h>
+
+static U16 InfoAddr = 0x0F, PoolAddr = 0x10, TransLen = 256;
+static U8 row, units, cnt;
+
+static int tp_print_proc_read(void)
+{
+ U16 i, j;
+ U16 left, offset = 0;
+ U8 dbbus_tx_data[3] = {0};
+ U8 u8Data;
+ S16 s16Data;
+ S32 s32Data;
+ char *buf = NULL;
+
+ left = cnt*row*units;
+ if ((bTpInSuspend == 0) && (InfoAddr != 0x0F) && (PoolAddr != 0x10) && (left > 0))
+ {
+ buf = kmalloc(left, GFP_KERNEL);
+ if (buf != NULL)
+ {
+ printk("tpp: \n");
+
+ while (left > 0)
+ {
+ dbbus_tx_data[0] = 0x53;
+ dbbus_tx_data[1] = ((PoolAddr + offset) >> 8) & 0xFF;
+ dbbus_tx_data[2] = (PoolAddr + offset) & 0xFF;
+ mutex_lock(&msg21xx_mutex);
+ write_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_tx_data[0], 3);
+ read_i2c_seq(SLAVE_I2C_ID_DWI2C, &buf[offset], left > TransLen ? TransLen : left);
+ mutex_unlock(&msg21xx_mutex);
+
+ if (left > TransLen)
+ {
+ left -= TransLen;
+ offset += TransLen;
+ }
+ else
+ {
+ left = 0;
+ }
+ }
+
+ for (i = 0; i < cnt; i++)
+ {
+ printk("tpp: ");
+ for (j = 0; j < row; j++)
+ {
+ if (units == 1)
+ {
+ u8Data = buf[i*row*units + j*units];
+ printk("%d\t", u8Data);
+ }
+ else if (units == 2)
+ {
+ s16Data = buf[i*row*units + j*units] + (buf[i*row*units + j*units + 1] << 8);
+ printk("%d\t", s16Data);
+ }
+ else if (units == 4)
+ {
+ s32Data = buf[i*row*units + j*units] + (buf[i*row*units + j*units + 1] << 8) + (buf[i*row*units + j*units + 2] << 16) + (buf[i*row*units + j*units + 3] << 24);
+ printk("%d\t", s32Data);
+ }
+ }
+ printk("\n");
+ }
+
+ kfree(buf);
+ }
+ }
+
+ return 0;
+}
+
+static void tp_print_create_entry(void)
+{
+ U8 dbbus_tx_data[3] = {0};
+ U8 dbbus_rx_data[8] = {0};
+
+ dbbus_tx_data[0] = 0x53;
+ dbbus_tx_data[1] = 0x00;
+ dbbus_tx_data[2] = 0x58;
+ mutex_lock(&msg21xx_mutex);
+ write_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_tx_data[0], 3);
+ read_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_rx_data[0], 4);
+ mutex_unlock(&msg21xx_mutex);
+ InfoAddr = (dbbus_rx_data[1]<<8) + dbbus_rx_data[0];
+ PoolAddr = (dbbus_rx_data[3]<<8) + dbbus_rx_data[2];
+ printk("InfoAddr=0x%X\n", InfoAddr);
+ printk("PoolAddr=0x%X\n", PoolAddr);
+
+ if ((InfoAddr != 0x0F) && (PoolAddr != 0x10))
+ {
+ msleep(10);
+ dbbus_tx_data[0] = 0x53;
+ dbbus_tx_data[1] = (InfoAddr >> 8) & 0xFF;
+ dbbus_tx_data[2] = InfoAddr & 0xFF;
+ mutex_lock(&msg21xx_mutex);
+ write_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_tx_data[0], 3);
+ read_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_rx_data[0], 8);
+ mutex_unlock(&msg21xx_mutex);
+
+ units = dbbus_rx_data[0];
+ row = dbbus_rx_data[1];
+ cnt = dbbus_rx_data[2];
+ TransLen = (dbbus_rx_data[7]<<8) + dbbus_rx_data[6];
+ printk("tpp: row=%d, units=%d\n", row, units);
+ printk("tpp: cnt=%d, TransLen=%d\n", cnt, TransLen);
+
+ // tpp
+ if (device_create_file(firmware_cmd_dev, &dev_attr_tpp) < 0)
+ {
+ pr_err("Failed to create device file(%s)!\n", dev_attr_tpp.attr.name);
+ }
+ }
+}
+#endif
+
+module_init(touch_driver_init);
+module_exit(touch_driver_exit);
+MODULE_AUTHOR("MStar Semiconductor, Inc.");
+MODULE_LICENSE("GPL v2");
+
diff --git a/kernel/drivers/input/touchscreen/synaptics_fw_update.c b/kernel/drivers/input/touchscreen/synaptics_fw_update.c
new file mode 100644
index 000000000000..4867d1f73c4d
--- /dev/null
+++ b/kernel/drivers/input/touchscreen/synaptics_fw_update.c
@@ -0,0 +1,1587 @@
+/*
+ * Synaptics RMI4 touchscreen driver
+ *
+ * Copyright (C) 2012 Synaptics Incorporated
+ *
+ * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
+ * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/firmware.h>
+#include <linux/input/synaptics_dsx.h>
+#include "synaptics_i2c_rmi4.h"
+
+#define DEBUG_FW_UPDATE
+#define SHOW_PROGRESS
+#define FW_IMAGE_NAME "PR12345678.img"
+
+#define CHECKSUM_OFFSET 0x00
+#define BOOTLOADER_VERSION_OFFSET 0x07
+#define IMAGE_SIZE_OFFSET 0x08
+#define CONFIG_SIZE_OFFSET 0x0C
+#define PRODUCT_ID_OFFSET 0x10
+#define PRODUCT_INFO_OFFSET 0x1E
+#define FW_IMAGE_OFFSET 0x100
+#define PRODUCT_ID_SIZE 10
+
+#define BOOTLOADER_ID_OFFSET 0
+#define FLASH_PROPERTIES_OFFSET 2
+#define BLOCK_SIZE_OFFSET 3
+#define FW_BLOCK_COUNT_OFFSET 5
+
+#define REG_MAP (1 << 0)
+#define UNLOCKED (1 << 1)
+#define HAS_CONFIG_ID (1 << 2)
+#define HAS_PERM_CONFIG (1 << 3)
+#define HAS_BL_CONFIG (1 << 4)
+#define HAS_DISP_CONFIG (1 << 5)
+#define HAS_CTRL1 (1 << 6)
+
+#define BLOCK_NUMBER_OFFSET 0
+#define BLOCK_DATA_OFFSET 2
+
+#define UI_CONFIG_AREA 0x00
+#define PERM_CONFIG_AREA 0x01
+#define BL_CONFIG_AREA 0x02
+#define DISP_CONFIG_AREA 0x03
+
+enum flash_command {
+ CMD_WRITE_FW_BLOCK = 0x2,
+ CMD_ERASE_ALL = 0x3,
+ CMD_READ_CONFIG_BLOCK = 0x5,
+ CMD_WRITE_CONFIG_BLOCK = 0x6,
+ CMD_ERASE_CONFIG = 0x7,
+ CMD_ERASE_BL_CONFIG = 0x9,
+ CMD_ERASE_DISP_CONFIG = 0xA,
+ CMD_ENABLE_FLASH_PROG = 0xF,
+};
+
+#define SLEEP_MODE_NORMAL (0x00)
+#define SLEEP_MODE_SENSOR_SLEEP (0x01)
+#define SLEEP_MODE_RESERVED0 (0x02)
+#define SLEEP_MODE_RESERVED1 (0x03)
+
+#define ENABLE_WAIT_MS (1 * 1000)
+#define WRITE_WAIT_MS (3 * 1000)
+#define ERASE_WAIT_MS (5 * 1000)
+
+#define MIN_SLEEP_TIME_US 50
+#define MAX_SLEEP_TIME_US 100
+
+static ssize_t fwu_sysfs_show_image(struct file *data_file,
+ struct kobject *kobj, struct bin_attribute *attributes,
+ char *buf, loff_t pos, size_t count);
+
+static ssize_t fwu_sysfs_store_image(struct file *data_file,
+ struct kobject *kobj, struct bin_attribute *attributes,
+ char *buf, loff_t pos, size_t count);
+
+static ssize_t fwu_sysfs_do_reflash_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t fwu_sysfs_write_config_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t fwu_sysfs_read_config_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t fwu_sysfs_config_area_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t fwu_sysfs_image_size_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t fwu_sysfs_block_size_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t fwu_sysfs_firmware_block_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t fwu_sysfs_configuration_block_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t fwu_sysfs_perm_config_block_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t fwu_sysfs_bl_config_block_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t fwu_sysfs_disp_config_block_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static int fwu_wait_for_idle(int timeout_ms);
+
+struct image_header {
+ unsigned int checksum;
+ unsigned int image_size;
+ unsigned int config_size;
+ unsigned char options;
+ unsigned char bootloader_version;
+ unsigned char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1];
+ unsigned char product_info[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE];
+};
+
+struct pdt_properties {
+ union {
+ struct {
+ unsigned char reserved_1:6;
+ unsigned char has_bsr:1;
+ unsigned char reserved_2:1;
+ } __packed;
+ unsigned char data[1];
+ };
+};
+
+struct f01_device_status {
+ union {
+ struct {
+ unsigned char status_code:4;
+ unsigned char reserved:2;
+ unsigned char flash_prog:1;
+ unsigned char unconfigured:1;
+ } __packed;
+ unsigned char data[1];
+ };
+};
+
+struct f01_device_control {
+ union {
+ struct {
+ unsigned char sleep_mode:2;
+ unsigned char nosleep:1;
+ unsigned char reserved:2;
+ unsigned char charger_connected:1;
+ unsigned char report_rate:1;
+ unsigned char configured:1;
+ } __packed;
+ unsigned char data[1];
+ };
+};
+
+struct f34_flash_control {
+ union {
+ struct {
+ unsigned char command:4;
+ unsigned char status:3;
+ unsigned char program_enabled:1;
+ } __packed;
+ unsigned char data[1];
+ };
+};
+
+struct f34_flash_properties {
+ union {
+ struct {
+ unsigned char regmap:1;
+ unsigned char unlocked:1;
+ unsigned char has_configid:1;
+ unsigned char has_perm_config:1;
+ unsigned char has_bl_config:1;
+ unsigned char has_display_config:1;
+ unsigned char has_blob_config:1;
+ unsigned char reserved:1;
+ } __packed;
+ unsigned char data[1];
+ };
+};
+
+struct synaptics_rmi4_fwu_handle {
+ bool initialized;
+ char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1];
+ unsigned int image_size;
+ unsigned int data_pos;
+ unsigned char intr_mask;
+ unsigned char bootloader_id[2];
+ unsigned char productinfo1;
+ unsigned char productinfo2;
+ unsigned char *ext_data_source;
+ unsigned char *read_config_buf;
+ const unsigned char *firmware_data;
+ const unsigned char *config_data;
+ unsigned short block_size;
+ unsigned short fw_block_count;
+ unsigned short config_block_count;
+ unsigned short perm_config_block_count;
+ unsigned short bl_config_block_count;
+ unsigned short disp_config_block_count;
+ unsigned short config_size;
+ unsigned short config_area;
+ unsigned short addr_f34_flash_control;
+ unsigned short addr_f01_interrupt_register;
+ struct synaptics_rmi4_fn_desc f01_fd;
+ struct synaptics_rmi4_fn_desc f34_fd;
+ struct synaptics_rmi4_exp_fn_ptr *fn_ptr;
+ struct synaptics_rmi4_data *rmi4_data;
+ struct f34_flash_control flash_control;
+ struct f34_flash_properties flash_properties;
+};
+
+static struct bin_attribute dev_attr_data = {
+ .attr = {
+ .name = "data",
+ .mode = (S_IRUGO | S_IWUGO),
+ },
+ .size = 0,
+ .read = fwu_sysfs_show_image,
+ .write = fwu_sysfs_store_image,
+};
+
+static struct device_attribute attrs[] = {
+ __ATTR(doreflash, S_IWUGO,
+ synaptics_rmi4_show_error,
+ fwu_sysfs_do_reflash_store),
+ __ATTR(writeconfig, S_IWUGO,
+ synaptics_rmi4_show_error,
+ fwu_sysfs_write_config_store),
+ __ATTR(readconfig, S_IWUGO,
+ synaptics_rmi4_show_error,
+ fwu_sysfs_read_config_store),
+ __ATTR(configarea, S_IWUGO,
+ synaptics_rmi4_show_error,
+ fwu_sysfs_config_area_store),
+ __ATTR(imagesize, S_IWUGO,
+ synaptics_rmi4_show_error,
+ fwu_sysfs_image_size_store),
+ __ATTR(blocksize, S_IRUGO,
+ fwu_sysfs_block_size_show,
+ synaptics_rmi4_store_error),
+ __ATTR(fwblockcount, S_IRUGO,
+ fwu_sysfs_firmware_block_count_show,
+ synaptics_rmi4_store_error),
+ __ATTR(configblockcount, S_IRUGO,
+ fwu_sysfs_configuration_block_count_show,
+ synaptics_rmi4_store_error),
+ __ATTR(permconfigblockcount, S_IRUGO,
+ fwu_sysfs_perm_config_block_count_show,
+ synaptics_rmi4_store_error),
+ __ATTR(blconfigblockcount, S_IRUGO,
+ fwu_sysfs_bl_config_block_count_show,
+ synaptics_rmi4_store_error),
+ __ATTR(dispconfigblockcount, S_IRUGO,
+ fwu_sysfs_disp_config_block_count_show,
+ synaptics_rmi4_store_error),
+};
+
+static struct synaptics_rmi4_fwu_handle *fwu;
+
+static struct completion remove_complete;
+
+static unsigned int extract_uint(const unsigned char *ptr)
+{
+ return (unsigned int)ptr[0] +
+ (unsigned int)ptr[1] * 0x100 +
+ (unsigned int)ptr[2] * 0x10000 +
+ (unsigned int)ptr[3] * 0x1000000;
+}
+
+static void parse_header(struct image_header *header,
+ const unsigned char *fw_image)
+{
+ header->checksum = extract_uint(&fw_image[CHECKSUM_OFFSET]);
+ header->bootloader_version = fw_image[BOOTLOADER_VERSION_OFFSET];
+ header->image_size = extract_uint(&fw_image[IMAGE_SIZE_OFFSET]);
+ header->config_size = extract_uint(&fw_image[CONFIG_SIZE_OFFSET]);
+ memcpy(header->product_id, &fw_image[PRODUCT_ID_OFFSET],
+ SYNAPTICS_RMI4_PRODUCT_ID_SIZE);
+ header->product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE] = 0;
+ memcpy(header->product_info, &fw_image[PRODUCT_INFO_OFFSET],
+ SYNAPTICS_RMI4_PRODUCT_INFO_SIZE);
+
+#ifdef DEBUG_FW_UPDATE
+ dev_info(&fwu->rmi4_data->i2c_client->dev,
+ "Firwmare size %d, config size %d\n",
+ header->image_size,
+ header->config_size);
+#endif
+ return;
+}
+
+static int fwu_check_version(void)
+{
+ int retval;
+ unsigned char firmware_id[4];
+ unsigned char config_id[4];
+ struct i2c_client *i2c_client = fwu->rmi4_data->i2c_client;
+
+ /* device firmware id */
+ retval = fwu->fn_ptr->read(fwu->rmi4_data,
+ fwu->f01_fd.query_base_addr + 18,
+ firmware_id,
+ sizeof(firmware_id));
+ if (retval < 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to read firmware ID (code %d).\n", retval);
+ return retval;
+ }
+ firmware_id[3] = 0;
+
+ dev_info(&i2c_client->dev, "Device firmware ID%d\n",
+ extract_uint(firmware_id));
+
+ /* device config id */
+ retval = fwu->fn_ptr->read(fwu->rmi4_data,
+ fwu->f34_fd.ctrl_base_addr,
+ config_id,
+ sizeof(config_id));
+ if (retval < 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to read config ID (code %d).\n", retval);
+ return retval;
+ }
+
+ dev_info(&i2c_client->dev,
+ "Device config ID 0x%02X, 0x%02X, 0x%02X, 0x%02X\n",
+ config_id[0], config_id[1], config_id[2], config_id[3]);
+
+ /* .img config id */
+ dev_info(&i2c_client->dev,
+ ".img config ID 0x%02X, 0x%02X, 0x%02X, 0x%02X\n",
+ fwu->config_data[0],
+ fwu->config_data[1],
+ fwu->config_data[2],
+ fwu->config_data[3]);
+ return 0;
+}
+
+static int fwu_read_f01_device_status(struct f01_device_status *status)
+{
+ int retval;
+
+ retval = fwu->fn_ptr->read(fwu->rmi4_data,
+ fwu->f01_fd.data_base_addr,
+ status->data,
+ sizeof(status->data));
+ if (retval < 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Failed to read F01 device status\n",
+ __func__);
+ return retval;
+ }
+
+ return 0;
+}
+
+static int fwu_read_f34_queries(void)
+{
+ int retval;
+ unsigned char count = 4;
+ unsigned char buf[10];
+ struct i2c_client *i2c_client = fwu->rmi4_data->i2c_client;
+
+ retval = fwu->fn_ptr->read(fwu->rmi4_data,
+ fwu->f34_fd.query_base_addr + BOOTLOADER_ID_OFFSET,
+ fwu->bootloader_id,
+ sizeof(fwu->bootloader_id));
+ if (retval < 0) {
+ dev_err(&i2c_client->dev,
+ "%s: Failed to read bootloader ID\n",
+ __func__);
+ return retval;
+ }
+
+ retval = fwu->fn_ptr->read(fwu->rmi4_data,
+ fwu->f34_fd.query_base_addr + FLASH_PROPERTIES_OFFSET,
+ fwu->flash_properties.data,
+ sizeof(fwu->flash_properties.data));
+ if (retval < 0) {
+ dev_err(&i2c_client->dev,
+ "%s: Failed to read flash properties\n",
+ __func__);
+ return retval;
+ }
+
+ dev_info(&i2c_client->dev, "%s perm:%d, bl%d, display:%d\n",
+ __func__,
+ fwu->flash_properties.has_perm_config,
+ fwu->flash_properties.has_bl_config,
+ fwu->flash_properties.has_display_config);
+
+ if (fwu->flash_properties.has_perm_config)
+ count += 2;
+
+ if (fwu->flash_properties.has_bl_config)
+ count += 2;
+
+ if (fwu->flash_properties.has_display_config)
+ count += 2;
+
+ retval = fwu->fn_ptr->read(fwu->rmi4_data,
+ fwu->f34_fd.query_base_addr + BLOCK_SIZE_OFFSET,
+ buf,
+ 2);
+ if (retval < 0) {
+ dev_err(&i2c_client->dev,
+ "%s: Failed to read block size info\n",
+ __func__);
+ return retval;
+ }
+
+ batohs(&fwu->block_size, &(buf[0]));
+
+ retval = fwu->fn_ptr->read(fwu->rmi4_data,
+ fwu->f34_fd.query_base_addr + FW_BLOCK_COUNT_OFFSET,
+ buf,
+ count);
+ if (retval < 0) {
+ dev_err(&i2c_client->dev,
+ "%s: Failed to read block count info\n",
+ __func__);
+ return retval;
+ }
+
+ batohs(&fwu->fw_block_count, &(buf[0]));
+ batohs(&fwu->config_block_count, &(buf[2]));
+
+ count = 4;
+
+ if (fwu->flash_properties.has_perm_config) {
+ batohs(&fwu->perm_config_block_count, &(buf[count]));
+ count += 2;
+ }
+
+ if (fwu->flash_properties.has_bl_config) {
+ batohs(&fwu->bl_config_block_count, &(buf[count]));
+ count += 2;
+ }
+
+ if (fwu->flash_properties.has_display_config)
+ batohs(&fwu->disp_config_block_count, &(buf[count]));
+
+ fwu->addr_f34_flash_control = fwu->f34_fd.data_base_addr +
+ BLOCK_DATA_OFFSET +
+ fwu->block_size;
+ return 0;
+}
+
+static int fwu_read_interrupt_status(void)
+{
+ int retval;
+ unsigned char interrupt_status;
+ retval = fwu->fn_ptr->read(fwu->rmi4_data,
+ fwu->addr_f01_interrupt_register,
+ &interrupt_status,
+ sizeof(interrupt_status));
+ if (retval < 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Failed to read flash status\n",
+ __func__);
+ return retval;
+ }
+ return interrupt_status;
+}
+
+static int fwu_read_f34_flash_status(void)
+{
+ int retval;
+ retval = fwu->fn_ptr->read(fwu->rmi4_data,
+ fwu->addr_f34_flash_control,
+ fwu->flash_control.data,
+ sizeof(fwu->flash_control.data));
+ if (retval < 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Failed to read flash status\n",
+ __func__);
+ return retval;
+ }
+ return 0;
+}
+
+static int fwu_reset_device(void)
+{
+ int retval;
+ unsigned char reset = 0x01;
+
+#ifdef DEBUG_FW_UPDATE
+ dev_info(&fwu->rmi4_data->i2c_client->dev, "Reset device\n");
+#endif
+
+ retval = fwu->fn_ptr->write(fwu->rmi4_data,
+ fwu->f01_fd.cmd_base_addr,
+ &reset,
+ sizeof(reset));
+ if (retval < 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Failed to reset device (addr : 0x%02x)\n",
+ __func__, fwu->f01_fd.cmd_base_addr);
+ return retval;
+ }
+
+ fwu_wait_for_idle(WRITE_WAIT_MS);
+
+ retval = fwu->rmi4_data->reset_device(fwu->rmi4_data);
+ if (retval < 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Failed to reset core driver after reflash\n",
+ __func__);
+ return retval;
+ }
+ return 0;
+}
+
+static int fwu_write_f34_command(unsigned char cmd)
+{
+ int retval;
+
+ retval = fwu->fn_ptr->write(fwu->rmi4_data,
+ fwu->addr_f34_flash_control,
+ &cmd,
+ sizeof(cmd));
+ if (retval < 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Failed to write command 0x%02x\n",
+ __func__, cmd);
+ return retval;
+ }
+ return 0;
+}
+
+static unsigned char fwu_check_flash_status(void)
+{
+ fwu_read_f34_flash_status();
+ return fwu->flash_control.status;
+}
+
+static int fwu_wait_for_idle(int timeout_ms)
+{
+ int count = 0;
+ int timeout_count = ((timeout_ms * 1000) / MAX_SLEEP_TIME_US) + 1;
+
+ do {
+ if (fwu_read_interrupt_status() > 0)
+ return 0;
+
+ usleep_range(MIN_SLEEP_TIME_US, MAX_SLEEP_TIME_US);
+ count++;
+ } while (count < timeout_count);
+
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Timed out waiting for idle status\n",
+ __func__);
+
+ return -ETIMEDOUT;
+}
+
+static int fwu_scan_pdt(void)
+{
+ int retval;
+ unsigned char ii;
+ unsigned char intr_count = 0;
+ unsigned char intr_off;
+ unsigned char intr_src;
+ unsigned short addr;
+ bool f01found = false;
+ bool f34found = false;
+ struct synaptics_rmi4_fn_desc rmi_fd;
+
+#ifdef DEBUG_FW_UPDATE
+ dev_info(&fwu->rmi4_data->i2c_client->dev, "Scan PDT\n");
+#endif
+
+ for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) {
+ retval = fwu->fn_ptr->read(fwu->rmi4_data,
+ addr,
+ (unsigned char *)&rmi_fd,
+ sizeof(rmi_fd));
+ if (retval < 0)
+ return retval;
+
+ if (rmi_fd.fn_number) {
+ dev_dbg(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Found F%02x\n",
+ __func__, rmi_fd.fn_number);
+ switch (rmi_fd.fn_number) {
+ case SYNAPTICS_RMI4_F01:
+ f01found = true;
+ fwu->f01_fd = rmi_fd;
+ fwu->addr_f01_interrupt_register =
+ fwu->f01_fd.data_base_addr + 1;
+ break;
+ case SYNAPTICS_RMI4_F34:
+ f34found = true;
+ fwu->f34_fd = rmi_fd;
+ fwu->intr_mask = 0;
+ intr_src = rmi_fd.intr_src_count;
+ intr_off = intr_count % 8;
+ for (ii = intr_off;
+ ii < ((intr_src & MASK_3BIT) +
+ intr_off);
+ ii++)
+ fwu->intr_mask |= 1 << ii;
+ break;
+ }
+ } else
+ break;
+
+ intr_count += (rmi_fd.intr_src_count & MASK_3BIT);
+ }
+
+ if (!f01found || !f34found) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Failed to find both F01 and F34\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ fwu_read_interrupt_status();
+ return 0;
+}
+
+static int fwu_write_blocks(unsigned char *block_ptr, unsigned short block_cnt,
+ unsigned char command)
+{
+ int retval;
+ unsigned char block_offset[] = {0, 0};
+ unsigned short block_num;
+#ifdef SHOW_PROGRESS
+ unsigned int progress = (command == CMD_WRITE_CONFIG_BLOCK) ?
+ 10 : 100;
+#endif
+ retval = fwu->fn_ptr->write(fwu->rmi4_data,
+ fwu->f34_fd.data_base_addr + BLOCK_NUMBER_OFFSET,
+ block_offset,
+ sizeof(block_offset));
+ if (retval < 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Failed to write to block number registers\n",
+ __func__);
+ return retval;
+ }
+
+ for (block_num = 0; block_num < block_cnt; block_num++) {
+#ifdef SHOW_PROGRESS
+ if (block_num % progress == 0)
+ dev_info(&fwu->rmi4_data->i2c_client->dev,
+ "%s: update %s %3d / %3d\n",
+ __func__,
+ command == CMD_WRITE_CONFIG_BLOCK ?
+ "config" : "firmware",
+ block_num,
+ block_cnt);
+#endif
+ retval = fwu->fn_ptr->write(fwu->rmi4_data,
+ fwu->f34_fd.data_base_addr + BLOCK_DATA_OFFSET,
+ block_ptr,
+ fwu->block_size);
+ if (retval < 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Failed to write block data (block %d)\n",
+ __func__, block_num);
+ return retval;
+ }
+
+ retval = fwu_write_f34_command(command);
+ if (retval < 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Failed to write command for block %d\n",
+ __func__, block_num);
+ return retval;
+ }
+
+ retval = fwu_wait_for_idle(WRITE_WAIT_MS);
+ if (retval < 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Failed to wait for idle status \
+ (block %d)\n",
+ __func__, block_num);
+ return retval;
+ }
+
+ retval = fwu_check_flash_status();
+ if (retval != 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Flash block %d status %d\n",
+ __func__, block_num, retval);
+ return -1;
+ }
+ block_ptr += fwu->block_size;
+ }
+#ifdef SHOW_PROGRESS
+ dev_info(&fwu->rmi4_data->i2c_client->dev,
+ "%s: update %s %3d / %3d\n",
+ __func__,
+ command == CMD_WRITE_CONFIG_BLOCK ?
+ "config" : "firmware",
+ block_cnt,
+ block_cnt);
+#endif
+ return 0;
+}
+
+static int fwu_write_firmware(void)
+{
+ return fwu_write_blocks((unsigned char *)fwu->firmware_data,
+ fwu->fw_block_count, CMD_WRITE_FW_BLOCK);
+}
+
+static int fwu_write_configuration(void)
+{
+ return fwu_write_blocks((unsigned char *)fwu->config_data,
+ fwu->config_block_count, CMD_WRITE_CONFIG_BLOCK);
+}
+
+static int fwu_write_bootloader_id(void)
+{
+ int retval;
+
+#ifdef DEBUG_FW_UPDATE
+ dev_info(&fwu->rmi4_data->i2c_client->dev, "Write bootloader ID\n");
+#endif
+ retval = fwu->fn_ptr->write(fwu->rmi4_data,
+ fwu->f34_fd.data_base_addr + BLOCK_DATA_OFFSET,
+ fwu->bootloader_id,
+ sizeof(fwu->bootloader_id));
+ if (retval < 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Failed to write bootloader ID\n",
+ __func__);
+ return retval;
+ }
+
+ return 0;
+}
+
+static int fwu_enter_flash_prog(void)
+{
+ int retval;
+ struct f01_device_status f01_device_status;
+ struct f01_device_control f01_device_control;
+
+#ifdef DEBUG_FW_UPDATE
+ dev_info(&fwu->rmi4_data->i2c_client->dev, "Enter bootloader mode\n");
+#endif
+ retval = fwu_read_f01_device_status(&f01_device_status);
+ if (retval < 0)
+ return retval;
+
+ if (f01_device_status.flash_prog) {
+ dev_info(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Already in flash prog mode\n",
+ __func__);
+ return 0;
+ }
+
+ retval = fwu_write_bootloader_id();
+ if (retval < 0)
+ return retval;
+
+ retval = fwu_write_f34_command(CMD_ENABLE_FLASH_PROG);
+ if (retval < 0)
+ return retval;
+
+ retval = fwu_wait_for_idle(ENABLE_WAIT_MS);
+ if (retval < 0)
+ return retval;
+
+ retval = fwu_read_f01_device_status(&f01_device_status);
+ if (retval < 0)
+ return retval;
+
+ if (!f01_device_status.flash_prog) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Program enabled bit not set\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ retval = fwu_scan_pdt();
+ if (retval < 0)
+ return retval;
+
+ retval = fwu_read_f01_device_status(&f01_device_status);
+ if (retval < 0)
+ return retval;
+
+ if (!f01_device_status.flash_prog) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Not in flash prog mode\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ retval = fwu_read_f34_queries();
+ if (retval < 0)
+ return retval;
+
+ retval = fwu->fn_ptr->read(fwu->rmi4_data,
+ fwu->f01_fd.ctrl_base_addr,
+ f01_device_control.data,
+ sizeof(f01_device_control.data));
+ if (retval < 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Failed to read F01 device control\n",
+ __func__);
+ return retval;
+ }
+
+ f01_device_control.nosleep = true;
+ f01_device_control.sleep_mode = SLEEP_MODE_NORMAL;
+
+ retval = fwu->fn_ptr->write(fwu->rmi4_data,
+ fwu->f01_fd.ctrl_base_addr,
+ f01_device_control.data,
+ sizeof(f01_device_control.data));
+ if (retval < 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Failed to write F01 device control\n",
+ __func__);
+ return retval;
+ }
+
+ return retval;
+}
+
+static int fwu_do_reflash(void)
+{
+ int retval;
+
+ retval = fwu_enter_flash_prog();
+ if (retval < 0)
+ return retval;
+
+ dev_dbg(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Entered flash prog mode\n",
+ __func__);
+
+ retval = fwu_write_bootloader_id();
+ if (retval < 0)
+ return retval;
+
+ dev_dbg(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Bootloader ID written\n",
+ __func__);
+
+ retval = fwu_write_f34_command(CMD_ERASE_ALL);
+ if (retval < 0)
+ return retval;
+
+ dev_dbg(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Erase all command written\n",
+ __func__);
+
+ retval = fwu_wait_for_idle(ERASE_WAIT_MS);
+ if (retval < 0)
+ return retval;
+
+ dev_dbg(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Idle status detected\n",
+ __func__);
+
+ if (fwu->firmware_data) {
+ retval = fwu_write_firmware();
+ if (retval < 0)
+ return retval;
+ pr_notice("%s: Firmware programmed\n", __func__);
+ }
+
+ if (fwu->config_data) {
+ retval = fwu_write_configuration();
+ if (retval < 0)
+ return retval;
+ pr_notice("%s: Configuration programmed\n", __func__);
+ }
+
+ return retval;
+}
+
+static int fwu_start_reflash(void)
+{
+ int retval;
+ struct image_header header;
+ const unsigned char *fw_image;
+ const struct firmware *fw_entry = NULL;
+ struct f01_device_status f01_device_status;
+
+ pr_notice("%s: Start of reflash process\n", __func__);
+
+ if (fwu->ext_data_source)
+ fw_image = fwu->ext_data_source;
+ else {
+ dev_dbg(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Requesting firmware image %s\n",
+ __func__, FW_IMAGE_NAME);
+
+ retval = request_firmware(&fw_entry, FW_IMAGE_NAME,
+ &fwu->rmi4_data->i2c_client->dev);
+ if (retval != 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Firmware image %s not available\n",
+ __func__, FW_IMAGE_NAME);
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ dev_dbg(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Firmware image size = %d\n",
+ __func__, fw_entry->size);
+
+ fw_image = fw_entry->data;
+ }
+
+ parse_header(&header, fw_image);
+
+ if (header.image_size)
+ fwu->firmware_data = fw_image + FW_IMAGE_OFFSET;
+ if (header.config_size) {
+ fwu->config_data = fw_image + FW_IMAGE_OFFSET +
+ header.image_size;
+ }
+
+ fwu->fn_ptr->enable(fwu->rmi4_data, false);
+
+ fwu_check_version();
+
+ retval = fwu_do_reflash();
+ if (retval < 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Failed to do reflash\n",
+ __func__);
+ }
+
+ /* reset device */
+ fwu_reset_device();
+
+ /* check device status */
+ retval = fwu_read_f01_device_status(&f01_device_status);
+ if (retval < 0)
+ goto exit;
+
+ dev_info(&fwu->rmi4_data->i2c_client->dev, "Device is in %s mode\n",
+ f01_device_status.flash_prog == 1 ? "bootloader" : "UI");
+ if (f01_device_status.flash_prog)
+ dev_info(&fwu->rmi4_data->i2c_client->dev, "Flash status %d\n",
+ f01_device_status.status_code);
+
+ if (f01_device_status.flash_prog) {
+ dev_info(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Device is in flash prog mode 0x%02X\n",
+ __func__, f01_device_status.status_code);
+ retval = 0;
+ goto exit;
+ }
+ fwu->fn_ptr->enable(fwu->rmi4_data, true);
+ if (fw_entry)
+ release_firmware(fw_entry);
+
+ pr_notice("%s: End of reflash process\n", __func__);
+exit:
+ return retval;
+}
+
+static int fwu_do_write_config(void)
+{
+ int retval;
+
+ retval = fwu_enter_flash_prog();
+ if (retval < 0)
+ return retval;
+
+ dev_dbg(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Entered flash prog mode\n",
+ __func__);
+
+ if (fwu->config_area == PERM_CONFIG_AREA) {
+ fwu->config_block_count = fwu->perm_config_block_count;
+ goto write_config;
+ }
+
+ retval = fwu_write_bootloader_id();
+ if (retval < 0)
+ return retval;
+
+ dev_dbg(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Bootloader ID written\n",
+ __func__);
+
+ switch (fwu->config_area) {
+ case UI_CONFIG_AREA:
+ retval = fwu_write_f34_command(CMD_ERASE_CONFIG);
+ break;
+ case BL_CONFIG_AREA:
+ retval = fwu_write_f34_command(CMD_ERASE_BL_CONFIG);
+ fwu->config_block_count = fwu->bl_config_block_count;
+ break;
+ case DISP_CONFIG_AREA:
+ retval = fwu_write_f34_command(CMD_ERASE_DISP_CONFIG);
+ fwu->config_block_count = fwu->disp_config_block_count;
+ break;
+ }
+ if (retval < 0)
+ return retval;
+
+ dev_dbg(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Erase command written\n",
+ __func__);
+
+ retval = fwu_wait_for_idle(ERASE_WAIT_MS);
+ if (retval < 0)
+ return retval;
+
+ dev_dbg(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Idle status detected\n",
+ __func__);
+
+write_config:
+ retval = fwu_write_configuration();
+ if (retval < 0)
+ return retval;
+
+ pr_notice("%s: Config written\n", __func__);
+
+ return retval;
+}
+
+static int fwu_start_write_config(void)
+{
+ int retval;
+ struct image_header header;
+
+ switch (fwu->config_area) {
+ case UI_CONFIG_AREA:
+ break;
+ case PERM_CONFIG_AREA:
+ if (!fwu->flash_properties.has_perm_config)
+ return -EINVAL;
+ break;
+ case BL_CONFIG_AREA:
+ if (!fwu->flash_properties.has_bl_config)
+ return -EINVAL;
+ break;
+ case DISP_CONFIG_AREA:
+ if (!fwu->flash_properties.has_display_config)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (fwu->ext_data_source)
+ fwu->config_data = fwu->ext_data_source;
+ else
+ return -EINVAL;
+
+ if (fwu->config_area == UI_CONFIG_AREA) {
+ parse_header(&header, fwu->ext_data_source);
+
+ if (header.config_size) {
+ fwu->config_data = fwu->ext_data_source +
+ FW_IMAGE_OFFSET +
+ header.image_size;
+ } else {
+ return -EINVAL;
+ }
+ }
+
+ pr_notice("%s: Start of write config process\n", __func__);
+
+ retval = fwu_do_write_config();
+ if (retval < 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Failed to write config\n",
+ __func__);
+ }
+
+ fwu->rmi4_data->reset_device(fwu->rmi4_data);
+
+ pr_notice("%s: End of write config process\n", __func__);
+
+ return retval;
+}
+
+static int fwu_do_read_config(void)
+{
+ int retval;
+ unsigned char block_offset[] = {0, 0};
+ unsigned short block_num;
+ unsigned short block_count;
+ unsigned short index = 0;
+
+ retval = fwu_enter_flash_prog();
+ if (retval < 0)
+ goto exit;
+
+ dev_dbg(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Entered flash prog mode\n",
+ __func__);
+
+ switch (fwu->config_area) {
+ case UI_CONFIG_AREA:
+ block_count = fwu->config_block_count;
+ break;
+ case PERM_CONFIG_AREA:
+ if (!fwu->flash_properties.has_perm_config) {
+ retval = -EINVAL;
+ goto exit;
+ }
+ block_count = fwu->perm_config_block_count;
+ break;
+ case BL_CONFIG_AREA:
+ if (!fwu->flash_properties.has_bl_config) {
+ retval = -EINVAL;
+ goto exit;
+ }
+ block_count = fwu->bl_config_block_count;
+ break;
+ case DISP_CONFIG_AREA:
+ if (!fwu->flash_properties.has_display_config) {
+ retval = -EINVAL;
+ goto exit;
+ }
+ block_count = fwu->disp_config_block_count;
+ break;
+ default:
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ fwu->config_size = fwu->block_size * block_count;
+
+ kfree(fwu->read_config_buf);
+ fwu->read_config_buf = kzalloc(fwu->config_size, GFP_KERNEL);
+
+ block_offset[1] |= (fwu->config_area << 5);
+
+ retval = fwu->fn_ptr->write(fwu->rmi4_data,
+ fwu->f34_fd.data_base_addr + BLOCK_NUMBER_OFFSET,
+ block_offset,
+ sizeof(block_offset));
+ if (retval < 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Failed to write to block number registers\n",
+ __func__);
+ goto exit;
+ }
+
+ for (block_num = 0; block_num < block_count; block_num++) {
+ retval = fwu_write_f34_command(CMD_READ_CONFIG_BLOCK);
+ if (retval < 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Failed to write read config command\n",
+ __func__);
+ goto exit;
+ }
+
+ retval = fwu_wait_for_idle(WRITE_WAIT_MS);
+ if (retval < 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Failed to wait for idle status\n",
+ __func__);
+ goto exit;
+ }
+
+ retval = fwu->fn_ptr->read(fwu->rmi4_data,
+ fwu->f34_fd.data_base_addr + BLOCK_DATA_OFFSET,
+ &fwu->read_config_buf[index],
+ fwu->block_size);
+ if (retval < 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Failed to read block data (block %d)\n",
+ __func__, block_num);
+ goto exit;
+ }
+
+ index += fwu->block_size;
+ }
+
+exit:
+ fwu->rmi4_data->reset_device(fwu->rmi4_data);
+
+ return retval;
+}
+
+int synaptics_fw_updater(unsigned char *fw_data)
+{
+ int retval;
+
+ if (!fwu)
+ return -ENODEV;
+
+ if (!fwu->initialized)
+ return -ENODEV;
+
+ fwu->ext_data_source = fw_data;
+ fwu->config_area = UI_CONFIG_AREA;
+
+ retval = fwu_start_reflash();
+
+ return retval;
+}
+EXPORT_SYMBOL(synaptics_fw_updater);
+
+static ssize_t fwu_sysfs_show_image(struct file *data_file,
+ struct kobject *kobj, struct bin_attribute *attributes,
+ char *buf, loff_t pos, size_t count)
+{
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ if (count < fwu->config_size) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Not enough space (%d bytes) in buffer\n",
+ __func__, count);
+ return -EINVAL;
+ }
+
+ memcpy(buf, fwu->read_config_buf, fwu->config_size);
+
+ return fwu->config_size;
+}
+
+static ssize_t fwu_sysfs_store_image(struct file *data_file,
+ struct kobject *kobj, struct bin_attribute *attributes,
+ char *buf, loff_t pos, size_t count)
+{
+ memcpy((void *)(&fwu->ext_data_source[fwu->data_pos]),
+ (const void *)buf,
+ count);
+
+ fwu->data_pos += count;
+
+ return count;
+}
+
+static ssize_t fwu_sysfs_do_reflash_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int retval;
+ unsigned int input;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ if (sscanf(buf, "%u", &input) != 1) {
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ if (input != 1) {
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ retval = synaptics_fw_updater(fwu->ext_data_source);
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to do reflash\n",
+ __func__);
+ goto exit;
+ }
+
+ retval = count;
+
+exit:
+ kfree(fwu->ext_data_source);
+ fwu->ext_data_source = NULL;
+ return retval;
+}
+
+static ssize_t fwu_sysfs_write_config_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int retval;
+ unsigned int input;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ if (sscanf(buf, "%u", &input) != 1) {
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ if (input != 1) {
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ retval = fwu_start_write_config();
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to write config\n",
+ __func__);
+ goto exit;
+ }
+
+ retval = count;
+
+exit:
+ kfree(fwu->ext_data_source);
+ fwu->ext_data_source = NULL;
+ return retval;
+}
+
+static ssize_t fwu_sysfs_read_config_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int retval;
+ unsigned int input;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ if (sscanf(buf, "%u", &input) != 1)
+ return -EINVAL;
+
+ if (input != 1)
+ return -EINVAL;
+
+ retval = fwu_do_read_config();
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to read config\n",
+ __func__);
+ return retval;
+ }
+
+ return count;
+}
+
+static ssize_t fwu_sysfs_config_area_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int retval;
+ unsigned long config_area;
+
+ retval = sstrtoul(buf, 10, &config_area);
+ if (retval)
+ return retval;
+
+ fwu->config_area = config_area;
+
+ return count;
+}
+
+static ssize_t fwu_sysfs_image_size_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int retval;
+ unsigned long size;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ retval = sstrtoul(buf, 10, &size);
+ if (retval)
+ return retval;
+
+ fwu->image_size = size;
+ fwu->data_pos = 0;
+
+ kfree(fwu->ext_data_source);
+ fwu->ext_data_source = kzalloc(fwu->image_size, GFP_KERNEL);
+ if (!fwu->ext_data_source) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to alloc mem for image data\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ return count;
+}
+
+static ssize_t fwu_sysfs_block_size_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n", fwu->block_size);
+}
+
+static ssize_t fwu_sysfs_firmware_block_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n", fwu->fw_block_count);
+}
+
+static ssize_t fwu_sysfs_configuration_block_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n", fwu->config_block_count);
+}
+
+static ssize_t fwu_sysfs_perm_config_block_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n", fwu->perm_config_block_count);
+}
+
+static ssize_t fwu_sysfs_bl_config_block_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n", fwu->bl_config_block_count);
+}
+
+static ssize_t fwu_sysfs_disp_config_block_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n", fwu->disp_config_block_count);
+}
+
+static void synaptics_rmi4_fwu_attn(struct synaptics_rmi4_data *rmi4_data,
+ unsigned char intr_mask)
+{
+ if (fwu->intr_mask & intr_mask)
+ fwu_read_f34_flash_status();
+
+ return;
+}
+
+static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval;
+ unsigned char attr_count;
+ struct pdt_properties pdt_props;
+
+ fwu = kzalloc(sizeof(*fwu), GFP_KERNEL);
+ if (!fwu) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to alloc mem for fwu\n",
+ __func__);
+ goto exit;
+ }
+
+ fwu->fn_ptr = kzalloc(sizeof(*(fwu->fn_ptr)), GFP_KERNEL);
+ if (!fwu->fn_ptr) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to alloc mem for fn_ptr\n",
+ __func__);
+ retval = -ENOMEM;
+ goto exit_free_fwu;
+ }
+
+ fwu->rmi4_data = rmi4_data;
+ fwu->fn_ptr->read = rmi4_data->i2c_read;
+ fwu->fn_ptr->write = rmi4_data->i2c_write;
+ fwu->fn_ptr->enable = rmi4_data->irq_enable;
+
+ retval = fwu->fn_ptr->read(rmi4_data,
+ PDT_PROPS,
+ pdt_props.data,
+ sizeof(pdt_props.data));
+ if (retval < 0) {
+ dev_dbg(&rmi4_data->i2c_client->dev,
+ "%s: Failed to read PDT properties, assuming 0x00\n",
+ __func__);
+ } else if (pdt_props.has_bsr) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Reflash for LTS not currently supported\n",
+ __func__);
+ goto exit_free_mem;
+ }
+
+ retval = fwu_scan_pdt();
+ if (retval < 0)
+ goto exit_free_mem;
+
+ fwu->productinfo1 = rmi4_data->rmi4_mod_info.product_info[0];
+ fwu->productinfo2 = rmi4_data->rmi4_mod_info.product_info[1];
+
+ memcpy(fwu->product_id, rmi4_data->rmi4_mod_info.product_id_string,
+ SYNAPTICS_RMI4_PRODUCT_ID_SIZE);
+ fwu->product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE] = 0;
+
+ dev_dbg(&rmi4_data->i2c_client->dev,
+ "%s: F01 product info: 0x%04x 0x%04x\n",
+ __func__, fwu->productinfo1, fwu->productinfo2);
+ dev_dbg(&rmi4_data->i2c_client->dev,
+ "%s: F01 product ID: %s\n",
+ __func__, fwu->product_id);
+
+ retval = fwu_read_f34_queries();
+ if (retval < 0)
+ goto exit_free_mem;
+
+ fwu->initialized = true;
+
+ retval = sysfs_create_bin_file(&rmi4_data->input_dev->dev.kobj,
+ &dev_attr_data);
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to create sysfs bin file\n",
+ __func__);
+ goto exit_free_mem;
+ }
+
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj,
+ &attrs[attr_count].attr);
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to create sysfs attributes\n",
+ __func__);
+ retval = -ENODEV;
+ goto exit_remove_attrs;
+ }
+ }
+
+ return 0;
+
+exit_remove_attrs:
+for (attr_count--; attr_count >= 0; attr_count--) {
+ sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
+ &attrs[attr_count].attr);
+}
+
+sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data);
+
+exit_free_mem:
+ kfree(fwu->fn_ptr);
+
+exit_free_fwu:
+ kfree(fwu);
+
+exit:
+ return 0;
+}
+
+static void synaptics_rmi4_fwu_remove(struct synaptics_rmi4_data *rmi4_data)
+{
+ unsigned char attr_count;
+
+ sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data);
+
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
+ &attrs[attr_count].attr);
+ }
+
+ kfree(fwu->fn_ptr);
+ kfree(fwu);
+
+ complete(&remove_complete);
+
+ return;
+}
+
+static int __init rmi4_fw_update_module_init(void)
+{
+ synaptics_rmi4_new_function(RMI_FW_UPDATER, true,
+ synaptics_rmi4_fwu_init,
+ synaptics_rmi4_fwu_remove,
+ synaptics_rmi4_fwu_attn);
+ return 0;
+}
+
+static void __exit rmi4_fw_update_module_exit(void)
+{
+ init_completion(&remove_complete);
+ synaptics_rmi4_new_function(RMI_FW_UPDATER, false,
+ synaptics_rmi4_fwu_init,
+ synaptics_rmi4_fwu_remove,
+ synaptics_rmi4_fwu_attn);
+ wait_for_completion(&remove_complete);
+ return;
+}
+
+module_init(rmi4_fw_update_module_init);
+module_exit(rmi4_fw_update_module_exit);
+
+MODULE_AUTHOR("Synaptics, Inc.");
+MODULE_DESCRIPTION("RMI4 FW Update Module");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(SYNAPTICS_RMI4_DRIVER_VERSION);
diff --git a/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.c b/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.c
new file mode 100644
index 000000000000..85530225abd2
--- /dev/null
+++ b/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.c
@@ -0,0 +1,2110 @@
+/*
+ * Synaptics RMI4 touchscreen driver
+ *
+ * Copyright (C) 2012 Synaptics Incorporated
+ *
+ * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
+ * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/input/synaptics_dsx.h>
+#include "synaptics_i2c_rmi4.h"
+#ifdef KERNEL_ABOVE_2_6_38
+#include <linux/input/mt.h>
+#endif
+
+#define DRIVER_NAME "synaptics_rmi4_i2c"
+#define INPUT_PHYS_NAME "synaptics_rmi4_i2c/input0"
+
+#ifdef KERNEL_ABOVE_2_6_38
+#define TYPE_B_PROTOCOL
+#endif
+
+#define NO_0D_WHILE_2D
+/*
+#define REPORT_2D_Z
+*/
+#define REPORT_2D_W
+
+#define RPT_TYPE (1 << 0)
+#define RPT_X_LSB (1 << 1)
+#define RPT_X_MSB (1 << 2)
+#define RPT_Y_LSB (1 << 3)
+#define RPT_Y_MSB (1 << 4)
+#define RPT_Z (1 << 5)
+#define RPT_WX (1 << 6)
+#define RPT_WY (1 << 7)
+#define RPT_DEFAULT (RPT_TYPE | RPT_X_LSB | RPT_X_MSB | RPT_Y_LSB | RPT_Y_MSB)
+
+#define EXP_FN_DET_INTERVAL 1000 /* ms */
+#define POLLING_PERIOD 1 /* ms */
+#define SYN_I2C_RETRY_TIMES 10
+#define MAX_ABS_MT_TOUCH_MAJOR 15
+
+#define F01_STD_QUERY_LEN 21
+#define F01_BUID_ID_OFFSET 18
+#define F11_STD_QUERY_LEN 9
+#define F11_STD_CTRL_LEN 10
+#define F11_STD_DATA_LEN 12
+
+#define NORMAL_OPERATION (0 << 0)
+#define SENSOR_SLEEP (1 << 0)
+#define NO_SLEEP_OFF (0 << 3)
+#define NO_SLEEP_ON (1 << 3)
+
+static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data,
+ unsigned short addr, unsigned char *data,
+ unsigned short length);
+
+static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data,
+ unsigned short addr, unsigned char *data,
+ unsigned short length);
+
+static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static ssize_t synaptics_rmi4_full_pm_cycle_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t synaptics_rmi4_full_pm_cycle_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static void synaptics_rmi4_early_suspend(struct early_suspend *h);
+
+static void synaptics_rmi4_late_resume(struct early_suspend *h);
+
+static int synaptics_rmi4_suspend(struct device *dev);
+
+static int synaptics_rmi4_resume(struct device *dev);
+#endif
+
+static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t synaptics_rmi4_f01_productinfo_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t synaptics_rmi4_f01_buildid_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t synaptics_rmi4_f01_flashprog_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t synaptics_rmi4_0dbutton_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+struct synaptics_rmi4_f01_device_status {
+ union {
+ struct {
+ unsigned char status_code:4;
+ unsigned char reserved:2;
+ unsigned char flash_prog:1;
+ unsigned char unconfigured:1;
+ } __packed;
+ unsigned char data[1];
+ };
+};
+
+struct synaptics_rmi4_f1a_query {
+ union {
+ struct {
+ unsigned char max_button_count:3;
+ unsigned char reserved:5;
+ unsigned char has_general_control:1;
+ unsigned char has_interrupt_enable:1;
+ unsigned char has_multibutton_select:1;
+ unsigned char has_tx_rx_map:1;
+ unsigned char has_perbutton_threshold:1;
+ unsigned char has_release_threshold:1;
+ unsigned char has_strongestbtn_hysteresis:1;
+ unsigned char has_filter_strength:1;
+ } __packed;
+ unsigned char data[2];
+ };
+};
+
+struct synaptics_rmi4_f1a_control_0 {
+ union {
+ struct {
+ unsigned char multibutton_report:2;
+ unsigned char filter_mode:2;
+ unsigned char reserved:4;
+ } __packed;
+ unsigned char data[1];
+ };
+};
+
+struct synaptics_rmi4_f1a_control_3_4 {
+ unsigned char transmitterbutton;
+ unsigned char receiverbutton;
+};
+
+struct synaptics_rmi4_f1a_control {
+ struct synaptics_rmi4_f1a_control_0 general_control;
+ unsigned char *button_int_enable;
+ unsigned char *multi_button;
+ struct synaptics_rmi4_f1a_control_3_4 *electrode_map;
+ unsigned char *button_threshold;
+ unsigned char button_release_threshold;
+ unsigned char strongest_button_hysteresis;
+ unsigned char filter_strength;
+};
+
+struct synaptics_rmi4_f1a_handle {
+ int button_bitmask_size;
+ unsigned char button_count;
+ unsigned char valid_button_count;
+ unsigned char *button_data_buffer;
+ unsigned char *button_map;
+ struct synaptics_rmi4_f1a_query button_query;
+ struct synaptics_rmi4_f1a_control button_control;
+};
+
+struct synaptics_rmi4_exp_fn {
+ enum exp_fn fn_type;
+ bool inserted;
+ int (*func_init)(struct synaptics_rmi4_data *rmi4_data);
+ void (*func_remove)(struct synaptics_rmi4_data *rmi4_data);
+ void (*func_attn)(struct synaptics_rmi4_data *rmi4_data,
+ unsigned char intr_mask);
+ struct list_head link;
+};
+
+static struct device_attribute attrs[] = {
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ __ATTR(full_pm_cycle, (S_IRUGO | S_IWUGO),
+ synaptics_rmi4_full_pm_cycle_show,
+ synaptics_rmi4_full_pm_cycle_store),
+#endif
+ __ATTR(reset, S_IWUGO,
+ synaptics_rmi4_show_error,
+ synaptics_rmi4_f01_reset_store),
+ __ATTR(productinfo, S_IRUGO,
+ synaptics_rmi4_f01_productinfo_show,
+ synaptics_rmi4_store_error),
+ __ATTR(buildid, S_IRUGO,
+ synaptics_rmi4_f01_buildid_show,
+ synaptics_rmi4_store_error),
+ __ATTR(flashprog, S_IRUGO,
+ synaptics_rmi4_f01_flashprog_show,
+ synaptics_rmi4_store_error),
+ __ATTR(0dbutton, (S_IRUGO | S_IWUGO),
+ synaptics_rmi4_0dbutton_show,
+ synaptics_rmi4_0dbutton_store),
+};
+
+static bool exp_fn_inited;
+static struct mutex exp_fn_list_mutex;
+static struct list_head exp_fn_list;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static ssize_t synaptics_rmi4_full_pm_cycle_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ rmi4_data->full_pm_cycle);
+}
+
+static ssize_t synaptics_rmi4_full_pm_cycle_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int input;
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+
+ if (sscanf(buf, "%u", &input) != 1)
+ return -EINVAL;
+
+ rmi4_data->full_pm_cycle = input > 0 ? 1 : 0;
+
+ return count;
+}
+#endif
+
+static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int retval;
+ unsigned int reset;
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+
+ if (sscanf(buf, "%u", &reset) != 1)
+ return -EINVAL;
+
+ if (reset != 1)
+ return -EINVAL;
+
+ retval = synaptics_rmi4_reset_device(rmi4_data);
+ if (retval < 0) {
+ dev_err(dev,
+ "%s: Failed to issue reset command, error = %d\n",
+ __func__, retval);
+ return retval;
+ }
+
+ return count;
+}
+
+static ssize_t synaptics_rmi4_f01_productinfo_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "0x%02x 0x%02x\n",
+ (rmi4_data->rmi4_mod_info.product_info[0]),
+ (rmi4_data->rmi4_mod_info.product_info[1]));
+}
+
+static ssize_t synaptics_rmi4_f01_buildid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned int build_id;
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+ struct synaptics_rmi4_device_info *rmi;
+
+ rmi = &(rmi4_data->rmi4_mod_info);
+
+ build_id = (unsigned int)rmi->build_id[0] +
+ (unsigned int)rmi->build_id[1] * 0x100 +
+ (unsigned int)rmi->build_id[2] * 0x10000;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ build_id);
+}
+
+static ssize_t synaptics_rmi4_f01_flashprog_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int retval;
+ struct synaptics_rmi4_f01_device_status device_status;
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ rmi4_data->f01_data_base_addr,
+ device_status.data,
+ sizeof(device_status.data));
+ if (retval < 0) {
+ dev_err(dev,
+ "%s: Failed to read device status, error = %d\n",
+ __func__, retval);
+ return retval;
+ }
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ device_status.flash_prog);
+}
+
+static ssize_t synaptics_rmi4_0dbutton_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ rmi4_data->button_0d_enabled);
+}
+
+static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int retval;
+ unsigned int input;
+ unsigned char ii;
+ unsigned char intr_enable;
+ struct synaptics_rmi4_fn *fhandler;
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+ struct synaptics_rmi4_device_info *rmi;
+
+ rmi = &(rmi4_data->rmi4_mod_info);
+
+ if (sscanf(buf, "%u", &input) != 1)
+ return -EINVAL;
+
+ input = input > 0 ? 1 : 0;
+
+ if (rmi4_data->button_0d_enabled == input)
+ return count;
+
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) {
+ ii = fhandler->intr_reg_num;
+
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ rmi4_data->f01_ctrl_base_addr + 1 + ii,
+ &intr_enable,
+ sizeof(intr_enable));
+ if (retval < 0)
+ return retval;
+
+ if (input == 1)
+ intr_enable |= fhandler->intr_mask;
+ else
+ intr_enable &= ~fhandler->intr_mask;
+
+ retval = synaptics_rmi4_i2c_write(rmi4_data,
+ rmi4_data->f01_ctrl_base_addr + 1 + ii,
+ &intr_enable,
+ sizeof(intr_enable));
+ if (retval < 0)
+ return retval;
+ }
+ }
+
+ rmi4_data->button_0d_enabled = input;
+
+ return count;
+}
+
+ /**
+ * synaptics_rmi4_set_page()
+ *
+ * Called by synaptics_rmi4_i2c_read() and synaptics_rmi4_i2c_write().
+ *
+ * This function writes to the page select register to switch to the
+ * assigned page.
+ */
+static int synaptics_rmi4_set_page(struct synaptics_rmi4_data *rmi4_data,
+ unsigned int address)
+{
+ int retval = 0;
+ unsigned char retry;
+ unsigned char buf[PAGE_SELECT_LEN];
+ unsigned char page;
+ struct i2c_client *i2c = rmi4_data->i2c_client;
+
+ page = ((address >> 8) & MASK_8BIT);
+ if (page != rmi4_data->current_page) {
+ buf[0] = MASK_8BIT;
+ buf[1] = page;
+ for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
+ retval = i2c_master_send(i2c, buf, PAGE_SELECT_LEN);
+ if (retval != PAGE_SELECT_LEN) {
+ dev_err(&i2c->dev,
+ "%s: I2C retry %d\n",
+ __func__, retry + 1);
+ msleep(20);
+ } else {
+ rmi4_data->current_page = page;
+ break;
+ }
+ }
+ } else
+ return PAGE_SELECT_LEN;
+ return (retval == PAGE_SELECT_LEN) ? retval : -EIO;
+}
+
+ /**
+ * synaptics_rmi4_i2c_read()
+ *
+ * Called by various functions in this driver, and also exported to
+ * other expansion Function modules such as rmi_dev.
+ *
+ * This function reads data of an arbitrary length from the sensor,
+ * starting from an assigned register address of the sensor, via I2C
+ * with a retry mechanism.
+ */
+static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data,
+ unsigned short addr, unsigned char *data, unsigned short length)
+{
+ int retval;
+ unsigned char retry;
+ unsigned char buf;
+ struct i2c_msg msg[] = {
+ {
+ .addr = rmi4_data->i2c_client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &buf,
+ },
+ {
+ .addr = rmi4_data->i2c_client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = data,
+ },
+ };
+
+ buf = addr & MASK_8BIT;
+
+ mutex_lock(&(rmi4_data->rmi4_io_ctrl_mutex));
+
+ retval = synaptics_rmi4_set_page(rmi4_data, addr);
+ if (retval != PAGE_SELECT_LEN)
+ goto exit;
+
+ for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
+ if (i2c_transfer(rmi4_data->i2c_client->adapter, msg, 2) == 2) {
+ retval = length;
+ break;
+ }
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: I2C retry %d\n",
+ __func__, retry + 1);
+ msleep(20);
+ }
+
+ if (retry == SYN_I2C_RETRY_TIMES) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: I2C read over retry limit\n",
+ __func__);
+ retval = -EIO;
+ }
+
+exit:
+ mutex_unlock(&(rmi4_data->rmi4_io_ctrl_mutex));
+
+ return retval;
+}
+
+ /**
+ * synaptics_rmi4_i2c_write()
+ *
+ * Called by various functions in this driver, and also exported to
+ * other expansion Function modules such as rmi_dev.
+ *
+ * This function writes data of an arbitrary length to the sensor,
+ * starting from an assigned register address of the sensor, via I2C with
+ * a retry mechanism.
+ */
+static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data,
+ unsigned short addr, unsigned char *data, unsigned short length)
+{
+ int retval;
+ unsigned char retry;
+ unsigned char buf[length + 1];
+ struct i2c_msg msg[] = {
+ {
+ .addr = rmi4_data->i2c_client->addr,
+ .flags = 0,
+ .len = length + 1,
+ .buf = buf,
+ }
+ };
+
+ mutex_lock(&(rmi4_data->rmi4_io_ctrl_mutex));
+
+ retval = synaptics_rmi4_set_page(rmi4_data, addr);
+ if (retval != PAGE_SELECT_LEN)
+ goto exit;
+
+ buf[0] = addr & MASK_8BIT;
+ memcpy(&buf[1], &data[0], length);
+
+ for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
+ if (i2c_transfer(rmi4_data->i2c_client->adapter, msg, 1) == 1) {
+ retval = length;
+ break;
+ }
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: I2C retry %d\n",
+ __func__, retry + 1);
+ msleep(20);
+ }
+
+ if (retry == SYN_I2C_RETRY_TIMES) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: I2C write over retry limit\n",
+ __func__);
+ retval = -EIO;
+ }
+
+exit:
+ mutex_unlock(&(rmi4_data->rmi4_io_ctrl_mutex));
+
+ return retval;
+}
+
+ /**
+ * synaptics_rmi4_f11_abs_report()
+ *
+ * Called by synaptics_rmi4_report_touch() when valid Function $11
+ * finger data has been detected.
+ *
+ * This function reads the Function $11 data registers, determines the
+ * status of each finger supported by the Function, processes any
+ * necessary coordinate manipulation, reports the finger data to
+ * the input subsystem, and returns the number of fingers detected.
+ */
+static int synaptics_rmi4_f11_abs_report(struct synaptics_rmi4_data *rmi4_data,
+ struct synaptics_rmi4_fn *fhandler)
+{
+ int retval;
+ unsigned char touch_count = 0; /* number of touch points */
+ unsigned char reg_index;
+ unsigned char finger;
+ unsigned char fingers_supported;
+ unsigned char num_of_finger_status_regs;
+ unsigned char finger_shift;
+ unsigned char finger_status;
+ unsigned char data_reg_blk_size;
+ unsigned char finger_status_reg[3];
+ unsigned char data[F11_STD_DATA_LEN];
+ unsigned short data_addr;
+ unsigned short data_offset;
+ int x;
+ int y;
+ int wx;
+ int wy;
+
+ /*
+ * The number of finger status registers is determined by the
+ * maximum number of fingers supported - 2 bits per finger. So
+ * the number of finger status registers to read is:
+ * register_count = ceil(max_num_of_fingers / 4)
+ */
+ fingers_supported = fhandler->num_of_data_points;
+ num_of_finger_status_regs = (fingers_supported + 3) / 4;
+ data_addr = fhandler->full_addr.data_base;
+ data_reg_blk_size = fhandler->size_of_data_register_block;
+
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ data_addr,
+ finger_status_reg,
+ num_of_finger_status_regs);
+ if (retval < 0)
+ return 0;
+
+ for (finger = 0; finger < fingers_supported; finger++) {
+ reg_index = finger / 4;
+ finger_shift = (finger % 4) * 2;
+ finger_status = (finger_status_reg[reg_index] >> finger_shift)
+ & MASK_2BIT;
+
+ /*
+ * Each 2-bit finger status field represents the following:
+ * 00 = finger not present
+ * 01 = finger present and data accurate
+ * 10 = finger present but data may be inaccurate
+ * 11 = reserved
+ */
+#ifdef TYPE_B_PROTOCOL
+ input_mt_slot(rmi4_data->input_dev, finger);
+ input_mt_report_slot_state(rmi4_data->input_dev,
+ MT_TOOL_FINGER, finger_status != 0);
+#endif
+
+ if (finger_status) {
+ data_offset = data_addr +
+ num_of_finger_status_regs +
+ (finger * data_reg_blk_size);
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ data_offset,
+ data,
+ data_reg_blk_size);
+ if (retval < 0)
+ return 0;
+
+ x = (data[0] << 4) | (data[2] & MASK_4BIT);
+ y = (data[1] << 4) | ((data[2] >> 4) & MASK_4BIT);
+ wx = (data[3] & MASK_4BIT);
+ wy = (data[3] >> 4) & MASK_4BIT;
+
+ if (rmi4_data->board->x_flip)
+ x = rmi4_data->sensor_max_x - x;
+ if (rmi4_data->board->y_flip)
+ y = rmi4_data->sensor_max_y - y;
+
+ dev_dbg(&rmi4_data->i2c_client->dev,
+ "%s: Finger %d:\n"
+ "status = 0x%02x\n"
+ "x = %d\n"
+ "y = %d\n"
+ "wx = %d\n"
+ "wy = %d\n",
+ __func__, finger,
+ finger_status,
+ x, y, wx, wy);
+
+#ifdef TYPE_B_PROTOCOL
+ input_report_abs(rmi4_data->input_dev,
+ ABS_MT_POSITION_X, x);
+ input_report_abs(rmi4_data->input_dev,
+ ABS_MT_POSITION_Y, y);
+#ifdef REPORT_2D_W
+ input_report_abs(rmi4_data->input_dev,
+ ABS_MT_TOUCH_MAJOR, max(wx, wy));
+ input_report_abs(rmi4_data->input_dev,
+ ABS_MT_TOUCH_MINOR, min(wx, wy));
+#endif
+#else
+ input_report_abs(rmi4_data->input_dev,
+ ABS_MT_POSITION_X, x);
+ input_report_abs(rmi4_data->input_dev,
+ ABS_MT_POSITION_Y, y);
+#ifdef REPORT_2D_W
+ input_report_abs(rmi4_data->input_dev,
+ ABS_MT_TOUCH_MAJOR, max(wx, wy));
+ input_report_abs(rmi4_data->input_dev,
+ ABS_MT_TOUCH_MINOR, min(wx, wy));
+#endif
+ input_mt_sync(rmi4_data->input_dev);
+#endif
+ touch_count++;
+ }
+ }
+
+#ifndef TYPE_B_PROTOCOL
+ if (!touch_count)
+ input_mt_sync(rmi4_data->input_dev);
+#else
+ /* sync after groups of events */
+ #ifdef KERNEL_ABOVE_3_7
+ input_mt_sync_frame(rmi4_data->input_dev);
+ #endif
+#endif
+
+ input_sync(rmi4_data->input_dev);
+
+ return touch_count;
+}
+
+static void synaptics_rmi4_f1a_report(struct synaptics_rmi4_data *rmi4_data,
+ struct synaptics_rmi4_fn *fhandler)
+{
+ int retval;
+ unsigned char button;
+ unsigned char index;
+ unsigned char shift;
+ unsigned char status;
+ unsigned char *data;
+ unsigned short data_addr = fhandler->full_addr.data_base;
+ struct synaptics_rmi4_f1a_handle *f1a = fhandler->data;
+ static unsigned char do_once = 1;
+ static bool current_status[MAX_NUMBER_OF_BUTTONS];
+#ifdef NO_0D_WHILE_2D
+ static bool before_2d_status[MAX_NUMBER_OF_BUTTONS];
+ static bool while_2d_status[MAX_NUMBER_OF_BUTTONS];
+#endif
+
+ if (do_once) {
+ memset(current_status, 0, sizeof(current_status));
+#ifdef NO_0D_WHILE_2D
+ memset(before_2d_status, 0, sizeof(before_2d_status));
+ memset(while_2d_status, 0, sizeof(while_2d_status));
+#endif
+ do_once = 0;
+ }
+
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ data_addr,
+ f1a->button_data_buffer,
+ f1a->button_bitmask_size);
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to read button data registers\n",
+ __func__);
+ return;
+ }
+
+ data = f1a->button_data_buffer;
+
+ for (button = 0; button < f1a->valid_button_count; button++) {
+ index = button / 8;
+ shift = button % 8;
+ status = ((data[index] >> shift) & MASK_1BIT);
+
+ if (current_status[button] == status)
+ continue;
+ else
+ current_status[button] = status;
+
+ dev_dbg(&rmi4_data->i2c_client->dev,
+ "%s: Button %d (code %d) ->%d\n",
+ __func__, button,
+ f1a->button_map[button],
+ status);
+#ifdef NO_0D_WHILE_2D
+ if (rmi4_data->fingers_on_2d == false) {
+ if (status == 1) {
+ before_2d_status[button] = 1;
+ } else {
+ if (while_2d_status[button] == 1) {
+ while_2d_status[button] = 0;
+ continue;
+ } else {
+ before_2d_status[button] = 0;
+ }
+ }
+ input_report_key(rmi4_data->input_dev,
+ f1a->button_map[button],
+ status);
+ } else {
+ if (before_2d_status[button] == 1) {
+ before_2d_status[button] = 0;
+ input_report_key(rmi4_data->input_dev,
+ f1a->button_map[button],
+ status);
+ } else {
+ if (status == 1)
+ while_2d_status[button] = 1;
+ else
+ while_2d_status[button] = 0;
+ }
+ }
+#else
+ input_report_key(rmi4_data->input_dev,
+ f1a->button_map[button],
+ status);
+#endif
+ }
+
+ input_sync(rmi4_data->input_dev);
+
+ return;
+}
+
+ /**
+ * synaptics_rmi4_report_touch()
+ *
+ * Called by synaptics_rmi4_sensor_report().
+ *
+ * This function calls the appropriate finger data reporting function
+ * based on the function handler it receives and returns the number of
+ * fingers detected.
+ */
+static void synaptics_rmi4_report_touch(struct synaptics_rmi4_data *rmi4_data,
+ struct synaptics_rmi4_fn *fhandler,
+ unsigned char *touch_count)
+{
+ unsigned char touch_count_2d;
+
+ dev_dbg(&rmi4_data->i2c_client->dev,
+ "%s: Function %02x reporting\n",
+ __func__, fhandler->fn_number);
+
+ switch (fhandler->fn_number) {
+ case SYNAPTICS_RMI4_F11:
+ touch_count_2d = synaptics_rmi4_f11_abs_report(rmi4_data,
+ fhandler);
+
+ *touch_count += touch_count_2d;
+
+ if (touch_count_2d)
+ rmi4_data->fingers_on_2d = true;
+ else
+ rmi4_data->fingers_on_2d = false;
+ break;
+
+ case SYNAPTICS_RMI4_F1A:
+ synaptics_rmi4_f1a_report(rmi4_data, fhandler);
+ break;
+
+ default:
+ break;
+ }
+
+ return;
+}
+
+ /**
+ * synaptics_rmi4_sensor_report()
+ *
+ * Called by synaptics_rmi4_irq().
+ *
+ * This function determines the interrupt source(s) from the sensor
+ * and calls synaptics_rmi4_report_touch() with the appropriate
+ * function handler for each function with valid data inputs.
+ */
+static int synaptics_rmi4_sensor_report(struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval;
+ unsigned char touch_count = 0;
+ unsigned char intr[MAX_INTR_REGISTERS];
+ struct synaptics_rmi4_fn *fhandler;
+ struct synaptics_rmi4_exp_fn *exp_fhandler;
+ struct synaptics_rmi4_device_info *rmi;
+
+ rmi = &(rmi4_data->rmi4_mod_info);
+
+ /*
+ * Get interrupt status information from F01 Data1 register to
+ * determine the source(s) that are flagging the interrupt.
+ */
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ rmi4_data->f01_data_base_addr + 1,
+ intr,
+ rmi4_data->num_of_intr_regs);
+ if (retval < 0)
+ return retval;
+
+ /*
+ * Traverse the function handler list and service the source(s)
+ * of the interrupt accordingly.
+ */
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->num_of_data_sources) {
+ if (fhandler->intr_mask &
+ intr[fhandler->intr_reg_num]) {
+ synaptics_rmi4_report_touch(rmi4_data,
+ fhandler, &touch_count);
+ }
+ }
+ }
+
+ mutex_lock(&exp_fn_list_mutex);
+ if (!list_empty(&exp_fn_list)) {
+ list_for_each_entry(exp_fhandler, &exp_fn_list, link) {
+ if (exp_fhandler->inserted &&
+ (exp_fhandler->func_attn != NULL))
+ exp_fhandler->func_attn(rmi4_data, intr[0]);
+ }
+ }
+ mutex_unlock(&exp_fn_list_mutex);
+
+ return touch_count;
+}
+
+ /**
+ * synaptics_rmi4_irq()
+ *
+ * Called by the kernel when an interrupt occurs (when the sensor
+ * asserts the attention irq).
+ *
+ * This function is the ISR thread and handles the acquisition
+ * and the reporting of finger data when the presence of fingers
+ * is detected.
+ */
+static irqreturn_t synaptics_rmi4_irq(int irq, void *data)
+{
+ struct synaptics_rmi4_data *rmi4_data = data;
+
+ synaptics_rmi4_sensor_report(rmi4_data);
+
+ return IRQ_HANDLED;
+}
+
+ /**
+ * synaptics_rmi4_irq_enable()
+ *
+ * Called by synaptics_rmi4_probe() and the power management functions
+ * in this driver and also exported to other expansion Function modules
+ * such as rmi_dev.
+ *
+ * This function handles the enabling and disabling of the attention
+ * irq including the setting up of the ISR thread.
+ */
+static int synaptics_rmi4_irq_enable(struct synaptics_rmi4_data *rmi4_data,
+ bool enable)
+{
+ int retval = 0;
+ unsigned char intr_status;
+ const struct synaptics_rmi4_platform_data *platform_data =
+ rmi4_data->i2c_client->dev.platform_data;
+
+ if (enable) {
+ if (rmi4_data->irq_enabled)
+ return retval;
+
+ /* Clear interrupts first */
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ rmi4_data->f01_data_base_addr + 1,
+ &intr_status,
+ rmi4_data->num_of_intr_regs);
+ if (retval < 0)
+ return retval;
+
+ retval = request_threaded_irq(rmi4_data->irq, NULL,
+ synaptics_rmi4_irq, platform_data->irq_flags,
+ DRIVER_NAME, rmi4_data);
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to create irq thread\n",
+ __func__);
+ return retval;
+ }
+
+ rmi4_data->irq_enabled = true;
+ } else {
+ if (rmi4_data->irq_enabled) {
+ disable_irq(rmi4_data->irq);
+ free_irq(rmi4_data->irq, rmi4_data);
+ rmi4_data->irq_enabled = false;
+ }
+ }
+
+ return retval;
+}
+
+ /**
+ * synaptics_rmi4_f11_init()
+ *
+ * Called by synaptics_rmi4_query_device().
+ *
+ * This funtion parses information from the Function 11 registers
+ * and determines the number of fingers supported, x and y data ranges,
+ * offset to the associated interrupt status register, interrupt bit
+ * mask, and gathers finger data acquisition capabilities from the query
+ * registers.
+ */
+static int synaptics_rmi4_f11_init(struct synaptics_rmi4_data *rmi4_data,
+ struct synaptics_rmi4_fn *fhandler,
+ struct synaptics_rmi4_fn_desc *fd,
+ unsigned int intr_count)
+{
+ int retval;
+ unsigned char ii;
+ unsigned char intr_offset;
+ unsigned char abs_data_size;
+ unsigned char abs_data_blk_size;
+ unsigned char query[F11_STD_QUERY_LEN];
+ unsigned char control[F11_STD_CTRL_LEN];
+
+ fhandler->fn_number = fd->fn_number;
+ fhandler->num_of_data_sources = fd->intr_src_count;
+
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ fhandler->full_addr.query_base,
+ query,
+ sizeof(query));
+ if (retval < 0)
+ return retval;
+
+ /* Maximum number of fingers supported */
+ if ((query[1] & MASK_3BIT) <= 4)
+ fhandler->num_of_data_points = (query[1] & MASK_3BIT) + 1;
+ else if ((query[1] & MASK_3BIT) == 5)
+ fhandler->num_of_data_points = 10;
+
+ rmi4_data->num_of_fingers = fhandler->num_of_data_points;
+
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ fhandler->full_addr.ctrl_base,
+ control,
+ sizeof(control));
+ if (retval < 0)
+ return retval;
+
+ /* Maximum x and y */
+ rmi4_data->sensor_max_x = ((control[6] & MASK_8BIT) << 0) |
+ ((control[7] & MASK_4BIT) << 8);
+ rmi4_data->sensor_max_y = ((control[8] & MASK_8BIT) << 0) |
+ ((control[9] & MASK_4BIT) << 8);
+ dev_dbg(&rmi4_data->i2c_client->dev,
+ "%s: Function %02x max x = %d max y = %d\n",
+ __func__, fhandler->fn_number,
+ rmi4_data->sensor_max_x,
+ rmi4_data->sensor_max_y);
+
+ fhandler->intr_reg_num = (intr_count + 7) / 8;
+ if (fhandler->intr_reg_num != 0)
+ fhandler->intr_reg_num -= 1;
+
+ /* Set an enable bit for each data source */
+ intr_offset = intr_count % 8;
+ fhandler->intr_mask = 0;
+ for (ii = intr_offset;
+ ii < ((fd->intr_src_count & MASK_3BIT) +
+ intr_offset);
+ ii++)
+ fhandler->intr_mask |= 1 << ii;
+
+ abs_data_size = query[5] & MASK_2BIT;
+ abs_data_blk_size = 3 + (2 * (abs_data_size == 0 ? 1 : 0));
+ fhandler->size_of_data_register_block = abs_data_blk_size;
+
+ return retval;
+}
+
+static int synaptics_rmi4_f1a_alloc_mem(struct synaptics_rmi4_data *rmi4_data,
+ struct synaptics_rmi4_fn *fhandler)
+{
+ int retval;
+ struct synaptics_rmi4_f1a_handle *f1a;
+
+ f1a = kzalloc(sizeof(*f1a), GFP_KERNEL);
+ if (!f1a) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to alloc mem for function handle\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ fhandler->data = (void *)f1a;
+
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ fhandler->full_addr.query_base,
+ f1a->button_query.data,
+ sizeof(f1a->button_query.data));
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to read query registers\n",
+ __func__);
+ return retval;
+ }
+
+ f1a->button_count = f1a->button_query.max_button_count + 1;
+ f1a->button_bitmask_size = (f1a->button_count + 7) / 8;
+
+ f1a->button_data_buffer = kcalloc(f1a->button_bitmask_size,
+ sizeof(*(f1a->button_data_buffer)), GFP_KERNEL);
+ if (!f1a->button_data_buffer) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to alloc mem for data buffer\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ f1a->button_map = kcalloc(f1a->button_count,
+ sizeof(*(f1a->button_map)), GFP_KERNEL);
+ if (!f1a->button_map) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to alloc mem for button map\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int synaptics_rmi4_capacitance_button_map(
+ struct synaptics_rmi4_data *rmi4_data,
+ struct synaptics_rmi4_fn *fhandler)
+{
+ unsigned char ii;
+ struct synaptics_rmi4_f1a_handle *f1a = fhandler->data;
+ const struct synaptics_rmi4_platform_data *pdata = rmi4_data->board;
+
+ if (!pdata->capacitance_button_map) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: capacitance_button_map is \
+ NULL in board file\n",
+ __func__);
+ return -ENODEV;
+ } else if (!pdata->capacitance_button_map->map) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Button map is missing in board file\n",
+ __func__);
+ return -ENODEV;
+ } else {
+ if (pdata->capacitance_button_map->nbuttons !=
+ f1a->button_count) {
+ f1a->valid_button_count = min(f1a->button_count,
+ pdata->capacitance_button_map->nbuttons);
+ } else {
+ f1a->valid_button_count = f1a->button_count;
+ }
+
+ for (ii = 0; ii < f1a->valid_button_count; ii++)
+ f1a->button_map[ii] =
+ pdata->capacitance_button_map->map[ii];
+ }
+
+ return 0;
+}
+
+static void synaptics_rmi4_f1a_kfree(struct synaptics_rmi4_fn *fhandler)
+{
+ struct synaptics_rmi4_f1a_handle *f1a = fhandler->data;
+
+ if (f1a) {
+ kfree(f1a->button_data_buffer);
+ kfree(f1a->button_map);
+ kfree(f1a);
+ fhandler->data = NULL;
+ }
+
+ return;
+}
+
+static int synaptics_rmi4_f1a_init(struct synaptics_rmi4_data *rmi4_data,
+ struct synaptics_rmi4_fn *fhandler,
+ struct synaptics_rmi4_fn_desc *fd,
+ unsigned int intr_count)
+{
+ int retval;
+ unsigned char ii;
+ unsigned short intr_offset;
+
+ fhandler->fn_number = fd->fn_number;
+ fhandler->num_of_data_sources = fd->intr_src_count;
+
+ fhandler->intr_reg_num = (intr_count + 7) / 8;
+ if (fhandler->intr_reg_num != 0)
+ fhandler->intr_reg_num -= 1;
+
+ /* Set an enable bit for each data source */
+ intr_offset = intr_count % 8;
+ fhandler->intr_mask = 0;
+ for (ii = intr_offset;
+ ii < ((fd->intr_src_count & MASK_3BIT) +
+ intr_offset);
+ ii++)
+ fhandler->intr_mask |= 1 << ii;
+
+ retval = synaptics_rmi4_f1a_alloc_mem(rmi4_data, fhandler);
+ if (retval < 0)
+ goto error_exit;
+
+ retval = synaptics_rmi4_capacitance_button_map(rmi4_data, fhandler);
+ if (retval < 0)
+ goto error_exit;
+
+ rmi4_data->button_0d_enabled = 1;
+
+ return 0;
+
+error_exit:
+ synaptics_rmi4_f1a_kfree(fhandler);
+
+ return retval;
+}
+
+static int synaptics_rmi4_alloc_fh(struct synaptics_rmi4_fn **fhandler,
+ struct synaptics_rmi4_fn_desc *rmi_fd, int page_number)
+{
+ *fhandler = kmalloc(sizeof(**fhandler), GFP_KERNEL);
+ if (!(*fhandler))
+ return -ENOMEM;
+
+ (*fhandler)->full_addr.data_base =
+ (rmi_fd->data_base_addr |
+ (page_number << 8));
+ (*fhandler)->full_addr.ctrl_base =
+ (rmi_fd->ctrl_base_addr |
+ (page_number << 8));
+ (*fhandler)->full_addr.cmd_base =
+ (rmi_fd->cmd_base_addr |
+ (page_number << 8));
+ (*fhandler)->full_addr.query_base =
+ (rmi_fd->query_base_addr |
+ (page_number << 8));
+
+ return 0;
+}
+
+ /**
+ * synaptics_rmi4_query_device()
+ *
+ * Called by synaptics_rmi4_probe().
+ *
+ * This funtion scans the page description table, records the offsets
+ * to the register types of Function $01, sets up the function handlers
+ * for Function $11 and Function $12, determines the number of interrupt
+ * sources from the sensor, adds valid Functions with data inputs to the
+ * Function linked list, parses information from the query registers of
+ * Function $01, and enables the interrupt sources from the valid Functions
+ * with data inputs.
+ */
+static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval;
+ unsigned char ii;
+ unsigned char page_number;
+ unsigned char intr_count = 0;
+ unsigned char data_sources = 0;
+ unsigned char f01_query[F01_STD_QUERY_LEN];
+ unsigned short pdt_entry_addr;
+ unsigned short intr_addr;
+ struct synaptics_rmi4_f01_device_status status;
+ struct synaptics_rmi4_fn_desc rmi_fd;
+ struct synaptics_rmi4_fn *fhandler;
+ struct synaptics_rmi4_device_info *rmi;
+
+ rmi = &(rmi4_data->rmi4_mod_info);
+
+ INIT_LIST_HEAD(&rmi->support_fn_list);
+
+ /* Scan the page description tables of the pages to service */
+ for (page_number = 0; page_number < PAGES_TO_SERVICE; page_number++) {
+ for (pdt_entry_addr = PDT_START; pdt_entry_addr > PDT_END;
+ pdt_entry_addr -= PDT_ENTRY_SIZE) {
+ pdt_entry_addr |= (page_number << 8);
+
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ pdt_entry_addr,
+ (unsigned char *)&rmi_fd,
+ sizeof(rmi_fd));
+ if (retval < 0)
+ return retval;
+
+ fhandler = NULL;
+
+ if (rmi_fd.fn_number == 0) {
+ dev_dbg(&rmi4_data->i2c_client->dev,
+ "%s: Reached end of PDT\n",
+ __func__);
+ break;
+ }
+
+ dev_dbg(&rmi4_data->i2c_client->dev,
+ "%s: F%02x found (page %d)\n",
+ __func__, rmi_fd.fn_number,
+ page_number);
+
+ switch (rmi_fd.fn_number) {
+ case SYNAPTICS_RMI4_F01:
+ rmi4_data->f01_query_base_addr =
+ rmi_fd.query_base_addr;
+ rmi4_data->f01_ctrl_base_addr =
+ rmi_fd.ctrl_base_addr;
+ rmi4_data->f01_data_base_addr =
+ rmi_fd.data_base_addr;
+ rmi4_data->f01_cmd_base_addr =
+ rmi_fd.cmd_base_addr;
+
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ rmi4_data->f01_data_base_addr,
+ status.data,
+ sizeof(status.data));
+ if (retval < 0)
+ return retval;
+
+ if (status.flash_prog == 1) {
+ pr_notice("%s: In flash prog mode, status = 0x%02x\n",
+ __func__,
+ status.status_code);
+ goto flash_prog_mode;
+ }
+ break;
+ case SYNAPTICS_RMI4_F11:
+ if (rmi_fd.intr_src_count == 0)
+ break;
+
+ retval = synaptics_rmi4_alloc_fh(&fhandler,
+ &rmi_fd, page_number);
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to alloc for F%d\n",
+ __func__,
+ rmi_fd.fn_number);
+ return retval;
+ }
+
+ retval = synaptics_rmi4_f11_init(rmi4_data,
+ fhandler, &rmi_fd, intr_count);
+ if (retval < 0)
+ return retval;
+ break;
+
+ case SYNAPTICS_RMI4_F1A:
+ if (rmi_fd.intr_src_count == 0)
+ break;
+
+ retval = synaptics_rmi4_alloc_fh(&fhandler,
+ &rmi_fd, page_number);
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to alloc for F%d\n",
+ __func__,
+ rmi_fd.fn_number);
+ return retval;
+ }
+
+ retval = synaptics_rmi4_f1a_init(rmi4_data,
+ fhandler, &rmi_fd, intr_count);
+ if (retval < 0)
+ return retval;
+ break;
+ }
+
+ /* Accumulate the interrupt count */
+ intr_count += (rmi_fd.intr_src_count & MASK_3BIT);
+
+ if (fhandler && rmi_fd.intr_src_count) {
+ list_add_tail(&fhandler->link,
+ &rmi->support_fn_list);
+ }
+ }
+ }
+
+flash_prog_mode:
+ rmi4_data->num_of_intr_regs = (intr_count + 7) / 8;
+ dev_dbg(&rmi4_data->i2c_client->dev,
+ "%s: Number of interrupt registers = %d\n",
+ __func__, rmi4_data->num_of_intr_regs);
+
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ rmi4_data->f01_query_base_addr,
+ f01_query,
+ sizeof(f01_query));
+ if (retval < 0)
+ return retval;
+
+ /* RMI Version 4.0 currently supported */
+ rmi->version_major = 4;
+ rmi->version_minor = 0;
+
+ rmi->manufacturer_id = f01_query[0];
+ rmi->product_props = f01_query[1];
+ rmi->product_info[0] = f01_query[2] & MASK_7BIT;
+ rmi->product_info[1] = f01_query[3] & MASK_7BIT;
+ rmi->date_code[0] = f01_query[4] & MASK_5BIT;
+ rmi->date_code[1] = f01_query[5] & MASK_4BIT;
+ rmi->date_code[2] = f01_query[6] & MASK_5BIT;
+ rmi->tester_id = ((f01_query[7] & MASK_7BIT) << 8) |
+ (f01_query[8] & MASK_7BIT);
+ rmi->serial_number = ((f01_query[9] & MASK_7BIT) << 8) |
+ (f01_query[10] & MASK_7BIT);
+ memcpy(rmi->product_id_string, &f01_query[11], 10);
+
+ if (rmi->manufacturer_id != 1) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Non-Synaptics device found, manufacturer ID = %d\n",
+ __func__, rmi->manufacturer_id);
+ }
+
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ rmi4_data->f01_query_base_addr + F01_BUID_ID_OFFSET,
+ rmi->build_id,
+ sizeof(rmi->build_id));
+ if (retval < 0)
+ return retval;
+
+ memset(rmi4_data->intr_mask, 0x00, sizeof(rmi4_data->intr_mask));
+
+ /*
+ * Map out the interrupt bit masks for the interrupt sources
+ * from the registered function handlers.
+ */
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link)
+ data_sources += fhandler->num_of_data_sources;
+ if (data_sources) {
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->num_of_data_sources) {
+ rmi4_data->intr_mask[fhandler->intr_reg_num] |=
+ fhandler->intr_mask;
+ }
+ }
+ }
+
+ /* Enable the interrupt sources */
+ for (ii = 0; ii < rmi4_data->num_of_intr_regs; ii++) {
+ if (rmi4_data->intr_mask[ii] != 0x00) {
+ dev_dbg(&rmi4_data->i2c_client->dev,
+ "%s: Interrupt enable mask %d = 0x%02x\n",
+ __func__, ii, rmi4_data->intr_mask[ii]);
+ intr_addr = rmi4_data->f01_ctrl_base_addr + 1 + ii;
+ retval = synaptics_rmi4_i2c_write(rmi4_data,
+ intr_addr,
+ &(rmi4_data->intr_mask[ii]),
+ sizeof(rmi4_data->intr_mask[ii]));
+ if (retval < 0)
+ return retval;
+ }
+ }
+
+ return 0;
+}
+
+static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval;
+ unsigned char command = 0x01;
+ struct synaptics_rmi4_fn *fhandler;
+ struct synaptics_rmi4_device_info *rmi;
+
+ rmi = &(rmi4_data->rmi4_mod_info);
+
+ retval = synaptics_rmi4_i2c_write(rmi4_data,
+ rmi4_data->f01_cmd_base_addr,
+ &command,
+ sizeof(command));
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to issue reset command, error = %d\n",
+ __func__, retval);
+ return retval;
+ }
+
+ msleep(100);
+
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
+ synaptics_rmi4_f1a_kfree(fhandler);
+ else
+ kfree(fhandler->data);
+ kfree(fhandler);
+ }
+
+ retval = synaptics_rmi4_query_device(rmi4_data);
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to query device\n",
+ __func__);
+ return retval;
+ }
+
+ return 0;
+}
+
+/**
+* synaptics_rmi4_detection_work()
+*
+* Called by the kernel at the scheduled time.
+*
+* This function is a self-rearming work thread that checks for the
+* insertion and removal of other expansion Function modules such as
+* rmi_dev and calls their initialization and removal callback functions
+* accordingly.
+*/
+static void synaptics_rmi4_detection_work(struct work_struct *work)
+{
+ struct synaptics_rmi4_exp_fn *exp_fhandler, *next_list_entry;
+ struct synaptics_rmi4_data *rmi4_data =
+ container_of(work, struct synaptics_rmi4_data,
+ det_work.work);
+
+ queue_delayed_work(rmi4_data->det_workqueue,
+ &rmi4_data->det_work,
+ msecs_to_jiffies(EXP_FN_DET_INTERVAL));
+
+ mutex_lock(&exp_fn_list_mutex);
+ if (!list_empty(&exp_fn_list)) {
+ list_for_each_entry_safe(exp_fhandler,
+ next_list_entry,
+ &exp_fn_list,
+ link) {
+ if ((exp_fhandler->func_init != NULL) &&
+ (exp_fhandler->inserted == false)) {
+ exp_fhandler->func_init(rmi4_data);
+ exp_fhandler->inserted = true;
+ } else if ((exp_fhandler->func_init == NULL) &&
+ (exp_fhandler->inserted == true)) {
+ exp_fhandler->func_remove(rmi4_data);
+ list_del(&exp_fhandler->link);
+ kfree(exp_fhandler);
+ }
+ }
+ }
+ mutex_unlock(&exp_fn_list_mutex);
+
+ return;
+}
+
+/**
+* synaptics_rmi4_new_function()
+*
+* Called by other expansion Function modules in their module init and
+* module exit functions.
+*
+* This function is used by other expansion Function modules such as
+* rmi_dev to register themselves with the driver by providing their
+* initialization and removal callback function pointers so that they
+* can be inserted or removed dynamically at module init and exit times,
+* respectively.
+*/
+void synaptics_rmi4_new_function(enum exp_fn fn_type, bool insert,
+ int (*func_init)(struct synaptics_rmi4_data *rmi4_data),
+ void (*func_remove)(struct synaptics_rmi4_data *rmi4_data),
+ void (*func_attn)(struct synaptics_rmi4_data *rmi4_data,
+ unsigned char intr_mask))
+{
+ struct synaptics_rmi4_exp_fn *exp_fhandler;
+
+ if (!exp_fn_inited) {
+ mutex_init(&exp_fn_list_mutex);
+ INIT_LIST_HEAD(&exp_fn_list);
+ exp_fn_inited = 1;
+ }
+
+ mutex_lock(&exp_fn_list_mutex);
+ if (insert) {
+ exp_fhandler = kzalloc(sizeof(*exp_fhandler), GFP_KERNEL);
+ if (!exp_fhandler) {
+ pr_err("%s: Failed to alloc mem for expansion function\n",
+ __func__);
+ goto exit;
+ }
+ exp_fhandler->fn_type = fn_type;
+ exp_fhandler->func_init = func_init;
+ exp_fhandler->func_attn = func_attn;
+ exp_fhandler->func_remove = func_remove;
+ exp_fhandler->inserted = false;
+ list_add_tail(&exp_fhandler->link, &exp_fn_list);
+ } else {
+ list_for_each_entry(exp_fhandler, &exp_fn_list, link) {
+ if (exp_fhandler->func_init == func_init) {
+ exp_fhandler->inserted = false;
+ exp_fhandler->func_init = NULL;
+ exp_fhandler->func_attn = NULL;
+ goto exit;
+ }
+ }
+ }
+
+exit:
+ mutex_unlock(&exp_fn_list_mutex);
+
+ return;
+}
+EXPORT_SYMBOL(synaptics_rmi4_new_function);
+
+ /**
+ * synaptics_rmi4_probe()
+ *
+ * Called by the kernel when an association with an I2C device of the
+ * same name is made (after doing i2c_add_driver).
+ *
+ * This funtion allocates and initializes the resources for the driver
+ * as an input driver, turns on the power to the sensor, queries the
+ * sensor for its supported Functions and characteristics, registers
+ * the driver to the input subsystem, sets up the interrupt, handles
+ * the registration of the early_suspend and late_resume functions,
+ * and creates a work queue for detection of other expansion Function
+ * modules.
+ */
+static int __devinit synaptics_rmi4_probe(struct i2c_client *client,
+ const struct i2c_device_id *dev_id)
+{
+ int retval;
+ unsigned char ii;
+ unsigned char attr_count;
+ struct synaptics_rmi4_f1a_handle *f1a;
+ struct synaptics_rmi4_fn *fhandler;
+ struct synaptics_rmi4_data *rmi4_data;
+ struct synaptics_rmi4_device_info *rmi;
+ const struct synaptics_rmi4_platform_data *platform_data =
+ client->dev.platform_data;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev,
+ "%s: SMBus byte data not supported\n",
+ __func__);
+ return -EIO;
+ }
+
+ if (!platform_data) {
+ dev_err(&client->dev,
+ "%s: No platform data found\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ rmi4_data = kzalloc(sizeof(*rmi4_data) * 2, GFP_KERNEL);
+ if (!rmi4_data) {
+ dev_err(&client->dev,
+ "%s: Failed to alloc mem for rmi4_data\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ rmi = &(rmi4_data->rmi4_mod_info);
+
+ rmi4_data->input_dev = input_allocate_device();
+ if (rmi4_data->input_dev == NULL) {
+ dev_err(&client->dev,
+ "%s: Failed to allocate input device\n",
+ __func__);
+ retval = -ENOMEM;
+ goto err_input_device;
+ }
+/*
+ if (platform_data->regulator_en) {
+ rmi4_data->regulator = regulator_get(&client->dev, "vdd");
+ if (IS_ERR(rmi4_data->regulator)) {
+ dev_err(&client->dev,
+ "%s: Failed to get regulator\n",
+ __func__);
+ retval = PTR_ERR(rmi4_data->regulator);
+ goto err_regulator;
+ }
+ regulator_enable(rmi4_data->regulator);
+ }
+*/
+ rmi4_data->i2c_client = client;
+ rmi4_data->current_page = MASK_8BIT;
+ rmi4_data->board = platform_data;
+ rmi4_data->touch_stopped = false;
+ rmi4_data->sensor_sleep = false;
+ rmi4_data->irq_enabled = false;
+
+ rmi4_data->i2c_read = synaptics_rmi4_i2c_read;
+ rmi4_data->i2c_write = synaptics_rmi4_i2c_write;
+ rmi4_data->irq_enable = synaptics_rmi4_irq_enable;
+ rmi4_data->reset_device = synaptics_rmi4_reset_device;
+
+ init_waitqueue_head(&rmi4_data->wait);
+ mutex_init(&(rmi4_data->rmi4_io_ctrl_mutex));
+
+ retval = synaptics_rmi4_query_device(rmi4_data);
+ if (retval < 0) {
+ dev_err(&client->dev,
+ "%s: Failed to query device\n",
+ __func__);
+ goto err_query_device;
+ }
+
+ i2c_set_clientdata(client, rmi4_data);
+
+ rmi4_data->input_dev->name = DRIVER_NAME;
+ rmi4_data->input_dev->phys = INPUT_PHYS_NAME;
+ rmi4_data->input_dev->id.bustype = BUS_I2C;
+ rmi4_data->input_dev->dev.parent = &client->dev;
+ input_set_drvdata(rmi4_data->input_dev, rmi4_data);
+
+ set_bit(EV_SYN, rmi4_data->input_dev->evbit);
+ set_bit(EV_KEY, rmi4_data->input_dev->evbit);
+ set_bit(EV_ABS, rmi4_data->input_dev->evbit);
+
+#ifdef INPUT_PROP_DIRECT
+ set_bit(INPUT_PROP_DIRECT, rmi4_data->input_dev->propbit);
+#endif
+
+ input_set_abs_params(rmi4_data->input_dev,
+ ABS_MT_POSITION_X, 0,
+ rmi4_data->sensor_max_x, 0, 0);
+ input_set_abs_params(rmi4_data->input_dev,
+ ABS_MT_POSITION_Y, 0,
+ rmi4_data->sensor_max_y, 0, 0);
+#ifdef REPORT_2D_W
+ input_set_abs_params(rmi4_data->input_dev,
+ ABS_MT_TOUCH_MAJOR, 0,
+ MAX_ABS_MT_TOUCH_MAJOR, 0, 0);
+#endif
+
+#ifdef TYPE_B_PROTOCOL
+ input_mt_init_slots(rmi4_data->input_dev,
+ rmi4_data->num_of_fingers);
+#endif
+
+ f1a = NULL;
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
+ f1a = fhandler->data;
+ }
+
+ if (f1a) {
+ for (ii = 0; ii < f1a->valid_button_count; ii++) {
+ set_bit(f1a->button_map[ii],
+ rmi4_data->input_dev->keybit);
+ input_set_capability(rmi4_data->input_dev,
+ EV_KEY, f1a->button_map[ii]);
+ }
+ }
+
+ retval = input_register_device(rmi4_data->input_dev);
+ if (retval) {
+ dev_err(&client->dev,
+ "%s: Failed to register input device\n",
+ __func__);
+ goto err_register_input;
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ rmi4_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ rmi4_data->early_suspend.suspend = synaptics_rmi4_early_suspend;
+ rmi4_data->early_suspend.resume = synaptics_rmi4_late_resume;
+ register_early_suspend(&rmi4_data->early_suspend);
+#endif
+
+ if (!exp_fn_inited) {
+ mutex_init(&exp_fn_list_mutex);
+ INIT_LIST_HEAD(&exp_fn_list);
+ exp_fn_inited = 1;
+ }
+
+ rmi4_data->det_workqueue =
+ create_singlethread_workqueue("rmi_det_workqueue");
+ INIT_DELAYED_WORK(&rmi4_data->det_work,
+ synaptics_rmi4_detection_work);
+ queue_delayed_work(rmi4_data->det_workqueue,
+ &rmi4_data->det_work,
+ msecs_to_jiffies(EXP_FN_DET_INTERVAL));
+
+ if (platform_data->gpio_config) {
+ retval = platform_data->gpio_config(platform_data->irq_gpio,
+ true);
+ if (retval < 0) {
+ dev_err(&client->dev,
+ "%s: Failed to configure GPIO\n",
+ __func__);
+ goto err_gpio;
+ }
+ }
+
+ rmi4_data->irq = gpio_to_irq(platform_data->irq_gpio);
+
+ retval = synaptics_rmi4_irq_enable(rmi4_data, true);
+ if (retval < 0) {
+ dev_err(&client->dev,
+ "%s: Failed to enable attention interrupt\n",
+ __func__);
+ goto err_enable_irq;
+ }
+
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj,
+ &attrs[attr_count].attr);
+ if (retval < 0) {
+ dev_err(&client->dev,
+ "%s: Failed to create sysfs attributes\n",
+ __func__);
+ goto err_sysfs;
+ }
+ }
+
+ return retval;
+
+err_sysfs:
+ for (attr_count--; attr_count >= 0; attr_count--) {
+ sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
+ &attrs[attr_count].attr);
+ }
+
+err_enable_irq:
+err_gpio:
+ input_unregister_device(rmi4_data->input_dev);
+
+err_register_input:
+err_query_device:
+ if (platform_data->regulator_en) {
+ regulator_disable(rmi4_data->regulator);
+ regulator_put(rmi4_data->regulator);
+ }
+
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
+ synaptics_rmi4_f1a_kfree(fhandler);
+ else
+ kfree(fhandler->data);
+ kfree(fhandler);
+ }
+/*
+err_regulator:
+*/
+ input_free_device(rmi4_data->input_dev);
+ rmi4_data->input_dev = NULL;
+
+err_input_device:
+ kfree(rmi4_data);
+
+ return retval;
+}
+
+ /**
+ * synaptics_rmi4_remove()
+ *
+ * Called by the kernel when the association with an I2C device of the
+ * same name is broken (when the driver is unloaded).
+ *
+ * This funtion terminates the work queue, stops sensor data acquisition,
+ * frees the interrupt, unregisters the driver from the input subsystem,
+ * turns off the power to the sensor, and frees other allocated resources.
+ */
+static int __devexit synaptics_rmi4_remove(struct i2c_client *client)
+{
+ unsigned char attr_count;
+ struct synaptics_rmi4_fn *fhandler;
+ struct synaptics_rmi4_data *rmi4_data = i2c_get_clientdata(client);
+ struct synaptics_rmi4_device_info *rmi;
+ const struct synaptics_rmi4_platform_data *platform_data =
+ rmi4_data->board;
+
+ rmi = &(rmi4_data->rmi4_mod_info);
+
+ cancel_delayed_work_sync(&rmi4_data->det_work);
+ flush_workqueue(rmi4_data->det_workqueue);
+ destroy_workqueue(rmi4_data->det_workqueue);
+
+ rmi4_data->touch_stopped = true;
+ wake_up(&rmi4_data->wait);
+
+ synaptics_rmi4_irq_enable(rmi4_data, false);
+
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
+ &attrs[attr_count].attr);
+ }
+
+ input_unregister_device(rmi4_data->input_dev);
+
+ if (platform_data->regulator_en) {
+ regulator_disable(rmi4_data->regulator);
+ regulator_put(rmi4_data->regulator);
+ }
+
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
+ synaptics_rmi4_f1a_kfree(fhandler);
+ else
+ kfree(fhandler->data);
+ kfree(fhandler);
+ }
+ input_free_device(rmi4_data->input_dev);
+
+ kfree(rmi4_data);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+ /**
+ * synaptics_rmi4_sensor_sleep()
+ *
+ * Called by synaptics_rmi4_early_suspend() and synaptics_rmi4_suspend().
+ *
+ * This function stops finger data acquisition and puts the sensor to sleep.
+ */
+static void synaptics_rmi4_sensor_sleep(struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval;
+ unsigned char device_ctrl;
+
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ rmi4_data->f01_ctrl_base_addr,
+ &device_ctrl,
+ sizeof(device_ctrl));
+ if (retval < 0) {
+ dev_err(&(rmi4_data->input_dev->dev),
+ "%s: Failed to enter sleep mode\n",
+ __func__);
+ rmi4_data->sensor_sleep = false;
+ return;
+ }
+
+ device_ctrl = (device_ctrl & ~MASK_3BIT);
+ device_ctrl = (device_ctrl | NO_SLEEP_OFF | SENSOR_SLEEP);
+
+ retval = synaptics_rmi4_i2c_write(rmi4_data,
+ rmi4_data->f01_ctrl_base_addr,
+ &device_ctrl,
+ sizeof(device_ctrl));
+ if (retval < 0) {
+ dev_err(&(rmi4_data->input_dev->dev),
+ "%s: Failed to enter sleep mode\n",
+ __func__);
+ rmi4_data->sensor_sleep = false;
+ return;
+ } else {
+ rmi4_data->sensor_sleep = true;
+ }
+
+ return;
+}
+
+ /**
+ * synaptics_rmi4_sensor_wake()
+ *
+ * Called by synaptics_rmi4_resume() and synaptics_rmi4_late_resume().
+ *
+ * This function wakes the sensor from sleep.
+ */
+static void synaptics_rmi4_sensor_wake(struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval;
+ unsigned char device_ctrl;
+
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ rmi4_data->f01_ctrl_base_addr,
+ &device_ctrl,
+ sizeof(device_ctrl));
+ if (retval < 0) {
+ dev_err(&(rmi4_data->input_dev->dev),
+ "%s: Failed to wake from sleep mode\n",
+ __func__);
+ rmi4_data->sensor_sleep = true;
+ return;
+ }
+
+ device_ctrl = (device_ctrl & ~MASK_3BIT);
+ device_ctrl = (device_ctrl | NO_SLEEP_OFF | NORMAL_OPERATION);
+
+ retval = synaptics_rmi4_i2c_write(rmi4_data,
+ rmi4_data->f01_ctrl_base_addr,
+ &device_ctrl,
+ sizeof(device_ctrl));
+ if (retval < 0) {
+ dev_err(&(rmi4_data->input_dev->dev),
+ "%s: Failed to wake from sleep mode\n",
+ __func__);
+ rmi4_data->sensor_sleep = true;
+ return;
+ } else {
+ rmi4_data->sensor_sleep = false;
+ }
+
+ return;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ /**
+ * synaptics_rmi4_early_suspend()
+ *
+ * Called by the kernel during the early suspend phase when the system
+ * enters suspend.
+ *
+ * This function calls synaptics_rmi4_sensor_sleep() to stop finger
+ * data acquisition and put the sensor to sleep.
+ */
+static void synaptics_rmi4_early_suspend(struct early_suspend *h)
+{
+ struct synaptics_rmi4_data *rmi4_data =
+ container_of(h, struct synaptics_rmi4_data,
+ early_suspend);
+
+ rmi4_data->touch_stopped = true;
+ wake_up(&rmi4_data->wait);
+ synaptics_rmi4_irq_enable(rmi4_data, false);
+ synaptics_rmi4_sensor_sleep(rmi4_data);
+
+ if (rmi4_data->full_pm_cycle)
+ synaptics_rmi4_suspend(&(rmi4_data->input_dev->dev));
+
+ return;
+}
+
+ /**
+ * synaptics_rmi4_late_resume()
+ *
+ * Called by the kernel during the late resume phase when the system
+ * wakes up from suspend.
+ *
+ * This function goes through the sensor wake process if the system wakes
+ * up from early suspend (without going into suspend).
+ */
+static void synaptics_rmi4_late_resume(struct early_suspend *h)
+{
+ struct synaptics_rmi4_data *rmi4_data =
+ container_of(h, struct synaptics_rmi4_data,
+ early_suspend);
+
+ if (rmi4_data->full_pm_cycle)
+ synaptics_rmi4_resume(&(rmi4_data->input_dev->dev));
+
+ if (rmi4_data->sensor_sleep == true) {
+ synaptics_rmi4_sensor_wake(rmi4_data);
+ rmi4_data->touch_stopped = false;
+ synaptics_rmi4_irq_enable(rmi4_data, true);
+ }
+
+ return;
+}
+#endif
+
+ /**
+ * synaptics_rmi4_suspend()
+ *
+ * Called by the kernel during the suspend phase when the system
+ * enters suspend.
+ *
+ * This function stops finger data acquisition and puts the sensor to
+ * sleep (if not already done so during the early suspend phase),
+ * disables the interrupt, and turns off the power to the sensor.
+ */
+static int synaptics_rmi4_suspend(struct device *dev)
+{
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+ const struct synaptics_rmi4_platform_data *platform_data =
+ rmi4_data->board;
+
+ if (!rmi4_data->sensor_sleep) {
+ rmi4_data->touch_stopped = true;
+ wake_up(&rmi4_data->wait);
+ synaptics_rmi4_irq_enable(rmi4_data, false);
+ synaptics_rmi4_sensor_sleep(rmi4_data);
+ }
+
+ if (platform_data->regulator_en)
+ regulator_disable(rmi4_data->regulator);
+
+ return 0;
+}
+
+ /**
+ * synaptics_rmi4_resume()
+ *
+ * Called by the kernel during the resume phase when the system
+ * wakes up from suspend.
+ *
+ * This function turns on the power to the sensor, wakes the sensor
+ * from sleep, enables the interrupt, and starts finger data
+ * acquisition.
+ */
+static int synaptics_rmi4_resume(struct device *dev)
+{
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+ const struct synaptics_rmi4_platform_data *platform_data =
+ rmi4_data->board;
+
+ if (platform_data->regulator_en)
+ regulator_enable(rmi4_data->regulator);
+
+ synaptics_rmi4_sensor_wake(rmi4_data);
+ rmi4_data->touch_stopped = false;
+ synaptics_rmi4_irq_enable(rmi4_data, true);
+
+ return 0;
+}
+
+static const struct dev_pm_ops synaptics_rmi4_dev_pm_ops = {
+ .suspend = synaptics_rmi4_suspend,
+ .resume = synaptics_rmi4_resume,
+};
+#endif
+
+static const struct i2c_device_id synaptics_rmi4_id_table[] = {
+ {DRIVER_NAME, 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table);
+
+static struct i2c_driver synaptics_rmi4_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &synaptics_rmi4_dev_pm_ops,
+#endif
+ },
+ .probe = synaptics_rmi4_probe,
+ .remove = __devexit_p(synaptics_rmi4_remove),
+ .id_table = synaptics_rmi4_id_table,
+};
+
+ /**
+ * synaptics_rmi4_init()
+ *
+ * Called by the kernel during do_initcalls (if built-in)
+ * or when the driver is loaded (if a module).
+ *
+ * This function registers the driver to the I2C subsystem.
+ *
+ */
+static int __init synaptics_rmi4_init(void)
+{
+ return i2c_add_driver(&synaptics_rmi4_driver);
+}
+
+ /**
+ * synaptics_rmi4_exit()
+ *
+ * Called by the kernel when the driver is unloaded.
+ *
+ * This funtion unregisters the driver from the I2C subsystem.
+ *
+ */
+static void __exit synaptics_rmi4_exit(void)
+{
+ i2c_del_driver(&synaptics_rmi4_driver);
+}
+
+module_init(synaptics_rmi4_init);
+module_exit(synaptics_rmi4_exit);
+
+MODULE_AUTHOR("Synaptics, Inc.");
+MODULE_DESCRIPTION("Synaptics RMI4 I2C Touch Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(SYNAPTICS_RMI4_DRIVER_VERSION);
diff --git a/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.h b/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.h
new file mode 100644
index 000000000000..7ee0a925959a
--- /dev/null
+++ b/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.h
@@ -0,0 +1,282 @@
+/*
+ * Synaptics RMI4 touchscreen driver
+ *
+ * Copyright (C) 2012 Synaptics Incorporated
+ *
+ * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
+ * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 _SYNAPTICS_DSX_RMI4_H_
+#define _SYNAPTICS_DSX_RMI4_H_
+
+#define SYNAPTICS_RMI4_DRIVER_VERSION "DSX 1.0"
+
+#include <linux/version.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38))
+#define KERNEL_ABOVE_2_6_38
+#endif
+
+#ifdef KERNEL_ABOVE_2_6_38
+#define sstrtoul(...) kstrtoul(__VA_ARGS__)
+#else
+#define sstrtoul(...) strict_strtoul(__VA_ARGS__)
+#endif
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0))
+#define KERNEL_ABOVE_3_7
+#endif
+
+#define PDT_PROPS (0x00EF)
+#define PDT_START (0x00E9)
+#define PDT_END (0x000A)
+#define PDT_ENTRY_SIZE (0x0006)
+#define PAGES_TO_SERVICE (10)
+#define PAGE_SELECT_LEN (2)
+
+#define SYNAPTICS_RMI4_F01 (0x01)
+#define SYNAPTICS_RMI4_F11 (0x11)
+#define SYNAPTICS_RMI4_F1A (0x1a)
+#define SYNAPTICS_RMI4_F34 (0x34)
+#define SYNAPTICS_RMI4_F54 (0x54)
+#define SYNAPTICS_RMI4_F55 (0x55)
+
+#define SYNAPTICS_RMI4_PRODUCT_INFO_SIZE 2
+#define SYNAPTICS_RMI4_DATE_CODE_SIZE 3
+#define SYNAPTICS_RMI4_PRODUCT_ID_SIZE 10
+#define SYNAPTICS_RMI4_BUILD_ID_SIZE 3
+
+#define MAX_NUMBER_OF_FINGERS 10
+#define MAX_NUMBER_OF_BUTTONS 4
+#define MAX_INTR_REGISTERS 4
+
+#define MASK_16BIT 0xFFFF
+#define MASK_8BIT 0xFF
+#define MASK_7BIT 0x7F
+#define MASK_6BIT 0x3F
+#define MASK_5BIT 0x1F
+#define MASK_4BIT 0x0F
+#define MASK_3BIT 0x07
+#define MASK_2BIT 0x03
+#define MASK_1BIT 0x01
+
+/*
+ * struct synaptics_rmi4_fn_desc - function descriptor fields in PDT
+ * @query_base_addr: base address for query registers
+ * @cmd_base_addr: base address for command registers
+ * @ctrl_base_addr: base address for control registers
+ * @data_base_addr: base address for data registers
+ * @intr_src_count: number of interrupt sources
+ * @fn_number: function number
+ */
+struct synaptics_rmi4_fn_desc {
+ unsigned char query_base_addr;
+ unsigned char cmd_base_addr;
+ unsigned char ctrl_base_addr;
+ unsigned char data_base_addr;
+ unsigned char intr_src_count;
+ unsigned char fn_number;
+};
+
+/*
+ * synaptics_rmi4_fn_full_addr - full 16-bit base addresses
+ * @query_base: 16-bit base address for query registers
+ * @cmd_base: 16-bit base address for data registers
+ * @ctrl_base: 16-bit base address for command registers
+ * @data_base: 16-bit base address for control registers
+ */
+struct synaptics_rmi4_fn_full_addr {
+ unsigned short query_base;
+ unsigned short cmd_base;
+ unsigned short ctrl_base;
+ unsigned short data_base;
+};
+
+/*
+ * struct synaptics_rmi4_fn - function handler data structure
+ * @fn_number: function number
+ * @num_of_data_sources: number of data sources
+ * @num_of_data_points: maximum number of fingers supported
+ * @size_of_data_register_block: data register block size
+ * @data1_offset: offset to data1 register from data base address
+ * @intr_reg_num: index to associated interrupt register
+ * @intr_mask: interrupt mask
+ * @full_addr: full 16-bit base addresses of function registers
+ * @link: linked list for function handlers
+ * @data_size: size of private data
+ * @data: pointer to private data
+ */
+struct synaptics_rmi4_fn {
+ unsigned char fn_number;
+ unsigned char num_of_data_sources;
+ unsigned char num_of_data_points;
+ unsigned char size_of_data_register_block;
+ unsigned char data1_offset;
+ unsigned char intr_reg_num;
+ unsigned char intr_mask;
+ struct synaptics_rmi4_fn_full_addr full_addr;
+ struct list_head link;
+ int data_size;
+ void *data;
+};
+
+/*
+ * struct synaptics_rmi4_device_info - device information
+ * @version_major: rmi protocol major version number
+ * @version_minor: rmi protocol minor version number
+ * @manufacturer_id: manufacturer id
+ * @product_props: product properties information
+ * @product_info: product info array
+ * @date_code: device manufacture date
+ * @tester_id: tester id array
+ * @serial_number: device serial number
+ * @product_id_string: device product id
+ * @support_fn_list: linked list for function handlers
+ */
+struct synaptics_rmi4_device_info {
+ unsigned int version_major;
+ unsigned int version_minor;
+ unsigned char manufacturer_id;
+ unsigned char product_props;
+ unsigned char product_info[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE];
+ unsigned char date_code[SYNAPTICS_RMI4_DATE_CODE_SIZE];
+ unsigned short tester_id;
+ unsigned short serial_number;
+ unsigned char product_id_string[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1];
+ unsigned char build_id[SYNAPTICS_RMI4_BUILD_ID_SIZE];
+ struct list_head support_fn_list;
+};
+
+/*
+ * struct synaptics_rmi4_data - rmi4 device instance data
+ * @i2c_client: pointer to associated i2c client
+ * @input_dev: pointer to associated input device
+ * @board: constant pointer to platform data
+ * @rmi4_mod_info: device information
+ * @regulator: pointer to associated regulator
+ * @rmi4_io_ctrl_mutex: mutex for i2c i/o control
+ * @det_work: work thread instance for expansion function detection
+ * @det_workqueue: pointer to work queue for work thread instance
+ * @early_suspend: instance to support early suspend power management
+ * @current_page: current page in sensor to acess
+ * @button_0d_enabled: flag for 0d button support
+ * @full_pm_cycle: flag for full power management cycle in early suspend stage
+ * @num_of_intr_regs: number of interrupt registers
+ * @f01_query_base_addr: query base address for f01
+ * @f01_cmd_base_addr: command base address for f01
+ * @f01_ctrl_base_addr: control base address for f01
+ * @f01_data_base_addr: data base address for f01
+ * @irq: attention interrupt
+ * @sensor_max_x: sensor maximum x value
+ * @sensor_max_y: sensor maximum y value
+ * @irq_enabled: flag for indicating interrupt enable status
+ * @touch_stopped: flag to stop interrupt thread processing
+ * @fingers_on_2d: flag to indicate presence of fingers in 2d area
+ * @sensor_sleep: flag to indicate sleep state of sensor
+ * @wait: wait queue for touch data polling in interrupt thread
+ * @i2c_read: pointer to i2c read function
+ * @i2c_write: pointer to i2c write function
+ * @irq_enable: pointer to irq enable function
+ */
+struct synaptics_rmi4_data {
+ struct i2c_client *i2c_client;
+ struct input_dev *input_dev;
+ const struct synaptics_rmi4_platform_data *board;
+ struct synaptics_rmi4_device_info rmi4_mod_info;
+ struct regulator *regulator;
+ struct mutex rmi4_io_ctrl_mutex;
+ struct delayed_work det_work;
+ struct workqueue_struct *det_workqueue;
+ struct early_suspend early_suspend;
+ unsigned char current_page;
+ unsigned char button_0d_enabled;
+ unsigned char full_pm_cycle;
+ unsigned char num_of_rx;
+ unsigned char num_of_tx;
+ unsigned char num_of_fingers;
+ unsigned char intr_mask[MAX_INTR_REGISTERS];
+ unsigned short num_of_intr_regs;
+ unsigned short f01_query_base_addr;
+ unsigned short f01_cmd_base_addr;
+ unsigned short f01_ctrl_base_addr;
+ unsigned short f01_data_base_addr;
+ int irq;
+ int sensor_max_x;
+ int sensor_max_y;
+ bool irq_enabled;
+ bool touch_stopped;
+ bool fingers_on_2d;
+ bool sensor_sleep;
+ wait_queue_head_t wait;
+ int (*i2c_read)(struct synaptics_rmi4_data *pdata, unsigned short addr,
+ unsigned char *data, unsigned short length);
+ int (*i2c_write)(struct synaptics_rmi4_data *pdata, unsigned short addr,
+ unsigned char *data, unsigned short length);
+ int (*irq_enable)(struct synaptics_rmi4_data *rmi4_data, bool enable);
+ int (*reset_device)(struct synaptics_rmi4_data *rmi4_data);
+};
+
+enum exp_fn {
+ RMI_DEV = 0,
+ RMI_F34,
+ RMI_F54,
+ RMI_FW_UPDATER,
+ RMI_LAST,
+};
+
+struct synaptics_rmi4_exp_fn_ptr {
+ int (*read)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr,
+ unsigned char *data, unsigned short length);
+ int (*write)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr,
+ unsigned char *data, unsigned short length);
+ int (*enable)(struct synaptics_rmi4_data *rmi4_data, bool enable);
+};
+
+void synaptics_rmi4_new_function(enum exp_fn fn_type, bool insert,
+ int (*func_init)(struct synaptics_rmi4_data *rmi4_data),
+ void (*func_remove)(struct synaptics_rmi4_data *rmi4_data),
+ void (*func_attn)(struct synaptics_rmi4_data *rmi4_data,
+ unsigned char intr_mask));
+
+static inline ssize_t synaptics_rmi4_show_error(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ dev_warn(dev, "%s Attempted to read from write-only attribute %s\n",
+ __func__, attr->attr.name);
+ return -EPERM;
+}
+
+static inline ssize_t synaptics_rmi4_store_error(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ dev_warn(dev, "%s Attempted to write to read-only attribute %s\n",
+ __func__, attr->attr.name);
+ return -EPERM;
+}
+
+static inline void batohs(unsigned short *dest, unsigned char *src)
+{
+ *dest = src[1] * 0x100 + src[0];
+}
+
+static inline void hstoba(unsigned char *dest, unsigned short src)
+{
+ dest[0] = src % 0x100;
+ dest[1] = src / 0x100;
+}
+
+#endif
diff --git a/kernel/drivers/input/touchscreen/synaptics_rmi_dev.c b/kernel/drivers/input/touchscreen/synaptics_rmi_dev.c
new file mode 100644
index 000000000000..75857802c97a
--- /dev/null
+++ b/kernel/drivers/input/touchscreen/synaptics_rmi_dev.c
@@ -0,0 +1,710 @@
+/*
+ * Synaptics RMI4 touchscreen driver
+ *
+ * Copyright (C) 2012 Synaptics Incorporated
+ *
+ * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
+ * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/gpio.h>
+#include <linux/uaccess.h>
+#include <linux/cdev.h>
+#include <linux/input/synaptics_dsx.h>
+#include "synaptics_i2c_rmi4.h"
+
+#define CHAR_DEVICE_NAME "rmi"
+#define DEVICE_CLASS_NAME "rmidev"
+#define DEV_NUMBER 1
+#define REG_ADDR_LIMIT 0xFFFF
+
+static ssize_t rmidev_sysfs_open_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t rmidev_sysfs_release_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t rmidev_sysfs_address_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t rmidev_sysfs_length_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t rmidev_sysfs_data_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmidev_sysfs_data_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+struct rmidev_handle {
+ dev_t dev_no;
+ unsigned short address;
+ unsigned int length;
+ struct device dev;
+ struct synaptics_rmi4_data *rmi4_data;
+ struct synaptics_rmi4_exp_fn_ptr *fn_ptr;
+ struct kobject *sysfs_dir;
+ void *data;
+};
+
+struct rmidev_data {
+ int ref_count;
+ struct cdev main_dev;
+ struct class *device_class;
+ struct mutex file_mutex;
+ struct rmidev_handle *rmi_dev;
+};
+
+static struct device_attribute attrs[] = {
+ __ATTR(open, S_IWUGO,
+ synaptics_rmi4_show_error,
+ rmidev_sysfs_open_store),
+ __ATTR(release, S_IWUGO,
+ synaptics_rmi4_show_error,
+ rmidev_sysfs_release_store),
+ __ATTR(address, S_IWUGO,
+ synaptics_rmi4_show_error,
+ rmidev_sysfs_address_store),
+ __ATTR(length, S_IWUGO,
+ synaptics_rmi4_show_error,
+ rmidev_sysfs_length_store),
+ __ATTR(data, (S_IRUGO | S_IWUGO),
+ rmidev_sysfs_data_show,
+ rmidev_sysfs_data_store),
+};
+
+static int rmidev_major_num;
+
+static struct class *rmidev_device_class;
+
+static struct rmidev_handle *rmidev;
+
+static struct completion remove_complete;
+
+static ssize_t rmidev_sysfs_open_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int input;
+
+ if (sscanf(buf, "%u", &input) != 1)
+ return -EINVAL;
+
+ if (input != 1)
+ return -EINVAL;
+
+ rmidev->fn_ptr->enable(rmidev->rmi4_data, false);
+ dev_dbg(&rmidev->rmi4_data->i2c_client->dev,
+ "%s: Attention interrupt disabled\n",
+ __func__);
+
+ return count;
+}
+
+static ssize_t rmidev_sysfs_release_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int input;
+
+ if (sscanf(buf, "%u", &input) != 1)
+ return -EINVAL;
+
+ if (input != 1)
+ return -EINVAL;
+
+ rmidev->fn_ptr->enable(rmidev->rmi4_data, true);
+ dev_dbg(&rmidev->rmi4_data->i2c_client->dev,
+ "%s: Attention interrupt enabled\n",
+ __func__);
+
+ return count;
+}
+
+static ssize_t rmidev_sysfs_address_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int input;
+
+ if (sscanf(buf, "%u", &input) != 1)
+ return -EINVAL;
+
+ if (input > REG_ADDR_LIMIT)
+ return -EINVAL;
+
+ rmidev->address = (unsigned short)input;
+
+ return count;
+}
+
+static ssize_t rmidev_sysfs_length_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int input;
+
+ if (sscanf(buf, "%u", &input) != 1)
+ return -EINVAL;
+
+ if (input > REG_ADDR_LIMIT)
+ return -EINVAL;
+
+ rmidev->length = input;
+
+ return count;
+}
+
+static ssize_t rmidev_sysfs_data_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int retval;
+ unsigned int data_length = rmidev->length;
+
+ if (data_length > (REG_ADDR_LIMIT - rmidev->address))
+ data_length = REG_ADDR_LIMIT - rmidev->address;
+
+ if (data_length) {
+ retval = rmidev->fn_ptr->read(rmidev->rmi4_data,
+ rmidev->address,
+ (unsigned char *)buf,
+ data_length);
+ if (retval < 0) {
+ dev_err(&rmidev->rmi4_data->i2c_client->dev,
+ "%s: Failed to read data\n",
+ __func__);
+ return retval;
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ return data_length;
+}
+
+static ssize_t rmidev_sysfs_data_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int retval;
+ unsigned int data_length = rmidev->length;
+
+ if (data_length > (REG_ADDR_LIMIT - rmidev->address))
+ data_length = REG_ADDR_LIMIT - rmidev->address;
+
+ if (data_length) {
+ retval = rmidev->fn_ptr->write(rmidev->rmi4_data,
+ rmidev->address,
+ (unsigned char *)buf,
+ data_length);
+ if (retval < 0) {
+ dev_err(&rmidev->rmi4_data->i2c_client->dev,
+ "%s: Failed to write data\n",
+ __func__);
+ return retval;
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ return data_length;
+}
+
+/*
+ * rmidev_llseek - used to set up register address
+ *
+ * @filp: file structure for seek
+ * @off: offset
+ * if whence == SEEK_SET,
+ * high 16 bits: page address
+ * low 16 bits: register address
+ * if whence == SEEK_CUR,
+ * offset from current position
+ * if whence == SEEK_END,
+ * offset from end position (0xFFFF)
+ * @whence: SEEK_SET, SEEK_CUR, or SEEK_END
+ */
+static loff_t rmidev_llseek(struct file *filp, loff_t off, int whence)
+{
+ loff_t newpos;
+ struct rmidev_data *dev_data = filp->private_data;
+
+ if (IS_ERR(dev_data)) {
+ pr_err("%s: Pointer of char device data is invalid", __func__);
+ return -EBADF;
+ }
+
+ mutex_lock(&(dev_data->file_mutex));
+
+ switch (whence) {
+ case SEEK_SET:
+ newpos = off;
+ break;
+ case SEEK_CUR:
+ newpos = filp->f_pos + off;
+ break;
+ case SEEK_END:
+ newpos = REG_ADDR_LIMIT + off;
+ break;
+ default:
+ newpos = -EINVAL;
+ goto clean_up;
+ }
+
+ if (newpos < 0 || newpos > REG_ADDR_LIMIT) {
+ dev_err(&rmidev->rmi4_data->i2c_client->dev,
+ "%s: New position 0x%04x is invalid\n",
+ __func__, (unsigned int)newpos);
+ newpos = -EINVAL;
+ goto clean_up;
+ }
+
+ filp->f_pos = newpos;
+
+clean_up:
+ mutex_unlock(&(dev_data->file_mutex));
+
+ return newpos;
+}
+
+/*
+ * rmidev_read: - use to read data from rmi device
+ *
+ * @filp: file structure for read
+ * @buf: user space buffer pointer
+ * @count: number of bytes to read
+ * @f_pos: offset (starting register address)
+ */
+static ssize_t rmidev_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ ssize_t retval;
+ unsigned char tmpbuf[count + 1];
+ struct rmidev_data *dev_data = filp->private_data;
+
+ if (IS_ERR(dev_data)) {
+ pr_err("%s: Pointer of char device data is invalid", __func__);
+ return -EBADF;
+ }
+
+ if (count == 0)
+ return 0;
+
+ if (count > (REG_ADDR_LIMIT - *f_pos))
+ count = REG_ADDR_LIMIT - *f_pos;
+
+ mutex_lock(&(dev_data->file_mutex));
+
+ retval = rmidev->fn_ptr->read(rmidev->rmi4_data,
+ *f_pos,
+ tmpbuf,
+ count);
+ if (retval < 0)
+ goto clean_up;
+
+ if (copy_to_user(buf, tmpbuf, count))
+ retval = -EFAULT;
+ else
+ *f_pos += retval;
+
+clean_up:
+ mutex_unlock(&(dev_data->file_mutex));
+
+ return retval;
+}
+
+/*
+ * rmidev_write: - used to write data to rmi device
+ *
+ * @filep: file structure for write
+ * @buf: user space buffer pointer
+ * @count: number of bytes to write
+ * @f_pos: offset (starting register address)
+ */
+static ssize_t rmidev_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ ssize_t retval;
+ unsigned char tmpbuf[count + 1];
+ struct rmidev_data *dev_data = filp->private_data;
+
+ if (IS_ERR(dev_data)) {
+ pr_err("%s: Pointer of char device data is invalid", __func__);
+ return -EBADF;
+ }
+
+ if (count == 0)
+ return 0;
+
+ if (count > (REG_ADDR_LIMIT - *f_pos))
+ count = REG_ADDR_LIMIT - *f_pos;
+
+ if (copy_from_user(tmpbuf, buf, count))
+ return -EFAULT;
+
+ mutex_lock(&(dev_data->file_mutex));
+
+ retval = rmidev->fn_ptr->write(rmidev->rmi4_data,
+ *f_pos,
+ tmpbuf,
+ count);
+ if (retval >= 0)
+ *f_pos += retval;
+
+ mutex_unlock(&(dev_data->file_mutex));
+
+ return retval;
+}
+
+/*
+ * rmidev_open: enable access to rmi device
+ * @inp: inode struture
+ * @filp: file structure
+ */
+static int rmidev_open(struct inode *inp, struct file *filp)
+{
+ int retval = 0;
+ struct rmidev_data *dev_data =
+ container_of(inp->i_cdev, struct rmidev_data, main_dev);
+
+ if (!dev_data)
+ return -EACCES;
+
+ filp->private_data = dev_data;
+
+ mutex_lock(&(dev_data->file_mutex));
+
+ rmidev->fn_ptr->enable(rmidev->rmi4_data, false);
+ dev_dbg(&rmidev->rmi4_data->i2c_client->dev,
+ "%s: Attention interrupt disabled\n",
+ __func__);
+
+ if (dev_data->ref_count < 1)
+ dev_data->ref_count++;
+ else
+ retval = -EACCES;
+
+ mutex_unlock(&(dev_data->file_mutex));
+
+ return retval;
+}
+
+/*
+ * rmidev_release: - release access to rmi device
+ * @inp: inode structure
+ * @filp: file structure
+ */
+static int rmidev_release(struct inode *inp, struct file *filp)
+{
+ struct rmidev_data *dev_data =
+ container_of(inp->i_cdev, struct rmidev_data, main_dev);
+
+ if (!dev_data)
+ return -EACCES;
+
+ mutex_lock(&(dev_data->file_mutex));
+
+ dev_data->ref_count--;
+ if (dev_data->ref_count < 0)
+ dev_data->ref_count = 0;
+
+ rmidev->fn_ptr->enable(rmidev->rmi4_data, true);
+ dev_dbg(&rmidev->rmi4_data->i2c_client->dev,
+ "%s: Attention interrupt enabled\n",
+ __func__);
+
+ mutex_unlock(&(dev_data->file_mutex));
+
+ return 0;
+}
+
+static const struct file_operations rmidev_fops = {
+ .owner = THIS_MODULE,
+ .llseek = rmidev_llseek,
+ .read = rmidev_read,
+ .write = rmidev_write,
+ .open = rmidev_open,
+ .release = rmidev_release,
+};
+
+static void rmidev_device_cleanup(struct rmidev_data *dev_data)
+{
+ dev_t devno;
+
+ if (dev_data) {
+ devno = dev_data->main_dev.dev;
+
+ if (dev_data->device_class)
+ device_destroy(dev_data->device_class, devno);
+
+ cdev_del(&dev_data->main_dev);
+
+ unregister_chrdev_region(devno, 1);
+
+ dev_dbg(&rmidev->rmi4_data->i2c_client->dev,
+ "%s: rmidev device removed\n",
+ __func__);
+ }
+
+ return;
+}
+
+static char *rmi_char_devnode(struct device *dev, mode_t *mode)
+{
+ if (!mode)
+ return NULL;
+
+ *mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+
+ return kasprintf(GFP_KERNEL, "rmi/%s", dev_name(dev));
+}
+
+static int rmidev_create_device_class(void)
+{
+ rmidev_device_class = class_create(THIS_MODULE, DEVICE_CLASS_NAME);
+
+ if (IS_ERR(rmidev_device_class)) {
+ pr_err("%s: Failed to create /dev/%s\n",
+ __func__, CHAR_DEVICE_NAME);
+ return -ENODEV;
+ }
+
+ rmidev_device_class->devnode = rmi_char_devnode;
+
+ return 0;
+}
+
+static int rmidev_init_device(struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval;
+ dev_t dev_no;
+ unsigned char attr_count;
+ struct rmidev_data *dev_data;
+ struct device *device_ptr;
+
+ rmidev = kzalloc(sizeof(*rmidev), GFP_KERNEL);
+ if (!rmidev) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to alloc mem for rmidev\n",
+ __func__);
+ retval = -ENOMEM;
+ goto err_rmidev;
+ }
+
+ rmidev->fn_ptr = kzalloc(sizeof(*(rmidev->fn_ptr)), GFP_KERNEL);
+ if (!rmidev) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to alloc mem for fn_ptr\n",
+ __func__);
+ retval = -ENOMEM;
+ goto err_fn_ptr;
+ }
+
+ rmidev->fn_ptr->read = rmi4_data->i2c_read;
+ rmidev->fn_ptr->write = rmi4_data->i2c_write;
+ rmidev->fn_ptr->enable = rmi4_data->irq_enable;
+ rmidev->rmi4_data = rmi4_data;
+
+ retval = rmidev_create_device_class();
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to create device class\n",
+ __func__);
+ goto err_device_class;
+ }
+
+ if (rmidev_major_num) {
+ dev_no = MKDEV(rmidev_major_num, DEV_NUMBER);
+ retval = register_chrdev_region(dev_no, 1, CHAR_DEVICE_NAME);
+ } else {
+ retval = alloc_chrdev_region(&dev_no, 0, 1, CHAR_DEVICE_NAME);
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to allocate char device region\n",
+ __func__);
+ goto err_device_region;
+ }
+
+ rmidev_major_num = MAJOR(dev_no);
+ dev_dbg(&rmi4_data->i2c_client->dev,
+ "%s: Major number of rmidev = %d\n",
+ __func__, rmidev_major_num);
+ }
+
+ dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL);
+ if (!dev_data) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to alloc mem for dev_data\n",
+ __func__);
+ retval = -ENOMEM;
+ goto err_dev_data;
+ }
+
+ mutex_init(&dev_data->file_mutex);
+ dev_data->rmi_dev = rmidev;
+ rmidev->data = dev_data;
+
+ cdev_init(&dev_data->main_dev, &rmidev_fops);
+
+ retval = cdev_add(&dev_data->main_dev, dev_no, 1);
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to add rmi char device\n",
+ __func__);
+ goto err_char_device;
+ }
+
+ dev_set_name(&rmidev->dev, "rmidev%d", MINOR(dev_no));
+ dev_data->device_class = rmidev_device_class;
+
+ device_ptr = device_create(dev_data->device_class, NULL, dev_no,
+ NULL, CHAR_DEVICE_NAME"%d", MINOR(dev_no));
+ if (IS_ERR(device_ptr)) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to create rmi char device\n",
+ __func__);
+ retval = -ENODEV;
+ goto err_char_device;
+ }
+
+ retval = gpio_export(rmi4_data->board->irq_gpio, false);
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to export attention gpio\n",
+ __func__);
+ } else {
+ retval = gpio_export_link(&(rmi4_data->input_dev->dev),
+ "attn", rmi4_data->board->irq_gpio);
+ if (retval < 0) {
+ dev_err(&rmi4_data->input_dev->dev,
+ "%s Failed to create gpio symlink\n",
+ __func__);
+ } else {
+ dev_dbg(&rmi4_data->input_dev->dev,
+ "%s: Exported attention gpio %d\n",
+ __func__, rmi4_data->board->irq_gpio);
+ }
+ }
+
+ rmidev->sysfs_dir = kobject_create_and_add("rmidev",
+ &rmi4_data->input_dev->dev.kobj);
+ if (!rmidev->sysfs_dir) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to create sysfs directory\n",
+ __func__);
+ goto err_sysfs_dir;
+ }
+
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ retval = sysfs_create_file(rmidev->sysfs_dir,
+ &attrs[attr_count].attr);
+ if (retval < 0) {
+ dev_err(&rmi4_data->input_dev->dev,
+ "%s: Failed to create sysfs attributes\n",
+ __func__);
+ retval = -ENODEV;
+ goto err_sysfs_attrs;
+ }
+ }
+
+ return 0;
+
+err_sysfs_attrs:
+ for (attr_count--; attr_count >= 0; attr_count--) {
+ sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
+ &attrs[attr_count].attr);
+ }
+
+ kobject_put(rmidev->sysfs_dir);
+
+err_sysfs_dir:
+err_char_device:
+ rmidev_device_cleanup(dev_data);
+ kfree(dev_data);
+
+err_dev_data:
+ unregister_chrdev_region(dev_no, 1);
+
+err_device_region:
+ class_destroy(rmidev_device_class);
+
+err_device_class:
+ kfree(rmidev->fn_ptr);
+
+err_fn_ptr:
+ kfree(rmidev);
+
+err_rmidev:
+ return retval;
+}
+
+static void rmidev_remove_device(struct synaptics_rmi4_data *rmi4_data)
+{
+ unsigned char attr_count;
+ struct rmidev_data *dev_data;
+
+ if (!rmidev)
+ return;
+
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++)
+ sysfs_remove_file(rmidev->sysfs_dir, &attrs[attr_count].attr);
+
+ kobject_put(rmidev->sysfs_dir);
+
+ dev_data = rmidev->data;
+ if (dev_data) {
+ rmidev_device_cleanup(dev_data);
+ kfree(dev_data);
+ }
+
+ unregister_chrdev_region(rmidev->dev_no, 1);
+
+ class_destroy(rmidev_device_class);
+
+ kfree(rmidev->fn_ptr);
+ kfree(rmidev);
+
+ complete(&remove_complete);
+
+ return;
+}
+
+static int __init rmidev_module_init(void)
+{
+ synaptics_rmi4_new_function(RMI_DEV, true,
+ rmidev_init_device,
+ rmidev_remove_device,
+ NULL);
+ return 0;
+}
+
+static void __exit rmidev_module_exit(void)
+{
+ init_completion(&remove_complete);
+ synaptics_rmi4_new_function(RMI_DEV, false,
+ rmidev_init_device,
+ rmidev_remove_device,
+ NULL);
+ wait_for_completion(&remove_complete);
+ return;
+}
+
+module_init(rmidev_module_init);
+module_exit(rmidev_module_exit);
+
+MODULE_AUTHOR("Synaptics, Inc.");
+MODULE_DESCRIPTION("RMI4 RMI_Dev Module");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(SYNAPTICS_RMI4_DRIVER_VERSION);
diff --git a/kernel/include/linux/input/synaptics_dsx.h b/kernel/include/linux/input/synaptics_dsx.h
new file mode 100644
index 000000000000..b779e42a9bac
--- /dev/null
+++ b/kernel/include/linux/input/synaptics_dsx.h
@@ -0,0 +1,59 @@
+/*
+ * Synaptics RMI4 touchscreen driver
+ *
+ * Copyright (C) 2012 Synaptics Incorporated
+ *
+ * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
+ * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 _SYNAPTICS_DSX_H_
+#define _SYNAPTICS_DSX_H_
+
+/*
+ * struct synaptics_rmi4_capacitance_button_map - 0d button map
+ * @nbuttons: number of buttons
+ * @map: button map
+ */
+struct synaptics_rmi4_capacitance_button_map {
+ unsigned char nbuttons;
+ unsigned char *map;
+};
+
+/*
+ * struct synaptics_rmi4_platform_data - rmi4 platform data
+ * @x_flip: x flip flag
+ * @y_flip: y flip flag
+ * @regulator_en: regulator enable flag
+ * @irq_gpio: attention interrupt gpio
+ * @irq_flags: flags used by the irq
+ * @reset_gpio: reset gpio
+ * @panel_x: panel maximum values on the x
+ * @panel_y: panel maximum values on the y
+ * @gpio_config: pointer to gpio configuration function
+ * @capacitance_button_map: pointer to 0d button map
+ */
+struct synaptics_rmi4_platform_data {
+ bool x_flip;
+ bool y_flip;
+ bool regulator_en;
+ unsigned irq_gpio;
+ unsigned long irq_flags;
+ unsigned reset_gpio;
+ unsigned panel_x;
+ unsigned panel_y;
+ int (*gpio_config)(unsigned gpio, bool configure);
+ struct synaptics_rmi4_capacitance_button_map *capacitance_button_map;
+};
+
+#endif
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/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/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
index 0af4b336acd7..08c2b89de646 100755
--- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
@@ -964,7 +964,8 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream)
return -EINVAL;
}
- if (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_LE)
+ if ((prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_LE) ||
+ (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_3LE))
bits_per_sample = 24;
else if (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S32_LE)
bits_per_sample = 32;
diff --git a/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c b/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c
index 48180cf5e337..ad2f2e9865c3 100644
--- a/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c
+++ b/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c
@@ -1523,8 +1523,9 @@ static int msm_ds2_dap_get_param(u32 cmd, void *arg)
}
/* Return if invalid length */
- if (dolby_data->length >
- (DOLBY_MAX_LENGTH_INDIVIDUAL_PARAM - DOLBY_PARAM_PAYLOAD_SIZE)) {
+ if ((dolby_data->length >
+ (DOLBY_MAX_LENGTH_INDIVIDUAL_PARAM - DOLBY_PARAM_PAYLOAD_SIZE)) ||
+ (dolby_data->length <= 0)) {
pr_err("Invalid length %d", dolby_data->length);
rc = -EINVAL;
goto end;
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;
diff --git a/sound/usb/usb_audio_qmi_svc.c b/sound/usb/usb_audio_qmi_svc.c
index 9d2b75876bfe..94c051773fd9 100644
--- a/sound/usb/usb_audio_qmi_svc.c
+++ b/sound/usb/usb_audio_qmi_svc.c
@@ -405,12 +405,15 @@ static int prepare_qmi_response(struct snd_usb_substream *subs,
subs->interface, subs->altset_idx);
goto err;
}
- resp->bDelay = as->bDelay;
+ resp->data_path_delay = as->bDelay;
+ resp->data_path_delay_valid = 1;
fmt_v1 = (struct uac_format_type_i_discrete_descriptor *)fmt;
- resp->bSubslotSize = fmt_v1->bSubframeSize;
+ resp->usb_audio_subslot_size = fmt_v1->bSubframeSize;
+ resp->usb_audio_subslot_size_valid = 1;
} else if (protocol == UAC_VERSION_2) {
fmt_v2 = (struct uac_format_type_i_ext_descriptor *)fmt;
- resp->bSubslotSize = fmt_v2->bSubslotSize;
+ resp->usb_audio_subslot_size = fmt_v2->bSubslotSize;
+ resp->usb_audio_subslot_size_valid = 1;
} else {
pr_err("%s: unknown protocol version %x\n", __func__, protocol);
goto err;
@@ -424,11 +427,14 @@ static int prepare_qmi_response(struct snd_usb_substream *subs,
subs->interface, subs->altset_idx);
goto err;
}
- resp->bcdADC = ac->bcdADC;
+ resp->usb_audio_spec_revision = ac->bcdADC;
+ resp->usb_audio_spec_revision_valid = 1;
resp->slot_id = subs->dev->slot_id;
+ resp->slot_id_valid = 1;
memcpy(&resp->std_as_opr_intf_desc, &alts->desc, sizeof(alts->desc));
+ resp->std_as_opr_intf_desc_valid = 1;
ep = usb_pipe_endpoint(subs->dev, subs->data_endpoint->pipe);
if (!ep) {
@@ -437,6 +443,7 @@ static int prepare_qmi_response(struct snd_usb_substream *subs,
goto err;
}
memcpy(&resp->std_as_data_ep_desc, &ep->desc, sizeof(ep->desc));
+ resp->std_as_data_ep_desc_valid = 1;
xhci_pa = usb_get_xfer_ring_dma_addr(subs->dev, ep);
if (!xhci_pa) {
@@ -454,6 +461,8 @@ static int prepare_qmi_response(struct snd_usb_substream *subs,
goto err;
}
memcpy(&resp->std_as_sync_ep_desc, &ep->desc, sizeof(ep->desc));
+ resp->std_as_sync_ep_desc_valid = 1;
+
xhci_pa = usb_get_xfer_ring_dma_addr(subs->dev, ep);
if (!xhci_pa) {
pr_err("%s:failed to get sync ep ring dma address\n",
@@ -464,6 +473,7 @@ static int prepare_qmi_response(struct snd_usb_substream *subs,
}
resp->interrupter_num = uaudio_qdev->intr_num;
+ resp->interrupter_num_valid = 1;
/* map xhci data structures PA memory to iova */
@@ -570,6 +580,8 @@ skip_sync:
resp->xhci_mem_info.xfer_buff.va = PREPEND_SID_TO_IOVA(va,
uaudio_qdev->sid);
+ resp->xhci_mem_info_valid = 1;
+
if (!atomic_read(&uadev[card_num].in_use)) {
kref_init(&uadev[card_num].kref);
init_waitqueue_head(&uadev[card_num].disconnect_wq);
@@ -734,7 +746,7 @@ static void uaudio_dev_release(struct kref *kref)
static int handle_uaudio_stream_req(void *req_h, void *req)
{
struct qmi_uaudio_stream_req_msg_v01 *req_msg;
- struct qmi_uaudio_stream_resp_msg_v01 resp = {0};
+ struct qmi_uaudio_stream_resp_msg_v01 resp = {{0}, 0};
struct snd_usb_substream *subs;
struct snd_usb_audio *chip = NULL;
struct uaudio_qmi_svc *svc = uaudio_svc;
@@ -744,6 +756,13 @@ static int handle_uaudio_stream_req(void *req_h, void *req)
req_msg = (struct qmi_uaudio_stream_req_msg_v01 *)req;
+ if (!req_msg->audio_format_valid || !req_msg->bit_rate_valid ||
+ !req_msg->number_of_ch_valid || !req_msg->xfer_buff_size_valid) {
+ pr_err("%s: invalid request msg\n", __func__);
+ ret = -EINVAL;
+ goto response;
+ }
+
direction = req_msg->usb_token & SND_PCM_STREAM_DIRECTION;
pcm_dev_num = (req_msg->usb_token & SND_PCM_DEV_NUM_MASK) >> 8;
pcm_card_num = (req_msg->usb_token & SND_PCM_CARD_NUM_MASK) >> 16;
@@ -828,7 +847,12 @@ response:
uaudio_dev_release);
}
- resp.status = ret;
+ resp.usb_token = req_msg->usb_token;
+ resp.usb_token_valid = 1;
+ resp.internal_status = ret;
+ resp.internal_status_valid = 1;
+ resp.status = ret ? USB_AUDIO_STREAM_REQ_FAILURE_V01 : ret;
+ resp.status_valid = 1;
ret = qmi_send_resp_from_cb(svc->uaudio_svc_hdl, svc->curr_conn, req_h,
&uaudio_stream_resp_desc, &resp, sizeof(resp));
diff --git a/sound/usb/usb_audio_qmi_v01.c b/sound/usb/usb_audio_qmi_v01.c
index 31b1ba74d5c7..6f6f194e89fb 100644
--- a/sound/usb/usb_audio_qmi_v01.c
+++ b/sound/usb/usb_audio_qmi_v01.c
@@ -280,65 +280,92 @@ static struct elem_info usb_interface_descriptor_v01_ei[] = {
struct elem_info qmi_uaudio_stream_req_msg_v01_ei[] = {
{
- .data_type = QMI_UNSIGNED_4_BYTE,
+ .data_type = QMI_UNSIGNED_1_BYTE,
.elem_len = 1,
- .elem_size = sizeof(uint32_t),
+ .elem_size = sizeof(uint8_t),
.is_array = NO_ARRAY,
.tlv_type = 0x01,
.offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
- priv_data),
+ enable),
},
{
- .data_type = QMI_UNSIGNED_1_BYTE,
+ .data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
- .elem_size = sizeof(uint8_t),
+ .elem_size = sizeof(uint32_t),
.is_array = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
- enable),
+ usb_token),
},
{
- .data_type = QMI_UNSIGNED_4_BYTE,
+ .data_type = QMI_OPT_FLAG,
.elem_len = 1,
- .elem_size = sizeof(uint32_t),
+ .elem_size = sizeof(uint8_t),
.is_array = NO_ARRAY,
- .tlv_type = 0x03,
+ .tlv_type = 0x10,
.offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
- usb_token),
+ audio_format_valid),
},
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(uint32_t),
.is_array = NO_ARRAY,
- .tlv_type = 0x04,
+ .tlv_type = 0x10,
.offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
audio_format),
},
{
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ number_of_ch_valid),
+ },
+ {
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(uint32_t),
.is_array = NO_ARRAY,
- .tlv_type = 0x05,
+ .tlv_type = 0x11,
.offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
number_of_ch),
},
{
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ bit_rate_valid),
+ },
+ {
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(uint32_t),
.is_array = NO_ARRAY,
- .tlv_type = 0x06,
+ .tlv_type = 0x12,
.offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
bit_rate),
},
{
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ xfer_buff_size_valid),
+ },
+ {
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(uint32_t),
.is_array = NO_ARRAY,
- .tlv_type = 0x07,
+ .tlv_type = 0x13,
.offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
xfer_buff_size),
},
@@ -351,115 +378,256 @@ struct elem_info qmi_uaudio_stream_req_msg_v01_ei[] = {
struct elem_info qmi_uaudio_stream_resp_msg_v01_ei[] = {
{
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct qmi_response_type_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ status_valid),
+ },
+ {
+ .data_type = QMI_SIGNED_4_BYTE_ENUM,
+ .elem_len = 1,
+ .elem_size = sizeof(enum usb_audio_stream_status_enum_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ status),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ internal_status_valid),
+ },
+ {
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(uint32_t),
.is_array = NO_ARRAY,
- .tlv_type = 0x01,
+ .tlv_type = 0x11,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ internal_status),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
.offset = offsetof(
struct qmi_uaudio_stream_resp_msg_v01,
- priv_data),
+ slot_id_valid),
},
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(uint32_t),
.is_array = NO_ARRAY,
- .tlv_type = 0x02,
+ .tlv_type = 0x12,
.offset = offsetof(
struct qmi_uaudio_stream_resp_msg_v01,
- status),
+ slot_id),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ usb_token_valid),
},
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(uint32_t),
.is_array = NO_ARRAY,
- .tlv_type = 0x03,
+ .tlv_type = 0x13,
.offset = offsetof(
struct qmi_uaudio_stream_resp_msg_v01,
- slot_id),
+ usb_token),
},
{
- .data_type = QMI_UNSIGNED_1_BYTE,
+ .data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(uint8_t),
.is_array = NO_ARRAY,
- .tlv_type = 0x04,
+ .tlv_type = 0x14,
.offset = offsetof(
struct qmi_uaudio_stream_resp_msg_v01,
- bSubslotSize),
+ std_as_opr_intf_desc_valid),
},
{
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct usb_interface_descriptor_v01),
.is_array = NO_ARRAY,
- .tlv_type = 0x05,
+ .tlv_type = 0x14,
.offset = offsetof(
struct qmi_uaudio_stream_resp_msg_v01,
std_as_opr_intf_desc),
.ei_array = usb_interface_descriptor_v01_ei,
},
{
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x15,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ std_as_data_ep_desc_valid),
+ },
+ {
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct usb_endpoint_descriptor_v01),
.is_array = NO_ARRAY,
- .tlv_type = 0x06,
+ .tlv_type = 0x15,
.offset = offsetof(
struct qmi_uaudio_stream_resp_msg_v01,
std_as_data_ep_desc),
.ei_array = usb_endpoint_descriptor_v01_ei,
},
{
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x16,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ std_as_sync_ep_desc_valid),
+ },
+ {
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct usb_endpoint_descriptor_v01),
.is_array = NO_ARRAY,
- .tlv_type = 0x07,
+ .tlv_type = 0x16,
.offset = offsetof(
struct qmi_uaudio_stream_resp_msg_v01,
std_as_sync_ep_desc),
.ei_array = usb_endpoint_descriptor_v01_ei,
},
{
- .data_type = QMI_UNSIGNED_1_BYTE,
+ .data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(uint8_t),
.is_array = NO_ARRAY,
- .tlv_type = 0x08,
+ .tlv_type = 0x17,
.offset = offsetof(
struct qmi_uaudio_stream_resp_msg_v01,
- bDelay),
+ usb_audio_spec_revision_valid),
},
{
.data_type = QMI_UNSIGNED_2_BYTE,
.elem_len = 1,
.elem_size = sizeof(uint16_t),
.is_array = NO_ARRAY,
- .tlv_type = 0x09,
+ .tlv_type = 0x17,
.offset = offsetof(
struct qmi_uaudio_stream_resp_msg_v01,
- bcdADC),
+ usb_audio_spec_revision),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x18,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ data_path_delay_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x18,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ data_path_delay),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x19,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ usb_audio_subslot_size_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x19,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ usb_audio_subslot_size),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x1A,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ xhci_mem_info_valid),
},
{
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct apps_mem_info_v01),
.is_array = NO_ARRAY,
- .tlv_type = 0x0A,
+ .tlv_type = 0x1A,
.offset = offsetof(
struct qmi_uaudio_stream_resp_msg_v01,
xhci_mem_info),
.ei_array = apps_mem_info_v01_ei,
},
{
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x1B,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ interrupter_num_valid),
+ },
+ {
.data_type = QMI_UNSIGNED_1_BYTE,
.elem_len = 1,
.elem_size = sizeof(uint8_t),
.is_array = NO_ARRAY,
- .tlv_type = 0x0B,
+ .tlv_type = 0x1B,
.offset = offsetof(
struct qmi_uaudio_stream_resp_msg_v01,
interrupter_num),
@@ -473,13 +641,14 @@ struct elem_info qmi_uaudio_stream_resp_msg_v01_ei[] = {
struct elem_info qmi_uaudio_stream_ind_msg_v01_ei[] = {
{
- .data_type = QMI_UNSIGNED_4_BYTE,
+ .data_type = QMI_SIGNED_4_BYTE_ENUM,
.elem_len = 1,
- .elem_size = sizeof(uint32_t),
+ .elem_size = sizeof(
+ enum usb_audio_device_indication_enum_v01),
.is_array = NO_ARRAY,
.tlv_type = 0x01,
.offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
- usb_token),
+ dev_event),
},
{
.data_type = QMI_UNSIGNED_4_BYTE,
@@ -488,76 +657,175 @@ struct elem_info qmi_uaudio_stream_ind_msg_v01_ei[] = {
.is_array = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
- priv_data),
+ slot_id),
},
{
- .data_type = QMI_UNSIGNED_4_BYTE,
+ .data_type = QMI_OPT_FLAG,
.elem_len = 1,
- .elem_size = sizeof(uint32_t),
+ .elem_size = sizeof(uint8_t),
.is_array = NO_ARRAY,
- .tlv_type = 0x03,
+ .tlv_type = 0x10,
.offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
- status),
+ usb_token_valid),
},
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(uint32_t),
.is_array = NO_ARRAY,
- .tlv_type = 0x04,
+ .tlv_type = 0x10,
.offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
- slot_id),
+ usb_token),
},
{
- .data_type = QMI_UNSIGNED_1_BYTE,
+ .data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(uint8_t),
.is_array = NO_ARRAY,
- .tlv_type = 0x05,
+ .tlv_type = 0x11,
.offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
- bSubslotSize),
+ std_as_opr_intf_desc_valid),
},
{
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct usb_interface_descriptor_v01),
.is_array = NO_ARRAY,
- .tlv_type = 0x06,
+ .tlv_type = 0x11,
.offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
std_as_opr_intf_desc),
.ei_array = usb_interface_descriptor_v01_ei,
},
{
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ std_as_data_ep_desc_valid),
+ },
+ {
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct usb_endpoint_descriptor_v01),
.is_array = NO_ARRAY,
- .tlv_type = 0x07,
+ .tlv_type = 0x12,
.offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
std_as_data_ep_desc),
.ei_array = usb_endpoint_descriptor_v01_ei,
},
{
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ std_as_sync_ep_desc_valid),
+ },
+ {
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct usb_endpoint_descriptor_v01),
.is_array = NO_ARRAY,
- .tlv_type = 0x08,
+ .tlv_type = 0x13,
.offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
std_as_sync_ep_desc),
.ei_array = usb_endpoint_descriptor_v01_ei,
},
{
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ usb_audio_spec_revision_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint16_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ usb_audio_spec_revision),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x15,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ data_path_delay_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x15,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ data_path_delay),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x16,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ usb_audio_subslot_size_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x16,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ usb_audio_subslot_size),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x17,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ xhci_mem_info_valid),
+ },
+ {
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct apps_mem_info_v01),
.is_array = NO_ARRAY,
- .tlv_type = 0x09,
+ .tlv_type = 0x17,
.offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
xhci_mem_info),
.ei_array = apps_mem_info_v01_ei,
},
{
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x18,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ interrupter_num_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x18,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ interrupter_num),
+ },
+ {
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
.is_array = QMI_COMMON_TLV_TYPE,
diff --git a/sound/usb/usb_audio_qmi_v01.h b/sound/usb/usb_audio_qmi_v01.h
index 7ad1ab8a61a9..aa1018a22105 100644
--- a/sound/usb/usb_audio_qmi_v01.h
+++ b/sound/usb/usb_audio_qmi_v01.h
@@ -13,7 +13,7 @@
#ifndef USB_QMI_V01_H
#define USB_QMI_V01_H
-#define UAUDIO_STREAM_SERVICE_ID_V01 0x41C
+#define UAUDIO_STREAM_SERVICE_ID_V01 0x41D
#define UAUDIO_STREAM_SERVICE_VERS_V01 0x01
#define QMI_UAUDIO_STREAM_RESP_V01 0x0001
@@ -58,46 +58,93 @@ struct usb_interface_descriptor_v01 {
uint8_t iInterface;
};
+enum usb_audio_stream_status_enum_v01 {
+ USB_AUDIO_STREAM_STATUS_ENUM_MIN_VAL_V01 = INT_MIN,
+ USB_AUDIO_STREAM_REQ_SUCCESS_V01 = 0,
+ USB_AUDIO_STREAM_REQ_FAILURE_V01 = 1,
+ USB_AUDIO_STREAM_REQ_FAILURE_NOT_FOUND_V01 = 2,
+ USB_AUDIO_STREAM_REQ_FAILURE_INVALID_PARAM_V01 = 3,
+ USB_AUDIO_STREAM_REQ_FAILURE_MEMALLOC_V01 = 4,
+ USB_AUDIO_STREAM_STATUS_ENUM_MAX_VAL_V01 = INT_MAX,
+};
+
+enum usb_audio_device_indication_enum_v01 {
+ USB_AUDIO_DEVICE_INDICATION_ENUM_MIN_VAL_V01 = INT_MIN,
+ USB_AUDIO_DEV_CONNECT_V01 = 0,
+ USB_AUDIO_DEV_DISCONNECT_V01 = 1,
+ USB_AUDIO_DEV_SUSPEND_V01 = 2,
+ USB_AUDIO_DEV_RESUME_V01 = 3,
+ USB_AUDIO_DEVICE_INDICATION_ENUM_MAX_VAL_V01 = INT_MAX,
+};
+
struct qmi_uaudio_stream_req_msg_v01 {
- uint32_t priv_data;
uint8_t enable;
uint32_t usb_token;
+ uint8_t audio_format_valid;
uint32_t audio_format;
+ uint8_t number_of_ch_valid;
uint32_t number_of_ch;
+ uint8_t bit_rate_valid;
uint32_t bit_rate;
+ uint8_t xfer_buff_size_valid;
uint32_t xfer_buff_size;
};
-#define QMI_UAUDIO_STREAM_REQ_MSG_V01_MAX_MSG_LEN 46
+#define QMI_UAUDIO_STREAM_REQ_MSG_V01_MAX_MSG_LEN 39
extern struct elem_info qmi_uaudio_stream_req_msg_v01_ei[];
struct qmi_uaudio_stream_resp_msg_v01 {
- uint32_t priv_data;
- uint32_t status;
+ struct qmi_response_type_v01 resp;
+ uint8_t status_valid;
+ enum usb_audio_stream_status_enum_v01 status;
+ uint8_t internal_status_valid;
+ uint32_t internal_status;
+ uint8_t slot_id_valid;
uint32_t slot_id;
- uint8_t bSubslotSize;
+ uint8_t usb_token_valid;
+ uint32_t usb_token;
+ uint8_t std_as_opr_intf_desc_valid;
struct usb_interface_descriptor_v01 std_as_opr_intf_desc;
+ uint8_t std_as_data_ep_desc_valid;
struct usb_endpoint_descriptor_v01 std_as_data_ep_desc;
+ uint8_t std_as_sync_ep_desc_valid;
struct usb_endpoint_descriptor_v01 std_as_sync_ep_desc;
- uint8_t bDelay;
- uint16_t bcdADC;
+ uint8_t usb_audio_spec_revision_valid;
+ uint16_t usb_audio_spec_revision;
+ uint8_t data_path_delay_valid;
+ uint8_t data_path_delay;
+ uint8_t usb_audio_subslot_size_valid;
+ uint8_t usb_audio_subslot_size;
+ uint8_t xhci_mem_info_valid;
struct apps_mem_info_v01 xhci_mem_info;
+ uint8_t interrupter_num_valid;
uint8_t interrupter_num;
};
-#define QMI_UAUDIO_STREAM_RESP_MSG_V01_MAX_MSG_LEN 177
+#define QMI_UAUDIO_STREAM_RESP_MSG_V01_MAX_MSG_LEN 191
extern struct elem_info qmi_uaudio_stream_resp_msg_v01_ei[];
struct qmi_uaudio_stream_ind_msg_v01 {
- uint32_t usb_token;
- uint32_t priv_data;
- uint32_t status;
+ enum usb_audio_device_indication_enum_v01 dev_event;
uint32_t slot_id;
- uint8_t bSubslotSize;
+ uint8_t usb_token_valid;
+ uint32_t usb_token;
+ uint8_t std_as_opr_intf_desc_valid;
struct usb_interface_descriptor_v01 std_as_opr_intf_desc;
+ uint8_t std_as_data_ep_desc_valid;
struct usb_endpoint_descriptor_v01 std_as_data_ep_desc;
+ uint8_t std_as_sync_ep_desc_valid;
struct usb_endpoint_descriptor_v01 std_as_sync_ep_desc;
+ uint8_t usb_audio_spec_revision_valid;
+ uint16_t usb_audio_spec_revision;
+ uint8_t data_path_delay_valid;
+ uint8_t data_path_delay;
+ uint8_t usb_audio_subslot_size_valid;
+ uint8_t usb_audio_subslot_size;
+ uint8_t xhci_mem_info_valid;
struct apps_mem_info_v01 xhci_mem_info;
+ uint8_t interrupter_num_valid;
+ uint8_t interrupter_num;
};
-#define QMI_UAUDIO_STREAM_IND_MSG_V01_MAX_MSG_LEN 171
+#define QMI_UAUDIO_STREAM_IND_MSG_V01_MAX_MSG_LEN 177
extern struct elem_info qmi_uaudio_stream_ind_msg_v01_ei[];
#endif