diff options
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, ¶m1, 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, ¶m1, 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, ¶m1, 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, ¶m->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, ¶m->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, ¶m1, CALIB_RATIOMETRIC); + rc = qpnp_adc_map_temp_voltage(adcmap_100k_104ef_104fb, ARRAY_SIZE(adcmap_100k_104ef_104fb), param->low_thr_temp, ¶m->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, ¶m->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, ¶m->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, ®_addr, 1, ®_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, ®_addr, 1, ®_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, ®_addr, 1, ®_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, ¬ify_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, ¬ify_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, ¬ify_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, ¬ify_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, >p_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(>p_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, >p_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(>p_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 Binary files differnew file mode 100644 index 000000000000..b0c1b4d9e770 --- /dev/null +++ b/kernel/Documentation/firmware_updater/synaptics_fw_updater 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(®ister_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(®ister_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 |
