diff options
145 files changed, 25090 insertions, 4971 deletions
diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt b/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt index 2bd7653af5a3..cee9b942a9e3 100644 --- a/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt +++ b/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt @@ -182,6 +182,14 @@ Properties: command register for each of the two clusters managed by the OSM controller. +- qcom,apm-threshold-voltage + Usage: required + Value type: <u32> + Definition: Specifies the APM threshold voltage in microvolts. If the + VDD_APCC supply voltage is above or at this level, then the + APM is switched to use VDD_APCC. If VDD_APCC is below + this level, then the APM is switched to use VDD_MX. + - qcom,apm-mode-ctl Usage: required Value type: <prop-encoded-array> @@ -392,6 +400,8 @@ Example: qcom,apm-ctrl-status = <0x179d000c 0x179d0018>; + qcom,apm-threshold-voltage = <832000>; + qcom,pwrcl-apcs-mem-acc-cfg = <0x179d1360 0x179d1364 0x179d1364>; qcom,perfcl-apcs-mem-acc-cfg = 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/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/regulator/cprh-kbss-regulator.txt b/Documentation/devicetree/bindings/regulator/cprh-kbss-regulator.txt index b9143cfc2587..833fb645b92a 100644 --- a/Documentation/devicetree/bindings/regulator/cprh-kbss-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/cprh-kbss-regulator.txt @@ -42,10 +42,21 @@ KBSS specific properties: - qcom,apm-threshold-voltage Usage: optional Value type: <u32> - Definition: Specifies the APM threshold voltage in microvolts. If the - VDD_APCC supply voltage is above this level, then the APM is - switched to use VDD_APCC. If VDD_APCC is below this level, - then the APM is switched to use VDD_MX. + Definition: Specifies the APM threshold voltage in microvolts. The + floor to ceiling range for every corner is adjusted to ensure + it does not intersect this voltage. The value of this property + must match with the APM threshold voltage defined in the OSM + device to ensure that if the VDD_APCC supply voltage is above + this level, then the APM is switched to use VDD_APCC and if + VDD_APCC is below this level, then the APM is switched to use + VDD_MX. + +- qcom,apm-crossover-voltage + Usage: required if qcom,apm-threshold-voltage is specified + Value type: <u32> + Definition: Specifies the APM crossover voltage in microvolts which + corresponds to the voltage the VDD supply must be set at + during an APM switch transition. - qcom,apm-hysteresis-voltage Usage: optional diff --git a/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi index 25f3be5fff09..1ef5e6351aa6 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi @@ -174,7 +174,11 @@ "msm-dai-q6-dev.28672", "msm-dai-q6-dev.28673"; asoc-codec = <&stub_codec>; asoc-codec-names = "msm-stub-codec.1"; - qcom,wsa-max-devs = <0>; + qcom,wsa-max-devs = <2>; + qcom,wsa-devs = <&wsa881x_0211>, <&wsa881x_0212>, + <&wsa881x_0213>, <&wsa881x_0214>; + qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrRight", + "SpkrLeft", "SpkrRight"; }; cpe: qcom,msm-cpe-lsm { diff --git a/arch/arm/boot/dts/qcom/msmcobalt-bus.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-bus.dtsi index 3c39a61c4328..86decf438430 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-bus.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-bus.dtsi @@ -131,6 +131,16 @@ clock-names = "bus_clk", "bus_a_clk"; clocks = <&clock_gcc clk_mmssnoc_axi_clk>, <&clock_gcc clk_mmssnoc_axi_a_clk>; + clk-mdss-axi-no-rate-supply = + <&gdsc_mdss>; + clk-mdss-ahb-no-rate-supply = + <&gdsc_mdss>; + clk-camss-ahb-no-rate-supply = + <&gdsc_camss_top>; + clk-video-ahb-no-rate-supply = + <&gdsc_venus>; + clk-video-axi-no-rate-supply = + <&gdsc_venus>; qcom,node-qos-clks { clock-names = "clk-noc-cfg-ahb-no-rate", @@ -141,6 +151,7 @@ "clk-video-ahb-no-rate", "clk-video-axi-no-rate"; clocks = + <&clock_gcc clk_mmssnoc_axi_clk>, <&clock_gcc clk_gcc_mmss_noc_cfg_ahb_clk>, <&clock_mmss clk_mmss_mnoc_ahb_clk>, <&clock_mmss clk_mmss_mdss_ahb_clk>, diff --git a/arch/arm/boot/dts/qcom/msmcobalt-coresight.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-coresight.dtsi index c0777a7e193a..96698951bd6c 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-coresight.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-coresight.dtsi @@ -75,6 +75,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-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-regulator.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi index 0f3fe60465c1..5833b30d1fd1 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi @@ -82,7 +82,7 @@ rpm-regulator-smpa5 { status = "okay"; pmcobalt_s5: regulator-s5 { - regulator-min-microvolt = <2040000>; + regulator-min-microvolt = <1904000>; regulator-max-microvolt = <2040000>; status = "okay"; }; @@ -91,7 +91,7 @@ rpm-regulator-smpa7 { status = "okay"; pmcobalt_s7: regulator-s7 { - regulator-min-microvolt = <1028000>; + regulator-min-microvolt = <900000>; regulator-max-microvolt = <1028000>; status = "okay"; }; @@ -576,6 +576,7 @@ qcom,cpr-voltage-settling-time = <1760>; qcom,apm-threshold-voltage = <832000>; + qcom,apm-crossover-voltage = <880000>; qcom,apm-hysteresis-voltage = <32000>; qcom,voltage-step = <4000>; qcom,voltage-base = <352000>; @@ -737,6 +738,7 @@ qcom,cpr-voltage-settling-time = <1760>; qcom,apm-threshold-voltage = <832000>; + qcom,apm-crossover-voltage = <880000>; qcom,apm-hysteresis-voltage = <32000>; qcom,voltage-step = <4000>; qcom,voltage-base = <352000>; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-wcd.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-wcd.dtsi new file mode 100644 index 000000000000..7ae084debe06 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msmcobalt-wcd.dtsi @@ -0,0 +1,96 @@ +/* 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. + */ + +&slim_aud { + tasha_codec { + wsa_spkr_sd1: msm_cdc_pinctrll { + compatible = "qcom,msm-cdc-pinctrl"; + pinctrl-names = "aud_active", "aud_sleep"; + pinctrl-0 = <&spkr_1_sd_n_active>; + pinctrl-1 = <&spkr_1_sd_n_sleep>; + }; + + wsa_spkr_sd2: msm_cdc_pinctrlr { + compatible = "qcom,msm-cdc-pinctrl"; + pinctrl-names = "aud_active", "aud_sleep"; + pinctrl-0 = <&spkr_2_sd_n_active>; + pinctrl-1 = <&spkr_2_sd_n_sleep>; + }; + }; + + tavil_codec { + wcd: wcd_pinctrl@5 { + compatible = "qcom,wcd-pinctrl"; + qcom,num-gpios = <5>; + gpio-controller; + #gpio-cells = <2>; + + spkr_1_wcd_en_active: spkr_1_wcd_en_active { + mux { + pins = "gpio2"; + }; + + config { + pins = "gpio2"; + output-high; + }; + }; + + spkr_1_wcd_en_sleep: spkr_1_wcd_en_sleep { + mux { + pins = "gpio2"; + }; + + config { + pins = "gpio2"; + input-enable; + }; + }; + + spkr_2_wcd_en_active: spkr_2_sd_n_active { + mux { + pins = "gpio3"; + }; + + config { + pins = "gpio3"; + output-high; + }; + }; + + spkr_2_wcd_en_sleep: spkr_2_sd_n_sleep { + mux { + pins = "gpio3"; + }; + + config { + pins = "gpio3"; + input-enable; + }; + }; + }; + + wsa_spkr_wcd_sd1: msm_cdc_pinctrll { + compatible = "qcom,msm-cdc-pinctrl"; + pinctrl-names = "aud_active", "aud_sleep"; + pinctrl-0 = <&spkr_1_wcd_en_active>; + pinctrl-1 = <&spkr_1_wcd_en_sleep>; + }; + + wsa_spkr_wcd_sd2: msm_cdc_pinctrlr { + compatible = "qcom,msm-cdc-pinctrl"; + pinctrl-names = "aud_active", "aud_sleep"; + pinctrl-0 = <&spkr_2_wcd_en_active>; + pinctrl-1 = <&spkr_2_wcd_en_sleep>; + }; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-wsa881x.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-wsa881x.dtsi index f9bb0e0d9dc3..baf05c1c241b 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-wsa881x.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-wsa881x.dtsi @@ -9,24 +9,7 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ - -&soc { - wsa_spkr_sd1: msm_cdc_pinctrl@65 { - compatible = "qcom,msm-cdc-pinctrl"; - qcom,cdc-rst-n-gpio = <&tlmm 65 0>; - pinctrl-names = "aud_active", "aud_sleep"; - pinctrl-0 = <&spkr_1_sd_n_active>; - pinctrl-1 = <&spkr_1_sd_n_sleep>; - }; - - wsa_spkr_sd2: msm_cdc_pinctrl@66 { - compatible = "qcom,msm-cdc-pinctrl"; - qcom,cdc-rst-n-gpio = <&tlmm 66 0>; - pinctrl-names = "aud_active", "aud_sleep"; - pinctrl-0 = <&spkr_2_sd_n_active>; - pinctrl-1 = <&spkr_2_sd_n_sleep>; - }; -}; +#include "msmcobalt-wcd.dtsi" &slim_aud { tasha_codec { @@ -70,25 +53,25 @@ wsa881x_0211: wsa881x@20170211 { compatible = "qcom,wsa881x"; reg = <0x00 0x20170211>; - qcom,spkr-sd-n-node = <&wsa_spkr_sd1>; + qcom,spkr-sd-n-node = <&wsa_spkr_wcd_sd1>; }; wsa881x_0212: wsa881x@20170212 { compatible = "qcom,wsa881x"; reg = <0x00 0x20170212>; - qcom,spkr-sd-n-node = <&wsa_spkr_sd2>; + qcom,spkr-sd-n-node = <&wsa_spkr_wcd_sd2>; }; wsa881x_0213: wsa881x@21170213 { compatible = "qcom,wsa881x"; reg = <0x00 0x21170213>; - qcom,spkr-sd-n-node = <&wsa_spkr_sd1>; + qcom,spkr-sd-n-node = <&wsa_spkr_wcd_sd1>; }; wsa881x_0214: wsa881x@21170214 { compatible = "qcom,wsa881x"; reg = <0x00 0x21170214>; - qcom,spkr-sd-n-node = <&wsa_spkr_sd2>; + qcom,spkr-sd-n-node = <&wsa_spkr_wcd_sd2>; }; }; }; diff --git a/arch/arm/boot/dts/qcom/msmcobalt.dtsi b/arch/arm/boot/dts/qcom/msmcobalt.dtsi index e748783b0c7d..5dc530ea8494 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt.dtsi @@ -804,21 +804,21 @@ < 576000000 0x0504001e 0x03200020 0x1 >, < 633600000 0x05040021 0x03200020 0x1 >, < 710400000 0x05040025 0x03200020 0x1 >, - < 806400000 0x0504002a 0x04200020 0x2 >, - < 883200000 0x0404002e 0x04250025 0x2 >, - < 960000000 0x04040032 0x05280028 0x2 >, - < 1036800000 0x04040036 0x052b002b 0x3 >, - < 1113600000 0x0404003a 0x052e002e 0x3 >, - < 1190400000 0x0404003e 0x06320032 0x3 >, - < 1248000000 0x04040041 0x06340034 0x3 >, - < 1324800000 0x04040045 0x06370037 0x3 >, - < 1401600000 0x04040049 0x073a003a 0x3 >, - < 1478400000 0x0404004d 0x073e003e 0x3 >, - < 1574400000 0x04040052 0x08420042 0x4 >, - < 1651200000 0x04040056 0x08450045 0x4 >, - < 1728000000 0x0404005a 0x08480048 0x4 >, - < 1804800000 0x0404005e 0x094b004b 0x4 >, - < 1881600000 0x04040062 0x094e004e 0x4 >; + < 806400000 0x0504002a 0x04200020 0x1 >, + < 883200000 0x0404002e 0x04250025 0x1 >, + < 960000000 0x04040032 0x05280028 0x1 >, + < 1036800000 0x04040036 0x052b002b 0x2 >, + < 1113600000 0x0404003a 0x052e002e 0x2 >, + < 1190400000 0x0404003e 0x06320032 0x2 >, + < 1248000000 0x04040041 0x06340034 0x2 >, + < 1324800000 0x04040045 0x06370037 0x2 >, + < 1401600000 0x04040049 0x073a003a 0x2 >, + < 1478400000 0x0404004d 0x073e003e 0x2 >, + < 1574400000 0x04040052 0x08420042 0x3 >, + < 1651200000 0x04040056 0x08450045 0x3 >, + < 1728000000 0x0404005a 0x08480048 0x3 >, + < 1804800000 0x0404005e 0x094b004b 0x3 >, + < 1881600000 0x04040062 0x094e004e 0x3 >; qcom,perfcl-speedbin0-v0 = < 300000000 0x0004000f 0x01200020 0x1 >, @@ -888,6 +888,7 @@ <0x8fff0036 0x8fff003a 0x0fff0036>, <0x8fff003d 0x8fff0041 0x0fff003d>; + qcom,apm-threshold-voltage = <832000>; qcom,boost-fsm-en; qcom,safe-fsm-en; qcom,ps-fsm-en; @@ -900,13 +901,11 @@ qcom,perfcl-apcs-mem-acc-cfg = <0x179d1368 0x179d136C 0x179d1370>; qcom,pwrcl-apcs-mem-acc-val = - <0x00000000 0x10000000 0x10000000>, - <0x00000000 0x10000000 0x10000000>, + <0x00000000 0x80000000 0x80000000>, <0x00000000 0x00000000 0x00000000>, <0x00000000 0x00000001 0x00000001>; qcom,perfcl-apcs-mem-acc-val = - <0x00000000 0x00000000 0x10000000>, - <0x00000000 0x00000000 0x10000000>, + <0x00000000 0x00000000 0x80000000>, <0x00000000 0x00000000 0x00000000>, <0x00000000 0x00000000 0x00000001>; @@ -966,8 +965,12 @@ qcom,do-not-use-ch-gsi-20; qcom,ipa-wdi2; qcom,use-64-bit-dma-mask; - clock-names = "core_clk"; - clocks = <&clock_gcc clk_ipa_clk>; + clocks = <&clock_gcc clk_ipa_clk>, + <&clock_gcc clk_aggre2_noc_clk>; + clock-names = "core_clk", "smmu_clk"; + qcom,arm-smmu; + qcom,smmu-disable-htw; + qcom,smmu-s1-bypass; qcom,msm-bus,name = "ipa"; qcom,msm-bus,num-cases = <4>; qcom,msm-bus,num-paths = <3>; @@ -1074,6 +1077,23 @@ compatible = "qcom,smp2pgpio-map-ipa-1-in"; gpios = <&smp2pgpio_ipa_1_in 0 0>; }; + + ipa_smmu_ap: ipa_smmu_ap { + compatible = "qcom,ipa-smmu-ap-cb"; + iommus = <&anoc2_smmu 0x18e0>; + qcom,iova-mapping = <0x10000000 0x40000000>; + }; + + ipa_smmu_wlan: ipa_smmu_wlan { + compatible = "qcom,ipa-smmu-wlan-cb"; + iommus = <&anoc2_smmu 0x18e1>; + }; + + ipa_smmu_uc: ipa_smmu_uc { + compatible = "qcom,ipa-smmu-uc-cb"; + iommus = <&anoc2_smmu 0x18e2>; + qcom,iova-mapping = <0x40000000 0x20000000>; + }; }; qcom,ipa_fws@1e08000 { diff --git a/arch/arm/boot/dts/qcom/msmfalcon-ion.dtsi b/arch/arm/boot/dts/qcom/msmfalcon-ion.dtsi new file mode 100644 index 000000000000..f6deef335844 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msmfalcon-ion.dtsi @@ -0,0 +1,52 @@ +/* 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. + */ + +&soc { + qcom,ion { + compatible = "qcom,msm-ion"; + #address-cells = <1>; + #size-cells = <0>; + + system_heap: qcom,ion-heap@25 { + reg = <25>; + qcom,ion-heap-type = "SYSTEM"; + }; + + system_contig_heap: qcom,ion-heap@21 { + reg = <21>; + qcom,ion-heap-type = "SYSTEM_CONTIG"; + }; + + qcom,ion-heap@22 { /* ADSP HEAP */ + reg = <22>; + memory-region = <&adsp_mem>; + qcom,ion-heap-type = "DMA"; + }; + + qcom,ion-heap@27 { /* QSEECOM HEAP */ + reg = <27>; + memory-region = <&qseecom_mem>; + qcom,ion-heap-type = "DMA"; + }; + + qcom,ion-heap@10 { /* SECURE DISPLAY HEAP */ + reg = <10>; + memory-region = <&secure_display_memory>; + qcom,ion-heap-type = "HYP_CMA"; + }; + + qcom,ion-heap@9 { + reg = <9>; + qcom,ion-heap-type = "SYSTEM_SECURE"; + }; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msmfalcon.dtsi b/arch/arm/boot/dts/qcom/msmfalcon.dtsi index a737f5597c18..ff1f085499b7 100644 --- a/arch/arm/boot/dts/qcom/msmfalcon.dtsi +++ b/arch/arm/boot/dts/qcom/msmfalcon.dtsi @@ -130,6 +130,68 @@ }; soc: soc { }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + removed_regions: removed_regions@85800000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0x0 0x85800000 0x0 0x3700000>; + }; + + modem_fw_mem: modem_fw_region@8ac00000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0x0 0x8ac00000 0x0 0x7800000>; + }; + + adsp_fw_mem: adsp_fw_region@92400000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0x0 0x92400000 0x0 0x1e00000>; + }; + + cdsp_fw_mem: cdsp_fw_region@94200000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0x0 0x94200000 0x0 0x600000>; + }; + + venus_fw_mem: venus_fw_region { + compatible = "shared-dma-pool"; + alloc-ranges = <0x0 0x80000000 0x0 0x20000000>; + reusable; + alignment = <0x0 0x400000>; + size = <0x0 0x800000>; + }; + + adsp_mem: adsp_region { + compatible = "shared-dma-pool"; + alloc-ranges = <0x0 0x00000000 0x0 0xffffffff>; + reusable; + alignment = <0x0 0x400000>; + size = <0x0 0x400000>; + }; + + qseecom_mem: qseecom_region { + compatible = "shared-dma-pool"; + alloc-ranges = <0x0 0x00000000 0x0 0xffffffff>; + reusable; + alignment = <0x0 0x400000>; + size = <0x0 0x1400000>; + }; + + secure_display_memory: secure_region { + compatible = "shared-dma-pool"; + alloc-ranges = <0x0 0x00000000 0x0 0xffffffff>; + reusable; + alignment = <0x0 0x400000>; + size = <0x0 0x5c00000>; + }; + }; }; &soc { @@ -344,3 +406,5 @@ }; }; }; + +#include "msmfalcon-ion.dtsi" diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index bf6a92504175..d41957eae6ef 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -2284,6 +2284,7 @@ void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, set_dma_ops(dev, dma_ops); } +EXPORT_SYMBOL(arch_setup_dma_ops); void arch_teardown_dma_ops(struct device *dev) { diff --git a/arch/arm64/configs/msm-perf_defconfig b/arch/arm64/configs/msm-perf_defconfig index fc2cce36bff9..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 @@ -408,7 +409,10 @@ CONFIG_SND_SOC=y CONFIG_SND_SOC_MSM8996=y CONFIG_UHID=y CONFIG_HID_APPLE=y +CONFIG_HID_ELECOM=y +CONFIG_HID_MAGICMOUSE=y CONFIG_HID_MICROSOFT=y +CONFIG_HID_MULTITOUCH=y CONFIG_USB=y CONFIG_USB_ANNOUNCE_NEW_DEVICES=y CONFIG_USB_XHCI_HCD=y diff --git a/arch/arm64/configs/msm_defconfig b/arch/arm64/configs/msm_defconfig index 38e489936895..c2902be72848 100644 --- a/arch/arm64/configs/msm_defconfig +++ b/arch/arm64/configs/msm_defconfig @@ -397,7 +397,10 @@ CONFIG_SND_SOC=y CONFIG_SND_SOC_MSM8996=y CONFIG_UHID=y CONFIG_HID_APPLE=y +CONFIG_HID_ELECOM=y +CONFIG_HID_MAGICMOUSE=y CONFIG_HID_MICROSOFT=y +CONFIG_HID_MULTITOUCH=y CONFIG_USB=y CONFIG_USB_XHCI_HCD=y CONFIG_USB_EHCI_HCD=y diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig index 72584917f930..742ab830a812 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 @@ -390,7 +391,10 @@ CONFIG_SND_SOC=y CONFIG_SND_SOC_MSMCOBALT=y CONFIG_UHID=y CONFIG_HID_APPLE=y +CONFIG_HID_ELECOM=y +CONFIG_HID_MAGICMOUSE=y CONFIG_HID_MICROSOFT=y +CONFIG_HID_MULTITOUCH=y CONFIG_USB=y CONFIG_USB_ANNOUNCE_NEW_DEVICES=y CONFIG_USB_XHCI_HCD=y diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index e708960cf28b..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 @@ -395,7 +396,10 @@ CONFIG_SND_SOC=y CONFIG_SND_SOC_MSMCOBALT=y CONFIG_UHID=y CONFIG_HID_APPLE=y +CONFIG_HID_ELECOM=y +CONFIG_HID_MAGICMOUSE=y CONFIG_HID_MICROSOFT=y +CONFIG_HID_MULTITOUCH=y CONFIG_USB=y CONFIG_USB_XHCI_HCD=y CONFIG_USB_EHCI_HCD=y diff --git a/drivers/clk/msm/Makefile b/drivers/clk/msm/Makefile index 107af19abff2..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 @@ -27,4 +27,4 @@ ifeq ($(CONFIG_COMMON_CLK_MSM), y) endif obj-$(CONFIG_COMMON_CLK_MSM) += gdsc.o -obj-$(CONFIG_COMMON_CLK_MSM)-y += mdss/ +obj-$(CONFIG_COMMON_CLK_MSM) += mdss/ diff --git a/drivers/clk/msm/clock-osm.c b/drivers/clk/msm/clock-osm.c index 598e52b54c99..9d605503520f 100644 --- a/drivers/clk/msm/clock-osm.c +++ b/drivers/clk/msm/clock-osm.c @@ -75,8 +75,7 @@ enum clk_osm_trace_packet_id { #define MEM_ACC_SEQ_REG_CFG_START(n) (SEQ_REG(12 + (n))) #define MEM_ACC_SEQ_CONST(n) (n) #define MEM_ACC_INSTR_COMP(n) (0x67 + ((n) * 0x40)) -#define MEM_ACC_SEQ_REG_VAL_START(n) \ - ((n) < 8 ? SEQ_REG(4 + (n)) : SEQ_REG(60 + (n) - 8)) +#define MEM_ACC_SEQ_REG_VAL_START(n) (SEQ_REG(60 + (n))) #define OSM_TABLE_SIZE 40 #define MAX_CLUSTER_CNT 2 @@ -125,6 +124,7 @@ enum clk_osm_trace_packet_id { #define SPM_CC_CTRL 0x1028 #define SPM_CC_HYSTERESIS 0x101C #define SPM_CORE_RET_MAPPING 0x1024 +#define CFG_DELAY_VAL_3 0x12C #define LLM_FREQ_VOTE_HYSTERESIS 0x102C #define LLM_VOLT_VOTE_HYSTERESIS 0x1030 @@ -184,11 +184,11 @@ enum clk_osm_trace_packet_id { #define MAX_INSTRUCTIONS 256 #define MAX_BR_INSTRUCTIONS 49 -#define MAX_MEM_ACC_LEVELS 4 +#define MAX_MEM_ACC_LEVELS 3 #define MAX_MEM_ACC_VAL_PER_LEVEL 3 #define MAX_MEM_ACC_VALUES (MAX_MEM_ACC_LEVELS * \ MAX_MEM_ACC_VAL_PER_LEVEL) -#define MEM_ACC_READ_MASK 0x7 +#define MEM_ACC_APM_READ_MASK 0xff #define TRACE_CTRL 0x1F38 #define TRACE_CTRL_EN_MASK BIT(0) @@ -203,6 +203,11 @@ enum clk_osm_trace_packet_id { #define PERIODIC_TRACE_MAX_NS 21474836475 #define PERIODIC_TRACE_DEFAULT_NS 1000000 +#define PLL_DD_USER_CTL_LO_ENABLE 0x0f04c408 +#define PLL_DD_USER_CTL_LO_DISABLE 0x1f04c41f +#define PLL_DD_D0_USER_CTL_LO 0x17916208 +#define PLL_DD_D1_USER_CTL_LO 0x17816208 + static void __iomem *virt_base; #define lmh_lite_clk_src_source_val 1 @@ -222,50 +227,48 @@ static void __iomem *virt_base; static u32 seq_instr[] = { 0xc2005000, 0x2c9e3b21, 0xc0ab2cdc, 0xc2882525, 0x359dc491, - 0x700a500b, 0x70005001, 0x390938c8, 0xcb44c833, 0xce56cd54, - 0x341336e0, 0xadba0000, 0x10004000, 0x70005001, 0x1000500c, - 0xc792c5a1, 0x501625e1, 0x3da335a2, 0x50170006, 0x50150006, - 0xafb9c633, 0xacb31000, 0xacb41000, 0x1000c422, 0x500baefc, - 0x5001700a, 0xaefd7000, 0x700b5010, 0x700c5012, 0xadb9ad41, - 0x181b0000, 0x500f500c, 0x34135011, 0x84b9181b, 0xbd808539, - 0x2ba40003, 0x0006a001, 0x10007105, 0x1000500e, 0x1c0a500c, - 0x3b181c01, 0x3b431c06, 0x10001c07, 0x39831c06, 0x500c1c07, - 0x1c0a1c02, 0x10000000, 0x70015002, 0x10000000, 0x50038103, - 0x50047002, 0x10007003, 0x39853b44, 0x50038104, 0x40037002, - 0x70095005, 0xb1c0a146, 0x238b0003, 0x10004005, 0x848b8308, - 0x1000850c, 0x848e830d, 0x1000850c, 0x3a4c5006, 0x3a8f39cd, - 0x40063ad0, 0x50071000, 0x2c127006, 0x4007a00f, 0x71050006, - 0x1000700d, 0x1c1aa964, 0x700d4007, 0x50071000, 0x1c167006, - 0x50125010, 0x40072411, 0x4007700d, 0xa00f1000, 0x0006a821, - 0x40077105, 0x500c700d, 0x1c1591ad, 0x5011500f, 0x10000000, - 0x500c2bd4, 0x0006a00f, 0x10007105, 0xa821a00f, 0x70050006, - 0x91ad500c, 0x500f1c15, 0x10005011, 0x1c162bce, 0x50125010, - 0xa82aa022, 0x71050006, 0x1c1591a6, 0x5011500f, 0x5014500c, - 0x0006a00f, 0x00007105, 0x91a41000, 0x22175013, 0x1c1aa963, - 0x22171000, 0x1c1aa963, 0x50081000, 0x40087007, 0x1c1aa963, - 0x70085009, 0x10004009, 0x850c848e, 0x0003b1c0, 0x400d2b99, - 0x500d1000, 0xabaf1000, 0x853184b0, 0x0003bb80, 0xa0371000, - 0x71050006, 0x85481000, 0xbf8084c3, 0x2ba80003, 0xbf8084c2, - 0x2ba70003, 0xbf8084c1, 0x2ba60003, 0x8ec71000, 0xc6dd8dc3, - 0x8c1625ec, 0x8d498c97, 0x8ec61c00, 0xc6dd8dc2, 0x8c1325ec, - 0x8d158c94, 0x8ec51c00, 0xc6dd8dc1, 0x8c1025ec, 0x8d128c91, - 0x8dc01c00, 0x182cc633, 0x84c08548, 0x0003bf80, 0x84c12ba9, - 0x0003bf80, 0x84c22baa, 0x0003bf80, 0x10002bab, 0x8dc08ec4, - 0x25ecc6dd, 0x8c948c13, 0x1c008d15, 0x8dc18ec5, 0x25ecc6dd, - 0x8c978c16, 0x1c008d49, 0x8dc28ec6, 0x25ecc6dd, 0x8ccb8c4a, - 0x1c008d4c, 0xc6338dc3, 0x1000af9b, 0xa759a79a, 0x1000a718, + 0x700a500b, 0x5001aefc, 0xaefd7000, 0x390938c8, 0xcb44c833, + 0xce56cd54, 0x341336e0, 0xa4baadba, 0xb480a493, 0x10004000, + 0x70005001, 0x1000500c, 0xc792c5a1, 0x501625e1, 0x3da335a2, + 0x50170006, 0x50150006, 0x1000c633, 0x1000acb3, 0xc422acb4, + 0xaefc1000, 0x700a500b, 0x70005001, 0x5010aefd, 0x5012700b, + 0xad41700c, 0x84e5adb9, 0xb3808566, 0x239b0003, 0x856484e3, + 0xb9800007, 0x2bad0003, 0xac3aa20b, 0x0003181b, 0x0003bb40, + 0xa30d239b, 0x500c181b, 0x5011500f, 0x181b3413, 0x853984b9, + 0x0003bd80, 0xa0012ba4, 0x72050803, 0x500e1000, 0x500c1000, + 0x1c011c0a, 0x3b181c06, 0x1c073b43, 0x1c061000, 0x1c073983, + 0x1c02500c, 0x10001c0a, 0x70015002, 0x81031000, 0x70025003, + 0x70035004, 0x3b441000, 0x81553985, 0x70025003, 0x50054003, + 0xa1467009, 0x0003b1c0, 0x4005238b, 0x835a1000, 0x855c84db, + 0x1000a51f, 0x84de835d, 0xa52c855c, 0x50061000, 0x39cd3a4c, + 0x3ad03a8f, 0x10004006, 0x70065007, 0xa00f2c12, 0x08034007, + 0xaefc7205, 0xaefd700d, 0xa9641000, 0x40071c1a, 0x700daefc, + 0x1000aefd, 0x70065007, 0x50101c16, 0x40075012, 0x700daefc, + 0x2411aefd, 0xa8211000, 0x0803a00f, 0x500c7005, 0x1c1591e0, + 0x500f5014, 0x10005011, 0x500c2bd4, 0x0803a00f, 0x10007205, + 0xa00fa9d1, 0x0803a821, 0xa9d07005, 0x91e0500c, 0x500f1c15, + 0x10005011, 0x1c162bce, 0x50125010, 0xa022a82a, 0x70050803, + 0x1c1591df, 0x5011500f, 0x5014500c, 0x0803a00f, 0x10007205, + 0x501391a4, 0x22172217, 0x70075008, 0xa9634008, 0x1c1a0006, + 0x70085009, 0x10004009, 0x00008ed9, 0x3e05c8dd, 0x1c033604, + 0xabaf1000, 0x856284e1, 0x0003bb80, 0x1000239f, 0x0803a037, + 0x10007205, 0x8dc61000, 0x38a71c2a, 0x1c2a8dc4, 0x100038a6, + 0x1c2a8dc5, 0x8dc73867, 0x38681c2a, 0x8c491000, 0x8d4b8cca, + 0x10001c00, 0x8ccd8c4c, 0x1c008d4e, 0x8c4f1000, 0x8d518cd0, + 0x10001c00, 0xa759a79a, 0x1000a718, 0xbf80af9b, 0x00001000, }; static u32 seq_br_instr[] = { - 0x28c, 0x1e6, 0x238, 0xd0, 0xec, - 0xf4, 0xbc, 0xc4, 0x9c, 0xac, - 0xfc, 0xe2, 0x154, 0x174, 0x17c, - 0x10a, 0x126, 0x13a, 0x11c, 0x98, - 0x160, 0x1a6, 0x19a, 0x1ae, 0x1c0, - 0x1ce, 0x1d2, 0x30, 0x60, 0x86, - 0x7c, 0x1d8, 0x34, 0x3c, 0x56, - 0x5a, 0x1de, 0x2e, 0x222, 0x212, - 0x202, 0x254, 0x264, 0x274, 0x288, + 0x248, 0x20e, 0x21c, 0xf6, 0x112, + 0x11c, 0xe4, 0xea, 0xc6, 0xd6, + 0x126, 0x108, 0x184, 0x1a8, 0x1b0, + 0x134, 0x158, 0x16e, 0x14a, 0xc2, + 0x190, 0x1d2, 0x1cc, 0x1d4, 0x1e8, + 0x0, 0x1f6, 0x32, 0x66, 0xb0, + 0xa6, 0x1fc, 0x3c, 0x44, 0x5c, + 0x60, 0x204, 0x30, 0x22a, 0x234, + 0x23e, 0x0, 0x250, 0x0, 0x0, 0x9a, + 0x20c, }; DEFINE_EXT_CLK(xo_ao, NULL); @@ -298,6 +301,7 @@ struct clk_osm { u32 cluster_num; u32 irq; u32 apm_crossover_vc; + u32 apm_threshold_vc; u32 cycle_counter_reads; u32 cycle_counter_delay; u32 cycle_counter_factor; @@ -551,8 +555,8 @@ static void clk_osm_print_osm_table(struct clk_osm *c) lval, table[i].spare_data); } - pr_debug("APM crossover corner: %d\n", - c->apm_crossover_vc); + pr_debug("APM threshold corner=%d, crossover corner=%d\n", + c->apm_threshold_vc, c->apm_crossover_vc); } static int clk_osm_get_lut(struct platform_device *pdev, @@ -1116,10 +1120,21 @@ exit: static int clk_osm_resolve_crossover_corners(struct clk_osm *c, struct platform_device *pdev) { + struct regulator *regulator = c->vdd_reg; struct dev_pm_opp *opp; unsigned long freq = 0; - int vc, rc = 0; + int vc, i, threshold, rc = 0; + u32 corner_volt, data; + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,apm-threshold-voltage", + &threshold); + if (rc) { + pr_info("qcom,apm-threshold-voltage property not specified\n"); + return rc; + } + + /* Determine crossover virtual corner */ rcu_read_lock(); opp = dev_pm_opp_find_freq_exact(&c->vdd_dev->dev, freq, true); if (IS_ERR(opp)) { @@ -1138,6 +1153,48 @@ static int clk_osm_resolve_crossover_corners(struct clk_osm *c, vc--; c->apm_crossover_vc = vc; + /* Determine threshold virtual corner */ + for (i = 0; i < OSM_TABLE_SIZE; i++) { + freq = c->osm_table[i].frequency; + /* + * Only frequencies that are supported across all configurations + * are present in the OPP table associated with the regulator + * device. + */ + data = (c->osm_table[i].freq_data & GENMASK(18, 16)) >> 16; + if (data != MAX_CONFIG) + continue; + + rcu_read_lock(); + opp = dev_pm_opp_find_freq_exact(&c->vdd_dev->dev, freq, true); + if (IS_ERR(opp)) { + rc = PTR_ERR(opp); + if (rc == -ERANGE) + pr_err("Frequency %lu not found\n", freq); + goto exit; + } + + vc = dev_pm_opp_get_voltage(opp); + if (!vc) { + pr_err("No virtual corner found for frequency %lu\n", + freq); + rc = -ERANGE; + goto exit; + } + + rcu_read_unlock(); + + corner_volt = regulator_list_corner_voltage(regulator, vc); + + /* CPR virtual corners are zero-based numbered */ + vc--; + + if (corner_volt >= threshold) { + c->apm_threshold_vc = vc; + break; + } + } + return 0; exit: rcu_read_unlock(); @@ -1413,55 +1470,77 @@ static void clk_osm_program_apm_regs(struct clk_osm *c) */ clk_osm_write_reg(c, c->apm_mode_ctl, SEQ_REG(2)); - /* Program mode value to switch APM from VDD_APCC to VDD_MX */ - clk_osm_write_reg(c, APM_MX_MODE, SEQ_REG(22)); - - /* Program mode value to switch APM from VDD_MX to VDD_APCC */ - clk_osm_write_reg(c, APM_APC_MODE, SEQ_REG(25)); - /* Program address of controller status register */ clk_osm_write_reg(c, c->apm_ctrl_status, SEQ_REG(3)); - /* Program mask used to determine status of APM power supply switch */ - clk_osm_write_reg(c, APM_MODE_SWITCH_MASK, SEQ_REG(24)); + /* Program mode value to switch APM from VDD_APCC to VDD_MX */ + clk_osm_write_reg(c, APM_MX_MODE, SEQ_REG(77)); /* Program value used to determine current APM power supply is VDD_MX */ - clk_osm_write_reg(c, APM_MX_MODE_VAL, SEQ_REG(23)); + clk_osm_write_reg(c, APM_MX_MODE_VAL, SEQ_REG(78)); + + /* Program mask used to determine status of APM power supply switch */ + clk_osm_write_reg(c, APM_MODE_SWITCH_MASK, SEQ_REG(79)); + + /* Program mode value to switch APM from VDD_MX to VDD_APCC */ + clk_osm_write_reg(c, APM_APC_MODE, SEQ_REG(80)); /* * Program value used to determine current APM power supply * is VDD_APCC */ - clk_osm_write_reg(c, APM_APC_MODE_VAL, SEQ_REG(26)); + clk_osm_write_reg(c, APM_APC_MODE_VAL, SEQ_REG(81)); } static void clk_osm_program_mem_acc_regs(struct clk_osm *c) { - int i; + int i, curr_level, j = 0; + int mem_acc_level_map[MAX_MEM_ACC_LEVELS] = {0, 0, 0}; - if (!c->secure_init) - return; + curr_level = c->osm_table[0].spare_data; + for (i = 0; i < c->num_entries; i++) { + if (curr_level == MAX_MEM_ACC_LEVELS) + break; - clk_osm_write_reg(c, c->pbases[OSM_BASE] + SEQ_REG(50), - SEQ_REG(49)); - clk_osm_write_reg(c, MEM_ACC_SEQ_CONST(1), SEQ_REG(50)); - clk_osm_write_reg(c, MEM_ACC_SEQ_CONST(1), SEQ_REG(51)); - clk_osm_write_reg(c, MEM_ACC_SEQ_CONST(2), SEQ_REG(52)); - clk_osm_write_reg(c, MEM_ACC_SEQ_CONST(3), SEQ_REG(53)); - clk_osm_write_reg(c, MEM_ACC_SEQ_CONST(4), SEQ_REG(54)); - clk_osm_write_reg(c, MEM_ACC_INSTR_COMP(0), SEQ_REG(55)); - clk_osm_write_reg(c, MEM_ACC_INSTR_COMP(1), SEQ_REG(56)); - clk_osm_write_reg(c, MEM_ACC_INSTR_COMP(2), SEQ_REG(57)); - clk_osm_write_reg(c, MEM_ACC_INSTR_COMP(3), SEQ_REG(58)); - clk_osm_write_reg(c, MEM_ACC_READ_MASK, SEQ_REG(59)); - - for (i = 0; i < MAX_MEM_ACC_VALUES; i++) - clk_osm_write_reg(c, c->apcs_mem_acc_val[i], - MEM_ACC_SEQ_REG_VAL_START(i)); - - for (i = 0; i < MAX_MEM_ACC_VAL_PER_LEVEL; i++) - clk_osm_write_reg(c, c->apcs_mem_acc_cfg[i], - MEM_ACC_SEQ_REG_CFG_START(i)); + if (c->osm_table[i].spare_data != curr_level) { + mem_acc_level_map[j++] = i - 1; + curr_level = c->osm_table[i].spare_data; + } + } + + if (c->secure_init) { + clk_osm_write_reg(c, MEM_ACC_SEQ_CONST(1), SEQ_REG(51)); + clk_osm_write_reg(c, MEM_ACC_SEQ_CONST(2), SEQ_REG(52)); + clk_osm_write_reg(c, MEM_ACC_SEQ_CONST(3), SEQ_REG(53)); + clk_osm_write_reg(c, MEM_ACC_SEQ_CONST(4), SEQ_REG(54)); + clk_osm_write_reg(c, MEM_ACC_APM_READ_MASK, SEQ_REG(59)); + clk_osm_write_reg(c, mem_acc_level_map[0], SEQ_REG(55)); + clk_osm_write_reg(c, mem_acc_level_map[0] + 1, SEQ_REG(56)); + clk_osm_write_reg(c, mem_acc_level_map[1], SEQ_REG(57)); + clk_osm_write_reg(c, mem_acc_level_map[1] + 1, SEQ_REG(58)); + clk_osm_write_reg(c, c->pbases[OSM_BASE] + SEQ_REG(28), + SEQ_REG(49)); + + for (i = 0; i < MAX_MEM_ACC_VALUES; i++) + clk_osm_write_reg(c, c->apcs_mem_acc_val[i], + MEM_ACC_SEQ_REG_VAL_START(i)); + + for (i = 0; i < MAX_MEM_ACC_VAL_PER_LEVEL; i++) + clk_osm_write_reg(c, c->apcs_mem_acc_cfg[i], + MEM_ACC_SEQ_REG_CFG_START(i)); + } else { + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(55), + mem_acc_level_map[0]); + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(56), + mem_acc_level_map[0] + 1); + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(57), + mem_acc_level_map[1]); + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(58), + mem_acc_level_map[1] + 1); + /* SEQ_REG(49) = SEQ_REG(28) init by TZ */ + } + + return; } void clk_osm_setup_sequencer(struct clk_osm *c) @@ -1500,10 +1579,12 @@ static void clk_osm_setup_cycle_counters(struct clk_osm *c) static void clk_osm_setup_osm_was(struct clk_osm *c) { + u32 cc_hyst; u32 val; val = clk_osm_read_reg(c, PDN_FSM_CTRL_REG); val |= IGNORE_PLL_LOCK_MASK; + cc_hyst = clk_osm_read_reg(c, SPM_CC_HYSTERESIS); if (c->secure_init) { clk_osm_write_reg(c, val, SEQ_REG(47)); @@ -1518,10 +1599,51 @@ static void clk_osm_setup_osm_was(struct clk_osm *c) clk_osm_write_reg(c, 0x0, SEQ_REG(45)); clk_osm_write_reg(c, c->pbases[OSM_BASE] + PDN_FSM_CTRL_REG, SEQ_REG(46)); + + /* C2D/C3 + D2D workaround */ + clk_osm_write_reg(c, c->pbases[OSM_BASE] + SPM_CC_HYSTERESIS, + SEQ_REG(6)); + clk_osm_write_reg(c, cc_hyst, SEQ_REG(7)); + + /* Droop detector PLL lock detect workaround */ + clk_osm_write_reg(c, PLL_DD_USER_CTL_LO_ENABLE, SEQ_REG(4)); + clk_osm_write_reg(c, PLL_DD_USER_CTL_LO_DISABLE, SEQ_REG(5)); + clk_osm_write_reg(c, c->cluster_num == 0 ? PLL_DD_D0_USER_CTL_LO + : PLL_DD_D1_USER_CTL_LO, SEQ_REG(21)); + + /* PLL lock detect and HMSS AHB clock workaround */ + clk_osm_write_reg(c, 0x640, CFG_DELAY_VAL_3); + + /* DxFSM workaround */ + clk_osm_write_reg(c, c->cluster_num == 0 ? 0x17911200 : + 0x17811200, SEQ_REG(22)); + clk_osm_write_reg(c, 0x80800, SEQ_REG(23)); + clk_osm_write_reg(c, 0x179D1100, SEQ_REG(24)); + clk_osm_write_reg(c, 0x11f, SEQ_REG(25)); + clk_osm_write_reg(c, c->cluster_num == 0 ? 0x17912000 : + 0x17811290, SEQ_REG(26)); + clk_osm_write_reg(c, c->cluster_num == 0 ? 0x17911290 : + 0x17811290, SEQ_REG(20)); + clk_osm_write_reg(c, c->cluster_num == 0 ? 0x17811290 : + 0x17911290, SEQ_REG(32)); + clk_osm_write_reg(c, 0x179D4020, SEQ_REG(35)); + clk_osm_write_reg(c, 0x11f, SEQ_REG(25)); + clk_osm_write_reg(c, 0xa, SEQ_REG(86)); + clk_osm_write_reg(c, 0xe, SEQ_REG(87)); + clk_osm_write_reg(c, 0x00400000, SEQ_REG(88)); + clk_osm_write_reg(c, 0x00700000, SEQ_REG(89)); } else { scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(47), val); val &= ~IGNORE_PLL_LOCK_MASK; scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(48), val); + + /* C2D/C3 + D2D workaround */ + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(7), + cc_hyst); + + /* Droop detector PLL lock detect workaround */ + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(4), + PLL_DD_USER_CTL_LO_ENABLE); } if (c->cluster_num == 0) { @@ -1671,18 +1793,15 @@ static void clk_osm_do_additional_setup(struct clk_osm *c, /* APM Programming */ clk_osm_program_apm_regs(c); - /* MEM-ACC Programming */ - clk_osm_program_mem_acc_regs(c); - /* GFMUX Programming */ clk_osm_write_reg(c, c->apcs_cfg_rcgr, SEQ_REG(16)); - clk_osm_write_reg(c, GPLL_SEL, SEQ_REG(17)); - clk_osm_write_reg(c, PLL_EARLY_SEL, SEQ_REG(20)); - clk_osm_write_reg(c, PLL_MAIN_SEL, SEQ_REG(32)); clk_osm_write_reg(c, c->apcs_cmd_rcgr, SEQ_REG(33)); clk_osm_write_reg(c, RCG_UPDATE, SEQ_REG(34)); - clk_osm_write_reg(c, RCG_UPDATE_SUCCESS, SEQ_REG(35)); - clk_osm_write_reg(c, RCG_UPDATE, SEQ_REG(36)); + clk_osm_write_reg(c, GPLL_SEL, SEQ_REG(17)); + clk_osm_write_reg(c, PLL_EARLY_SEL, SEQ_REG(82)); + clk_osm_write_reg(c, PLL_MAIN_SEL, SEQ_REG(83)); + clk_osm_write_reg(c, RCG_UPDATE_SUCCESS, SEQ_REG(84)); + clk_osm_write_reg(c, RCG_UPDATE, SEQ_REG(85)); pr_debug("seq_size: %lu, seqbr_size: %lu\n", ARRAY_SIZE(seq_instr), ARRAY_SIZE(seq_br_instr)); @@ -1693,17 +1812,43 @@ static void clk_osm_do_additional_setup(struct clk_osm *c, static void clk_osm_apm_vc_setup(struct clk_osm *c) { /* - * APM crossover virtual corner at which the switch - * from APC to MX and vice-versa should take place. + * APM crossover virtual corner corresponds to switching + * voltage during APM transition. APM threshold virtual + * corner is the first corner which requires switch + * sequence of APM from MX to APC. */ if (c->secure_init) { - clk_osm_write_reg(c, c->apm_crossover_vc, SEQ_REG(1)); + clk_osm_write_reg(c, c->apm_threshold_vc, SEQ_REG(1)); + clk_osm_write_reg(c, c->apm_crossover_vc, SEQ_REG(72)); + clk_osm_write_reg(c, c->pbases[OSM_BASE] + SEQ_REG(1), + SEQ_REG(8)); + clk_osm_write_reg(c, c->apm_threshold_vc, + SEQ_REG(15)); + clk_osm_write_reg(c, c->apm_threshold_vc != 0 ? + c->apm_threshold_vc - 1 : 0xff, + SEQ_REG(31)); + clk_osm_write_reg(c, 0x3b | c->apm_threshold_vc << 6, + SEQ_REG(73)); + clk_osm_write_reg(c, 0x39 | c->apm_threshold_vc << 6, + SEQ_REG(76)); /* Ensure writes complete before returning */ mb(); } else { scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(1), + c->apm_threshold_vc); + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(72), c->apm_crossover_vc); + /* SEQ_REG(8) = address of SEQ_REG(1) init by TZ */ + clk_osm_write_reg(c, c->apm_threshold_vc, + SEQ_REG(15)); + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(31), + c->apm_threshold_vc != 0 ? + c->apm_threshold_vc - 1 : 0xff); + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(73), + 0x3b | c->apm_threshold_vc << 6); + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(76), + 0x39 | c->apm_threshold_vc << 6); } } @@ -2412,6 +2557,10 @@ static int cpu_clock_osm_driver_probe(struct platform_device *pdev) clk_osm_do_additional_setup(&pwrcl_clk, pdev); clk_osm_do_additional_setup(&perfcl_clk, pdev); + /* MEM-ACC Programming */ + clk_osm_program_mem_acc_regs(&pwrcl_clk); + clk_osm_program_mem_acc_regs(&perfcl_clk); + /* Program APM crossover corners */ clk_osm_apm_vc_setup(&pwrcl_clk); clk_osm_apm_vc_setup(&perfcl_clk); diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 94419695cd2e..dc1b66f84af2 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -12,6 +12,7 @@ clk-qcom-y += clk-regmap-mux.o clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o clk-qcom-y += clk-hfpll.o clk-qcom-y += reset.o +clk-qcom-y += clk-dummy.o clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o diff --git a/drivers/clk/qcom/clk-dummy.c b/drivers/clk/qcom/clk-dummy.c new file mode 100644 index 000000000000..3205fbc6b8ba --- /dev/null +++ b/drivers/clk/qcom/clk-dummy.c @@ -0,0 +1,110 @@ +/* + * 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. + */ + +#include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +struct clk_dummy { + struct clk_hw hw; + unsigned long rrate; +}; + +#define to_clk_dummy(_hw) container_of(_hw, struct clk_dummy, hw) + +static int dummy_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_dummy *dummy = to_clk_dummy(hw); + + dummy->rrate = rate; + + pr_debug("set rate: %lu\n", rate); + + return 0; +} + +static long dummy_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + return rate; +} + +static unsigned long dummy_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_dummy *dummy = to_clk_dummy(hw); + + pr_debug("clock rate: %lu\n", dummy->rrate); + + return dummy->rrate; +} + +struct clk_ops clk_dummy_ops = { + .set_rate = dummy_clk_set_rate, + .round_rate = dummy_clk_round_rate, + .recalc_rate = dummy_clk_recalc_rate, +}; +EXPORT_SYMBOL_GPL(clk_dummy_ops); + +/** + * clk_register_dummy - register dummy clock with the + * clock framework + * @dev: device that is registering this clock + * @name: name of this clock + * @flags: framework-specific flags + */ +static struct clk *clk_register_dummy(struct device *dev, const char *name, + unsigned long flags) +{ + struct clk_dummy *dummy; + struct clk *clk; + struct clk_init_data init = {}; + + /* allocate dummy clock */ + dummy = kzalloc(sizeof(*dummy), GFP_KERNEL); + if (!dummy) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_dummy_ops; + init.flags = flags | CLK_IS_BASIC; + init.num_parents = 0; + dummy->hw.init = &init; + + /* register the clock */ + clk = clk_register(dev, &dummy->hw); + if (IS_ERR(clk)) + kfree(dummy); + + return clk; +} + +/** + * of_dummy_clk_setup() - Setup function for simple fixed rate clock + */ +static void of_dummy_clk_setup(struct device_node *node) +{ + struct clk *clk; + const char *clk_name = "dummy_clk"; + + of_property_read_string(node, "clock-output-names", &clk_name); + + clk = clk_register_dummy(NULL, clk_name, 0); + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + + pr_info("%s: Dummy clock registered\n", clk_name); +} +CLK_OF_DECLARE(dummy_clk, "qcom,dummycc", of_dummy_clk_setup); diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h index ae9bdeb21f29..10cabca921be 100644 --- a/drivers/clk/qcom/common.h +++ b/drivers/clk/qcom/common.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * Copyright (c) 2014, 2016, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -48,5 +48,5 @@ extern int qcom_cc_really_probe(struct platform_device *pdev, struct regmap *regmap); extern int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc); - +extern struct clk_ops clk_dummy_ops; #endif diff --git a/drivers/gpu/msm/Makefile b/drivers/gpu/msm/Makefile index db5a9ca28408..90aee3cad5ad 100644 --- a/drivers/gpu/msm/Makefile +++ b/drivers/gpu/msm/Makefile @@ -33,6 +33,8 @@ msm_adreno-y += \ adreno_a3xx_snapshot.o \ adreno_a4xx_snapshot.o \ adreno_a5xx_snapshot.o \ + adreno_a4xx_preempt.o \ + adreno_a5xx_preempt.o \ adreno_sysfs.o \ adreno.o \ adreno_cp_parser.o \ diff --git a/drivers/gpu/msm/a5xx_reg.h b/drivers/gpu/msm/a5xx_reg.h index 913cedb885ad..207588844931 100644 --- a/drivers/gpu/msm/a5xx_reg.h +++ b/drivers/gpu/msm/a5xx_reg.h @@ -60,6 +60,8 @@ #define A5XX_CP_RB_BASE 0x800 #define A5XX_CP_RB_BASE_HI 0x801 #define A5XX_CP_RB_CNTL 0x802 +#define A5XX_CP_RB_RPTR_ADDR_LO 0x804 +#define A5XX_CP_RB_RPTR_ADDR_HI 0x805 #define A5XX_CP_RB_RPTR 0x806 #define A5XX_CP_RB_WPTR 0x807 #define A5XX_CP_PFP_STAT_ADDR 0x808 diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 26e341a876e8..918231b73215 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -171,6 +171,30 @@ void adreno_writereg64(struct adreno_device *adreno_dev, } /** + * adreno_get_rptr() - Get the current ringbuffer read pointer + * @rb: Pointer the ringbuffer to query + * + * Get the latest rptr + */ +unsigned int adreno_get_rptr(struct adreno_ringbuffer *rb) +{ + struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb); + unsigned int rptr = 0; + + if (adreno_is_a3xx(adreno_dev)) + adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_RPTR, + &rptr); + else { + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + + kgsl_sharedmem_readl(&device->scratch, &rptr, + SCRATCH_RPTR_OFFSET(rb->id)); + } + + return rptr; +} + +/** * adreno_of_read_property() - Adreno read property * @node: Device node * @@ -1290,6 +1314,28 @@ static void _update_threshold_count(struct adreno_device *adreno_dev, adreno_dev->lm_threshold_cross = adj; } +static void _set_secvid(struct kgsl_device *device) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + + /* Program GPU contect protection init values */ + if (device->mmu.secured) { + if (adreno_is_a4xx(adreno_dev)) + adreno_writereg(adreno_dev, + ADRENO_REG_RBBM_SECVID_TRUST_CONFIG, 0x2); + adreno_writereg(adreno_dev, + ADRENO_REG_RBBM_SECVID_TSB_CONTROL, 0x0); + + adreno_writereg64(adreno_dev, + ADRENO_REG_RBBM_SECVID_TSB_TRUSTED_BASE, + ADRENO_REG_RBBM_SECVID_TSB_TRUSTED_BASE_HI, + KGSL_IOMMU_SECURE_BASE); + adreno_writereg(adreno_dev, + ADRENO_REG_RBBM_SECVID_TSB_TRUSTED_SIZE, + KGSL_IOMMU_SECURE_SIZE); + } +} + /** * _adreno_start - Power up the GPU and prepare to accept commands * @adreno_dev: Pointer to an adreno_device structure @@ -1332,26 +1378,13 @@ static int _adreno_start(struct adreno_device *adreno_dev) if (regulator_left_on) _soft_reset(adreno_dev); + adreno_ringbuffer_set_global(adreno_dev, 0); + status = kgsl_mmu_start(device); if (status) goto error_pwr_off; - /* Program GPU contect protection init values */ - if (device->mmu.secured) { - if (adreno_is_a4xx(adreno_dev)) - adreno_writereg(adreno_dev, - ADRENO_REG_RBBM_SECVID_TRUST_CONFIG, 0x2); - adreno_writereg(adreno_dev, - ADRENO_REG_RBBM_SECVID_TSB_CONTROL, 0x0); - - adreno_writereg64(adreno_dev, - ADRENO_REG_RBBM_SECVID_TSB_TRUSTED_BASE, - ADRENO_REG_RBBM_SECVID_TSB_TRUSTED_BASE_HI, - KGSL_IOMMU_SECURE_BASE); - adreno_writereg(adreno_dev, - ADRENO_REG_RBBM_SECVID_TSB_TRUSTED_SIZE, - KGSL_IOMMU_SECURE_SIZE); - } + _set_secvid(device); status = adreno_ocmem_malloc(adreno_dev); if (status) { @@ -1533,6 +1566,22 @@ static int adreno_vbif_clear_pending_transactions(struct kgsl_device *device) return ret; } +static void adreno_set_active_ctxs_null(struct adreno_device *adreno_dev) +{ + int i; + struct adreno_ringbuffer *rb; + + FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { + if (rb->drawctxt_active) + kgsl_context_put(&(rb->drawctxt_active->base)); + rb->drawctxt_active = NULL; + + kgsl_sharedmem_writel(KGSL_DEVICE(adreno_dev), + &rb->pagetable_desc, PT_INFO_OFFSET(current_rb_ptname), + 0); + } +} + static int adreno_stop(struct kgsl_device *device) { struct adreno_device *adreno_dev = ADRENO_DEVICE(device); @@ -1645,13 +1694,6 @@ int adreno_reset(struct kgsl_device *device, int fault) else kgsl_pwrctrl_change_state(device, KGSL_STATE_NAP); - /* Set the page table back to the default page table */ - kgsl_mmu_set_pt(&device->mmu, device->mmu.defaultpagetable); - kgsl_sharedmem_writel(device, - &adreno_dev->ringbuffers[0].pagetable_desc, - offsetof(struct adreno_ringbuffer_pagetable_info, - current_global_ptname), 0); - return ret; } @@ -2094,9 +2136,15 @@ static int adreno_soft_reset(struct kgsl_device *device) /* Reset the GPU */ _soft_reset(adreno_dev); + /* Set the page table back to the default page table */ + adreno_ringbuffer_set_global(adreno_dev, 0); + kgsl_mmu_set_pt(&device->mmu, device->mmu.defaultpagetable); + /* start of new CFF after reset */ kgsl_cffdump_open(device); + _set_secvid(device); + /* Enable 64 bit gpu addr if feature is set */ if (gpudev->enable_64bit && adreno_support_64bit(adreno_dev)) @@ -2149,8 +2197,6 @@ bool adreno_isidle(struct kgsl_device *device) if (!kgsl_state_is_awake(device)) return true; - adreno_get_rptr(ADRENO_CURRENT_RINGBUFFER(adreno_dev)); - /* * wptr is updated when we add commands to ringbuffer, add a barrier * to make sure updated wptr is compared to rptr @@ -2161,15 +2207,13 @@ bool adreno_isidle(struct kgsl_device *device) * ringbuffer is truly idle when all ringbuffers read and write * pointers are equal */ + FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { - if (rb->rptr != rb->wptr) - break; + if (!adreno_rb_empty(rb)) + return false; } - if (i == adreno_dev->num_ringbuffers) - return adreno_hw_isidle(adreno_dev); - - return false; + return adreno_hw_isidle(adreno_dev); } /** @@ -2267,25 +2311,11 @@ static int adreno_drain(struct kgsl_device *device) /* Caller must hold the device mutex. */ static int adreno_suspend_context(struct kgsl_device *device) { - int status = 0; - struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - /* process any profiling results that are available */ - adreno_profile_process_results(adreno_dev); + adreno_profile_process_results(ADRENO_DEVICE(device)); - status = adreno_idle(device); - if (status) - return status; - /* set the device to default pagetable */ - kgsl_mmu_set_pt(&device->mmu, device->mmu.defaultpagetable); - kgsl_sharedmem_writel(device, - &adreno_dev->ringbuffers[0].pagetable_desc, - offsetof(struct adreno_ringbuffer_pagetable_info, - current_global_ptname), 0); - /* set ringbuffers to NULL ctxt */ - adreno_set_active_ctxs_null(adreno_dev); - - return status; + /* Wait for the device to go idle */ + return adreno_idle(device); } /** diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h index 7ac91f203a70..9f462bca26ce 100644 --- a/drivers/gpu/msm/adreno.h +++ b/drivers/gpu/msm/adreno.h @@ -193,6 +193,47 @@ enum adreno_gpurev { struct adreno_gpudev; +/* Time to allow preemption to complete (in ms) */ +#define ADRENO_PREEMPT_TIMEOUT 10000 + +/** + * enum adreno_preempt_states + * ADRENO_PREEMPT_NONE: No preemption is scheduled + * ADRENO_PREEMPT_START: The S/W has started + * ADRENO_PREEMPT_TRIGGERED: A preeempt has been triggered in the HW + * ADRENO_PREEMPT_FAULTED: The preempt timer has fired + * ADRENO_PREEMPT_PENDING: The H/W has signaled preemption complete + * ADRENO_PREEMPT_COMPLETE: Preemption could not be finished in the IRQ handler, + * worker has been scheduled + */ +enum adreno_preempt_states { + ADRENO_PREEMPT_NONE = 0, + ADRENO_PREEMPT_START, + ADRENO_PREEMPT_TRIGGERED, + ADRENO_PREEMPT_FAULTED, + ADRENO_PREEMPT_PENDING, + ADRENO_PREEMPT_COMPLETE, +}; + +/** + * struct adreno_preemption + * @state: The current state of preemption + * @counters: Memory descriptor for the memory where the GPU writes the + * preemption counters on switch + * @timer: A timer to make sure preemption doesn't stall + * @work: A work struct for the preemption worker (for 5XX) + * @token_submit: Indicates if a preempt token has been submitted in + * current ringbuffer (for 4XX) + */ +struct adreno_preemption { + atomic_t state; + struct kgsl_memdesc counters; + struct timer_list timer; + struct work_struct work; + bool token_submit; +}; + + struct adreno_busy_data { unsigned int gpu_busy; unsigned int vbif_ram_cycles; @@ -368,7 +409,7 @@ struct adreno_device { const struct firmware *lm_fw; uint32_t *lm_sequence; uint32_t lm_size; - struct kgsl_memdesc preemption_counters; + struct adreno_preemption preempt; struct work_struct gpmu_work; uint32_t lm_leakage; uint32_t lm_limit; @@ -458,6 +499,8 @@ enum adreno_regs { ADRENO_REG_CP_WFI_PEND_CTR, ADRENO_REG_CP_RB_BASE, ADRENO_REG_CP_RB_BASE_HI, + ADRENO_REG_CP_RB_RPTR_ADDR_LO, + ADRENO_REG_CP_RB_RPTR_ADDR_HI, ADRENO_REG_CP_RB_RPTR, ADRENO_REG_CP_RB_WPTR, ADRENO_REG_CP_CNTL, @@ -709,17 +752,12 @@ struct adreno_gpudev { void (*pwrlevel_change_settings)(struct adreno_device *, unsigned int prelevel, unsigned int postlevel, bool post); - int (*preemption_pre_ibsubmit)(struct adreno_device *, - struct adreno_ringbuffer *, unsigned int *, - struct kgsl_context *, uint64_t cond_addr, - struct kgsl_memobj_node *); + unsigned int (*preemption_pre_ibsubmit)(struct adreno_device *, + struct adreno_ringbuffer *rb, + unsigned int *, struct kgsl_context *); int (*preemption_yield_enable)(unsigned int *); - int (*preemption_post_ibsubmit)(struct adreno_device *, - struct adreno_ringbuffer *, unsigned int *, - struct kgsl_context *); - int (*preemption_token)(struct adreno_device *, - struct adreno_ringbuffer *, unsigned int *, - uint64_t gpuaddr); + unsigned int (*preemption_post_ibsubmit)(struct adreno_device *, + unsigned int *); int (*preemption_init)(struct adreno_device *); void (*preemption_schedule)(struct adreno_device *); void (*enable_64bit)(struct adreno_device *); @@ -1260,34 +1298,32 @@ static inline int adreno_bootstrap_ucode(struct adreno_device *adreno_dev) } /** - * adreno_preempt_state() - Check if preemption state is equal to given state + * adreno_in_preempt_state() - Check if preemption state is equal to given state * @adreno_dev: Device whose preemption state is checked * @state: State to compare against */ -static inline unsigned int adreno_preempt_state( - struct adreno_device *adreno_dev, - enum adreno_dispatcher_preempt_states state) +static inline bool adreno_in_preempt_state(struct adreno_device *adreno_dev, + enum adreno_preempt_states state) { - return atomic_read(&adreno_dev->dispatcher.preemption_state) == - state; + return atomic_read(&adreno_dev->preempt.state) == state; } - /** - * adreno_get_rptr() - Get the current ringbuffer read pointer - * @rb: Pointer the ringbuffer to query - * - * Get the current read pointer from the GPU register. + * adreno_set_preempt_state() - Set the specified preemption state + * @adreno_dev: Device to change preemption state + * @state: State to set */ -static inline unsigned int -adreno_get_rptr(struct adreno_ringbuffer *rb) +static inline void adreno_set_preempt_state(struct adreno_device *adreno_dev, + enum adreno_preempt_states state) { - struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb); - if (adreno_dev->cur_rb == rb && - adreno_preempt_state(adreno_dev, - ADRENO_DISPATCHER_PREEMPT_CLEAR)) - adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_RPTR, &(rb->rptr)); + /* + * atomic_set doesn't use barriers, so we need to do it ourselves. One + * before... + */ + smp_wmb(); + atomic_set(&adreno_dev->preempt.state, state); - return rb->rptr; + /* ... and one after */ + smp_wmb(); } static inline bool adreno_is_preemption_enabled( @@ -1295,7 +1331,6 @@ static inline bool adreno_is_preemption_enabled( { return test_bit(ADRENO_DEVICE_PREEMPTION, &adreno_dev->priv); } - /** * adreno_ctx_get_rb() - Return the ringbuffer that a context should * use based on priority @@ -1332,25 +1367,6 @@ static inline struct adreno_ringbuffer *adreno_ctx_get_rb( return &(adreno_dev->ringbuffers[ adreno_dev->num_ringbuffers - 1]); } -/* - * adreno_set_active_ctxs_null() - Put back reference to any active context - * and set the active context to NULL - * @adreno_dev: The adreno device - */ -static inline void adreno_set_active_ctxs_null(struct adreno_device *adreno_dev) -{ - int i; - struct adreno_ringbuffer *rb; - FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { - if (rb->drawctxt_active) - kgsl_context_put(&(rb->drawctxt_active->base)); - rb->drawctxt_active = NULL; - kgsl_sharedmem_writel(KGSL_DEVICE(adreno_dev), - &rb->pagetable_desc, - offsetof(struct adreno_ringbuffer_pagetable_info, - current_rb_ptname), 0); - } -} /* * adreno_compare_prio_level() - Compares 2 priority levels based on enum values @@ -1371,6 +1387,13 @@ void adreno_readreg64(struct adreno_device *adreno_dev, void adreno_writereg64(struct adreno_device *adreno_dev, enum adreno_regs lo, enum adreno_regs hi, uint64_t val); +unsigned int adreno_get_rptr(struct adreno_ringbuffer *rb); + +static inline bool adreno_rb_empty(struct adreno_ringbuffer *rb) +{ + return (adreno_get_rptr(rb) == rb->wptr); +} + static inline bool adreno_soft_fault_detect(struct adreno_device *adreno_dev) { return adreno_dev->fast_hang_detect && @@ -1400,4 +1423,36 @@ static inline bool adreno_support_64bit(struct adreno_device *adreno_dev) } #endif /*BITS_PER_LONG*/ +static inline void adreno_ringbuffer_set_global( + struct adreno_device *adreno_dev, int name) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + + kgsl_sharedmem_writel(device, + &adreno_dev->ringbuffers[0].pagetable_desc, + PT_INFO_OFFSET(current_global_ptname), name); +} + +static inline void adreno_ringbuffer_set_pagetable(struct adreno_ringbuffer *rb, + struct kgsl_pagetable *pt) +{ + struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb); + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + unsigned long flags; + + spin_lock_irqsave(&rb->preempt_lock, flags); + + kgsl_sharedmem_writel(device, &rb->pagetable_desc, + PT_INFO_OFFSET(current_rb_ptname), pt->name); + + kgsl_sharedmem_writeq(device, &rb->pagetable_desc, + PT_INFO_OFFSET(ttbr0), kgsl_mmu_pagetable_get_ttbr0(pt)); + + kgsl_sharedmem_writel(device, &rb->pagetable_desc, + PT_INFO_OFFSET(contextidr), + kgsl_mmu_pagetable_get_contextidr(pt)); + + spin_unlock_irqrestore(&rb->preempt_lock, flags); +} + #endif /*__ADRENO_H */ diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c index ea8b75f4c83b..2accbe5c5764 100644 --- a/drivers/gpu/msm/adreno_a3xx.c +++ b/drivers/gpu/msm/adreno_a3xx.c @@ -1756,9 +1756,9 @@ static int _ringbuffer_bootstrap_ucode(struct adreno_device *adreno_dev, *cmds++ = cp_type3_packet(CP_INTERRUPT, 1); *cmds++ = 0; - rb->wptr = rb->wptr - 2; + rb->_wptr = rb->_wptr - 2; adreno_ringbuffer_submit(rb, NULL); - rb->wptr = rb->wptr + 2; + rb->_wptr = rb->_wptr + 2; } else { for (i = pfp_idx; i < adreno_dev->pfp_fw_size; i++) *cmds++ = adreno_dev->pfp_fw[i]; diff --git a/drivers/gpu/msm/adreno_a4xx.c b/drivers/gpu/msm/adreno_a4xx.c index b1196da0cee1..b15d23cfbe0a 100644 --- a/drivers/gpu/msm/adreno_a4xx.c +++ b/drivers/gpu/msm/adreno_a4xx.c @@ -178,111 +178,6 @@ static const struct adreno_vbif_platform a4xx_vbif_platforms[] = { { adreno_is_a418, a430_vbif }, }; -/* a4xx_preemption_start() - Setup state to start preemption */ -static void a4xx_preemption_start(struct adreno_device *adreno_dev, - struct adreno_ringbuffer *rb) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - uint32_t val; - - /* - * Setup scratch registers from which the GPU will program the - * registers required to start execution of new ringbuffer - * set ringbuffer address - */ - kgsl_regwrite(device, A4XX_CP_SCRATCH_REG8, - rb->buffer_desc.gpuaddr); - kgsl_regread(device, A4XX_CP_RB_CNTL, &val); - /* scratch REG9 corresponds to CP_RB_CNTL register */ - kgsl_regwrite(device, A4XX_CP_SCRATCH_REG9, val); - /* scratch REG10 corresponds to rptr address */ - kgsl_regwrite(device, A4XX_CP_SCRATCH_REG10, 0); - /* scratch REG11 corresponds to rptr */ - kgsl_regwrite(device, A4XX_CP_SCRATCH_REG11, rb->rptr); - /* scratch REG12 corresponds to wptr */ - kgsl_regwrite(device, A4XX_CP_SCRATCH_REG12, rb->wptr); - /* - * scratch REG13 corresponds to IB1_BASE, - * 0 since we do not do switches in between IB's - */ - kgsl_regwrite(device, A4XX_CP_SCRATCH_REG13, 0); - /* scratch REG14 corresponds to IB1_BUFSZ */ - kgsl_regwrite(device, A4XX_CP_SCRATCH_REG14, 0); - /* scratch REG15 corresponds to IB2_BASE */ - kgsl_regwrite(device, A4XX_CP_SCRATCH_REG15, 0); - /* scratch REG16 corresponds to IB2_BUFSZ */ - kgsl_regwrite(device, A4XX_CP_SCRATCH_REG16, 0); - /* scratch REG17 corresponds to GPR11 */ - kgsl_regwrite(device, A4XX_CP_SCRATCH_REG17, rb->gpr11); -} - -/* a4xx_preemption_save() - Save the state after preemption is done */ -static void a4xx_preemption_save(struct adreno_device *adreno_dev, - struct adreno_ringbuffer *rb) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - - kgsl_regread(device, A4XX_CP_SCRATCH_REG18, &rb->rptr); - kgsl_regread(device, A4XX_CP_SCRATCH_REG23, &rb->gpr11); -} - -static int a4xx_preemption_token(struct adreno_device *adreno_dev, - struct adreno_ringbuffer *rb, unsigned int *cmds, - uint64_t gpuaddr) -{ - unsigned int *cmds_orig = cmds; - - /* Turn on preemption flag */ - /* preemption token - fill when pt switch command size is known */ - *cmds++ = cp_type3_packet(CP_PREEMPT_TOKEN, 3); - *cmds++ = (uint)gpuaddr; - *cmds++ = 1; - /* generate interrupt on preemption completion */ - *cmds++ = 1 << CP_PREEMPT_ORDINAL_INTERRUPT; - - return cmds - cmds_orig; - -} - -static int a4xx_preemption_pre_ibsubmit( - struct adreno_device *adreno_dev, - struct adreno_ringbuffer *rb, unsigned int *cmds, - struct kgsl_context *context, uint64_t cond_addr, - struct kgsl_memobj_node *ib) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - unsigned int *cmds_orig = cmds; - int exec_ib = 0; - - cmds += a4xx_preemption_token(adreno_dev, rb, cmds, - device->memstore.gpuaddr + - KGSL_MEMSTORE_OFFSET(context->id, preempted)); - - if (ib) - exec_ib = 1; - - *cmds++ = cp_type3_packet(CP_COND_EXEC, 4); - *cmds++ = cond_addr; - *cmds++ = cond_addr; - *cmds++ = 1; - *cmds++ = 7 + exec_ib * 3; - if (exec_ib) { - *cmds++ = cp_type3_packet(CP_INDIRECT_BUFFER_PFE, 2); - *cmds++ = ib->gpuaddr; - *cmds++ = (unsigned int) ib->size >> 2; - } - /* clear preemption flag */ - *cmds++ = cp_type3_packet(CP_MEM_WRITE, 2); - *cmds++ = cond_addr; - *cmds++ = 0; - *cmds++ = cp_type3_packet(CP_WAIT_MEM_WRITES, 1); - *cmds++ = 0; - *cmds++ = cp_type3_packet(CP_WAIT_FOR_ME, 1); - *cmds++ = 0; - - return cmds - cmds_orig; -} - /* * a4xx_is_sptp_idle() - A430 SP/TP should be off to be considered idle * @adreno_dev: The adreno device pointer @@ -723,6 +618,8 @@ static void a4xx_start(struct adreno_device *adreno_dev) gpudev->vbif_xin_halt_ctrl0_mask = A405_VBIF_XIN_HALT_CTRL0_MASK; + adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE); + a4xx_protect_init(adreno_dev); } @@ -839,6 +736,7 @@ static unsigned int a4xx_register_offsets[ADRENO_REG_REGISTER_MAX] = { ADRENO_REG_DEFINE(ADRENO_REG_CP_WFI_PEND_CTR, A4XX_CP_WFI_PEND_CTR), ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_BASE, A4XX_CP_RB_BASE), ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_BASE_HI, ADRENO_REG_SKIP), + ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_RPTR_ADDR_LO, A4XX_CP_RB_RPTR_ADDR), ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_RPTR, A4XX_CP_RB_RPTR), ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_WPTR, A4XX_CP_RB_WPTR), ADRENO_REG_DEFINE(ADRENO_REG_CP_CNTL, A4XX_CP_CNTL), @@ -1634,8 +1532,15 @@ static int a4xx_rb_start(struct adreno_device *adreno_dev, unsigned int start_type) { struct adreno_ringbuffer *rb = ADRENO_CURRENT_RINGBUFFER(adreno_dev); + struct kgsl_device *device = &adreno_dev->dev; + uint64_t addr; int ret; + addr = SCRATCH_RPTR_GPU_ADDR(device, rb->id); + + adreno_writereg64(adreno_dev, ADRENO_REG_CP_RB_RPTR_ADDR_LO, + ADRENO_REG_CP_RB_RPTR_ADDR_HI, addr); + /* * The size of the ringbuffer in the hardware is the log2 * representation of the size in quadwords (sizedwords / 2). @@ -1644,8 +1549,8 @@ static int a4xx_rb_start(struct adreno_device *adreno_dev, */ adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_CNTL, - (ilog2(KGSL_RB_DWORDS >> 1) & 0x3F) | - (1 << 27)); + ((ilog2(4) << 8) & 0x1F00) | + (ilog2(KGSL_RB_DWORDS >> 1) & 0x3F)); adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_BASE, rb->buffer_desc.gpuaddr); @@ -1755,6 +1660,19 @@ static struct adreno_coresight a4xx_coresight = { .groups = a4xx_coresight_groups, }; +static void a4xx_preempt_callback(struct adreno_device *adreno_dev, int bit) +{ + if (atomic_read(&adreno_dev->preempt.state) != ADRENO_PREEMPT_TRIGGERED) + return; + + trace_adreno_hw_preempt_trig_to_comp_int(adreno_dev->cur_rb, + adreno_dev->next_rb, + adreno_get_rptr(adreno_dev->cur_rb), + adreno_get_rptr(adreno_dev->next_rb)); + + adreno_dispatcher_schedule(KGSL_DEVICE(adreno_dev)); +} + #define A4XX_INT_MASK \ ((1 << A4XX_INT_RBBM_AHB_ERROR) | \ (1 << A4XX_INT_RBBM_REG_TIMEOUT) | \ @@ -1792,7 +1710,7 @@ static struct adreno_irq_funcs a4xx_irq_funcs[32] = { /* 6 - RBBM_ATB_ASYNC_OVERFLOW */ ADRENO_IRQ_CALLBACK(a4xx_err_callback), ADRENO_IRQ_CALLBACK(NULL), /* 7 - RBBM_GPC_ERR */ - ADRENO_IRQ_CALLBACK(adreno_dispatcher_preempt_callback), /* 8 - CP_SW */ + ADRENO_IRQ_CALLBACK(a4xx_preempt_callback), /* 8 - CP_SW */ ADRENO_IRQ_CALLBACK(a4xx_err_callback), /* 9 - CP_OPCODE_ERROR */ /* 10 - CP_RESERVED_BIT_ERROR */ ADRENO_IRQ_CALLBACK(a4xx_err_callback), @@ -1833,433 +1751,6 @@ static struct adreno_snapshot_data a4xx_snapshot_data = { .sect_sizes = &a4xx_snap_sizes, }; -#define ADRENO_RB_PREEMPT_TOKEN_DWORDS 125 - -static int a4xx_submit_preempt_token(struct adreno_ringbuffer *rb, - struct adreno_ringbuffer *incoming_rb) -{ - struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb); - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - unsigned int *ringcmds, *start; - struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); - int ptname; - struct kgsl_pagetable *pt; - int pt_switch_sizedwords = 0, total_sizedwords = 20; - unsigned link[ADRENO_RB_PREEMPT_TOKEN_DWORDS]; - uint i; - - if (incoming_rb->preempted_midway) { - - kgsl_sharedmem_readl(&incoming_rb->pagetable_desc, - &ptname, offsetof( - struct adreno_ringbuffer_pagetable_info, - current_rb_ptname)); - pt = kgsl_mmu_get_pt_from_ptname(&(device->mmu), - ptname); - /* - * always expect a valid pt, else pt refcounting is - * messed up or current pt tracking has a bug which - * could lead to eventual disaster - */ - BUG_ON(!pt); - /* set the ringbuffer for incoming RB */ - pt_switch_sizedwords = - adreno_iommu_set_pt_generate_cmds(incoming_rb, - &link[0], pt); - total_sizedwords += pt_switch_sizedwords; - } - - /* - * Allocate total_sizedwords space in RB, this is the max space - * required. - */ - ringcmds = adreno_ringbuffer_allocspace(rb, total_sizedwords); - - if (IS_ERR(ringcmds)) - return PTR_ERR(ringcmds); - - start = ringcmds; - - *ringcmds++ = cp_packet(adreno_dev, CP_SET_PROTECTED_MODE, 1); - *ringcmds++ = 0; - - if (incoming_rb->preempted_midway) { - for (i = 0; i < pt_switch_sizedwords; i++) - *ringcmds++ = link[i]; - } - - *ringcmds++ = cp_register(adreno_dev, adreno_getreg(adreno_dev, - ADRENO_REG_CP_PREEMPT_DISABLE), 1); - *ringcmds++ = 0; - - *ringcmds++ = cp_packet(adreno_dev, CP_SET_PROTECTED_MODE, 1); - *ringcmds++ = 1; - - ringcmds += gpudev->preemption_token(adreno_dev, rb, ringcmds, - device->memstore.gpuaddr + - KGSL_MEMSTORE_RB_OFFSET(rb, preempted)); - - if ((uint)(ringcmds - start) > total_sizedwords) { - KGSL_DRV_ERR(device, "Insufficient rb size allocated\n"); - BUG(); - } - - /* - * If we have commands less than the space reserved in RB - * adjust the wptr accordingly - */ - rb->wptr = rb->wptr - (total_sizedwords - (uint)(ringcmds - start)); - - /* submit just the preempt token */ - mb(); - kgsl_pwrscale_busy(device); - adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_WPTR, rb->wptr); - return 0; -} - -/** - * a4xx_preempt_trig_state() - Schedule preemption in TRIGGERRED - * state - * @adreno_dev: Device which is in TRIGGERRED state - */ -static void a4xx_preempt_trig_state( - struct adreno_device *adreno_dev) -{ - struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - unsigned int rbbase, val; - - /* - * Hardware not yet idle means that preemption interrupt - * may still occur, nothing to do here until interrupt signals - * completion of preemption, just return here - */ - if (!adreno_hw_isidle(adreno_dev)) - return; - - /* - * We just changed states, reschedule dispatcher to change - * preemption states - */ - if (ADRENO_DISPATCHER_PREEMPT_TRIGGERED != - atomic_read(&dispatcher->preemption_state)) { - adreno_dispatcher_schedule(device); - return; - } - - /* - * H/W is idle and we did not get a preemption interrupt, may - * be device went idle w/o encountering any preempt token or - * we already preempted w/o interrupt - */ - adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_BASE, &rbbase); - /* Did preemption occur, if so then change states and return */ - if (rbbase != adreno_dev->cur_rb->buffer_desc.gpuaddr) { - adreno_readreg(adreno_dev, ADRENO_REG_CP_PREEMPT_DEBUG, &val); - if (val && rbbase == adreno_dev->next_rb->buffer_desc.gpuaddr) { - KGSL_DRV_INFO(device, - "Preemption completed without interrupt\n"); - trace_adreno_hw_preempt_trig_to_comp(adreno_dev->cur_rb, - adreno_dev->next_rb); - atomic_set(&dispatcher->preemption_state, - ADRENO_DISPATCHER_PREEMPT_COMPLETE); - adreno_dispatcher_schedule(device); - return; - } - adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); - /* reschedule dispatcher to take care of the fault */ - adreno_dispatcher_schedule(device); - return; - } - /* - * Check if preempt token was submitted after preemption trigger, if so - * then preemption should have occurred, since device is already idle it - * means something went wrong - trigger FT - */ - if (dispatcher->preempt_token_submit) { - adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); - /* reschedule dispatcher to take care of the fault */ - adreno_dispatcher_schedule(device); - return; - } - /* - * Preempt token was not submitted after preemption trigger so device - * may have gone idle before preemption could occur, if there are - * commands that got submitted to current RB after triggering preemption - * then submit them as those commands may have a preempt token in them - */ - adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_RPTR, - &adreno_dev->cur_rb->rptr); - if (adreno_dev->cur_rb->rptr != adreno_dev->cur_rb->wptr) { - /* - * Memory barrier before informing the - * hardware of new commands - */ - mb(); - kgsl_pwrscale_busy(device); - adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_WPTR, - adreno_dev->cur_rb->wptr); - return; - } - - /* Submit preempt token to make preemption happen */ - if (adreno_drawctxt_switch(adreno_dev, adreno_dev->cur_rb, NULL, 0)) - BUG(); - if (a4xx_submit_preempt_token(adreno_dev->cur_rb, - adreno_dev->next_rb)) - BUG(); - dispatcher->preempt_token_submit = 1; - adreno_dev->cur_rb->wptr_preempt_end = adreno_dev->cur_rb->wptr; - trace_adreno_hw_preempt_token_submit(adreno_dev->cur_rb, - adreno_dev->next_rb); -} - -/** - * a4xx_preempt_clear_state() - Schedule preemption in - * CLEAR state. Preemption can be issued in this state. - * @adreno_dev: Device which is in CLEAR state - */ -static void a4xx_preempt_clear_state( - struct adreno_device *adreno_dev) - -{ - struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct adreno_dispatcher_cmdqueue *dispatch_tempq; - struct kgsl_cmdbatch *cmdbatch; - struct adreno_ringbuffer *highest_busy_rb; - int switch_low_to_high; - int ret; - - /* Device not awake means there is nothing to do */ - if (!kgsl_state_is_awake(device)) - return; - - /* keep updating the current rptr when preemption is clear */ - adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_RPTR, - &(adreno_dev->cur_rb->rptr)); - - highest_busy_rb = adreno_dispatcher_get_highest_busy_rb(adreno_dev); - if (!highest_busy_rb) - return; - - switch_low_to_high = adreno_compare_prio_level( - highest_busy_rb->id, - adreno_dev->cur_rb->id); - - /* already current then return */ - if (!switch_low_to_high) - return; - - if (switch_low_to_high < 0) { - /* - * if switching to lower priority make sure that the rptr and - * wptr are equal, when the lower rb is not starved - */ - if (adreno_dev->cur_rb->rptr != adreno_dev->cur_rb->wptr) - return; - /* - * switch to default context because when we switch back - * to higher context then its not known which pt will - * be current, so by making it default here the next - * commands submitted will set the right pt - */ - ret = adreno_drawctxt_switch(adreno_dev, - adreno_dev->cur_rb, - NULL, 0); - /* - * lower priority RB has to wait until space opens up in - * higher RB - */ - if (ret) - return; - - adreno_writereg(adreno_dev, - ADRENO_REG_CP_PREEMPT_DISABLE, 1); - } - - /* - * setup registers to do the switch to highest priority RB - * which is not empty or may be starving away(poor thing) - */ - a4xx_preemption_start(adreno_dev, highest_busy_rb); - - /* turn on IOMMU as the preemption may trigger pt switch */ - kgsl_mmu_enable_clk(&device->mmu); - - atomic_set(&dispatcher->preemption_state, - ADRENO_DISPATCHER_PREEMPT_TRIGGERED); - - adreno_dev->next_rb = highest_busy_rb; - mod_timer(&dispatcher->preempt_timer, jiffies + - msecs_to_jiffies(ADRENO_DISPATCH_PREEMPT_TIMEOUT)); - - trace_adreno_hw_preempt_clear_to_trig(adreno_dev->cur_rb, - adreno_dev->next_rb); - /* issue PREEMPT trigger */ - adreno_writereg(adreno_dev, ADRENO_REG_CP_PREEMPT, 1); - /* - * IOMMU clock can be safely switched off after the timestamp - * of the first command in the new rb - */ - dispatch_tempq = &adreno_dev->next_rb->dispatch_q; - if (dispatch_tempq->head != dispatch_tempq->tail) - cmdbatch = dispatch_tempq->cmd_q[dispatch_tempq->head]; - else - cmdbatch = NULL; - if (cmdbatch) - adreno_ringbuffer_mmu_disable_clk_on_ts(device, - adreno_dev->next_rb, - cmdbatch->global_ts); - else - adreno_ringbuffer_mmu_disable_clk_on_ts(device, - adreno_dev->next_rb, adreno_dev->next_rb->timestamp); - /* submit preempt token packet to ensure preemption */ - if (switch_low_to_high < 0) { - ret = a4xx_submit_preempt_token( - adreno_dev->cur_rb, adreno_dev->next_rb); - /* - * unexpected since we are submitting this when rptr = wptr, - * this was checked above already - */ - BUG_ON(ret); - dispatcher->preempt_token_submit = 1; - adreno_dev->cur_rb->wptr_preempt_end = adreno_dev->cur_rb->wptr; - } else { - dispatcher->preempt_token_submit = 0; - adreno_dispatcher_schedule(device); - adreno_dev->cur_rb->wptr_preempt_end = 0xFFFFFFFF; - } -} - -/** - * a4xx_preempt_complete_state() - Schedule preemption in - * COMPLETE state - * @adreno_dev: Device which is in COMPLETE state - */ -static void a4xx_preempt_complete_state( - struct adreno_device *adreno_dev) - -{ - struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct adreno_dispatcher_cmdqueue *dispatch_q; - unsigned int wptr, rbbase; - unsigned int val, val1; - - del_timer_sync(&dispatcher->preempt_timer); - - adreno_readreg(adreno_dev, ADRENO_REG_CP_PREEMPT, &val); - adreno_readreg(adreno_dev, ADRENO_REG_CP_PREEMPT_DEBUG, &val1); - - if (val || !val1) { - KGSL_DRV_ERR(device, - "Invalid state after preemption CP_PREEMPT: %08x, CP_PREEMPT_DEBUG: %08x\n", - val, val1); - adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); - adreno_dispatcher_schedule(device); - return; - } - adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_BASE, &rbbase); - if (rbbase != adreno_dev->next_rb->buffer_desc.gpuaddr) { - KGSL_DRV_ERR(device, - "RBBASE incorrect after preemption, expected %x got %016llx\b", - rbbase, - adreno_dev->next_rb->buffer_desc.gpuaddr); - adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); - adreno_dispatcher_schedule(device); - return; - } - - a4xx_preemption_save(adreno_dev, adreno_dev->cur_rb); - - dispatch_q = &(adreno_dev->cur_rb->dispatch_q); - /* new RB is the current RB */ - trace_adreno_hw_preempt_comp_to_clear(adreno_dev->next_rb, - adreno_dev->cur_rb); - adreno_dev->prev_rb = adreno_dev->cur_rb; - adreno_dev->cur_rb = adreno_dev->next_rb; - adreno_dev->cur_rb->preempted_midway = 0; - adreno_dev->cur_rb->wptr_preempt_end = 0xFFFFFFFF; - adreno_dev->next_rb = NULL; - if (adreno_disp_preempt_fair_sched) { - /* starved rb is now scheduled so unhalt dispatcher */ - if (ADRENO_DISPATCHER_RB_STARVE_TIMER_ELAPSED == - adreno_dev->cur_rb->starve_timer_state) - adreno_put_gpu_halt(adreno_dev); - adreno_dev->cur_rb->starve_timer_state = - ADRENO_DISPATCHER_RB_STARVE_TIMER_SCHEDULED; - adreno_dev->cur_rb->sched_timer = jiffies; - /* - * If the outgoing RB is has commands then set the - * busy time for it - */ - if (adreno_dev->prev_rb->rptr != adreno_dev->prev_rb->wptr) { - adreno_dev->prev_rb->starve_timer_state = - ADRENO_DISPATCHER_RB_STARVE_TIMER_INIT; - adreno_dev->prev_rb->sched_timer = jiffies; - } else { - adreno_dev->prev_rb->starve_timer_state = - ADRENO_DISPATCHER_RB_STARVE_TIMER_UNINIT; - } - } - atomic_set(&dispatcher->preemption_state, - ADRENO_DISPATCHER_PREEMPT_CLEAR); - if (adreno_compare_prio_level(adreno_dev->prev_rb->id, - adreno_dev->cur_rb->id) < 0) { - if (adreno_dev->prev_rb->wptr_preempt_end != - adreno_dev->prev_rb->rptr) - adreno_dev->prev_rb->preempted_midway = 1; - } else if (adreno_dev->prev_rb->wptr_preempt_end != - adreno_dev->prev_rb->rptr) { - BUG(); - } - /* submit wptr if required for new rb */ - adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_WPTR, &wptr); - if (adreno_dev->cur_rb->wptr != wptr) { - kgsl_pwrscale_busy(device); - adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_WPTR, - adreno_dev->cur_rb->wptr); - } - /* clear preemption register */ - adreno_writereg(adreno_dev, ADRENO_REG_CP_PREEMPT_DEBUG, 0); - adreno_preempt_process_dispatch_queue(adreno_dev, dispatch_q); -} - -static void a4xx_preemption_schedule( - struct adreno_device *adreno_dev) -{ - struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - - if (!adreno_is_preemption_enabled(adreno_dev)) - return; - - mutex_lock(&device->mutex); - - switch (atomic_read(&dispatcher->preemption_state)) { - case ADRENO_DISPATCHER_PREEMPT_CLEAR: - a4xx_preempt_clear_state(adreno_dev); - break; - case ADRENO_DISPATCHER_PREEMPT_TRIGGERED: - a4xx_preempt_trig_state(adreno_dev); - /* - * if we transitioned to next state then fall-through - * processing to next state - */ - if (!adreno_preempt_state(adreno_dev, - ADRENO_DISPATCHER_PREEMPT_COMPLETE)) - break; - case ADRENO_DISPATCHER_PREEMPT_COMPLETE: - a4xx_preempt_complete_state(adreno_dev); - break; - default: - BUG(); - } - - mutex_unlock(&device->mutex); -} - struct adreno_gpudev adreno_a4xx_gpudev = { .reg_offsets = &a4xx_reg_offsets, .ft_perf_counters = a4xx_ft_perf_counters, @@ -2284,6 +1775,6 @@ struct adreno_gpudev adreno_a4xx_gpudev = { .regulator_enable = a4xx_regulator_enable, .regulator_disable = a4xx_regulator_disable, .preemption_pre_ibsubmit = a4xx_preemption_pre_ibsubmit, - .preemption_token = a4xx_preemption_token, .preemption_schedule = a4xx_preemption_schedule, + .preemption_init = a4xx_preemption_init, }; diff --git a/drivers/gpu/msm/adreno_a4xx.h b/drivers/gpu/msm/adreno_a4xx.h index e425dc8e9f7b..5dabc26fd34f 100644 --- a/drivers/gpu/msm/adreno_a4xx.h +++ b/drivers/gpu/msm/adreno_a4xx.h @@ -47,6 +47,15 @@ "RBBM_DPM_THERMAL_YELLOW_ERR" }, \ { BIT(A4XX_INT_RBBM_DPM_THERMAL_RED_ERR), "RBBM_DPM_THERMAL_RED_ERR" } +unsigned int a4xx_preemption_pre_ibsubmit(struct adreno_device *adreno_dev, + struct adreno_ringbuffer *rb, + unsigned int *cmds, + struct kgsl_context *context); + +void a4xx_preemption_schedule(struct adreno_device *adreno_dev); + +int a4xx_preemption_init(struct adreno_device *adreno_dev); + void a4xx_snapshot(struct adreno_device *adreno_dev, struct kgsl_snapshot *snapshot); diff --git a/drivers/gpu/msm/adreno_a4xx_preempt.c b/drivers/gpu/msm/adreno_a4xx_preempt.c new file mode 100644 index 000000000000..4087ac60c89e --- /dev/null +++ b/drivers/gpu/msm/adreno_a4xx_preempt.c @@ -0,0 +1,571 @@ +/* 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. + * + */ + +#include "adreno.h" +#include "adreno_a4xx.h" +#include "adreno_trace.h" +#include "adreno_pm4types.h" + +#define ADRENO_RB_PREEMPT_TOKEN_DWORDS 125 + +static void a4xx_preemption_timer(unsigned long data) +{ + struct adreno_device *adreno_dev = (struct adreno_device *) data; + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + unsigned int cur_rptr = adreno_get_rptr(adreno_dev->cur_rb); + unsigned int next_rptr = adreno_get_rptr(adreno_dev->next_rb); + + KGSL_DRV_ERR(device, + "Preemption timed out. cur_rb rptr/wptr %x/%x id %d, next_rb rptr/wptr %x/%x id %d, disp_state: %d\n", + cur_rptr, adreno_dev->cur_rb->wptr, adreno_dev->cur_rb->id, + next_rptr, adreno_dev->next_rb->wptr, adreno_dev->next_rb->id, + atomic_read(&adreno_dev->preempt.state)); + + adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); + adreno_dispatcher_schedule(device); +} + +static unsigned int a4xx_preemption_token(struct adreno_device *adreno_dev, + unsigned int *cmds, uint64_t gpuaddr) +{ + unsigned int *cmds_orig = cmds; + + /* Turn on preemption flag */ + /* preemption token - fill when pt switch command size is known */ + *cmds++ = cp_type3_packet(CP_PREEMPT_TOKEN, 3); + *cmds++ = (uint)gpuaddr; + *cmds++ = 1; + /* generate interrupt on preemption completion */ + *cmds++ = 1 << CP_PREEMPT_ORDINAL_INTERRUPT; + + return (unsigned int) (cmds - cmds_orig); +} + +unsigned int a4xx_preemption_pre_ibsubmit(struct adreno_device *adreno_dev, + struct adreno_ringbuffer *rb, unsigned int *cmds, + struct kgsl_context *context) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + unsigned int *cmds_orig = cmds; + unsigned int cond_addr = device->memstore.gpuaddr + + MEMSTORE_ID_GPU_ADDR(device, context->id, preempted); + + cmds += a4xx_preemption_token(adreno_dev, cmds, cond_addr); + + *cmds++ = cp_type3_packet(CP_COND_EXEC, 4); + *cmds++ = cond_addr; + *cmds++ = cond_addr; + *cmds++ = 1; + *cmds++ = 7; + + /* clear preemption flag */ + *cmds++ = cp_type3_packet(CP_MEM_WRITE, 2); + *cmds++ = cond_addr; + *cmds++ = 0; + *cmds++ = cp_type3_packet(CP_WAIT_MEM_WRITES, 1); + *cmds++ = 0; + *cmds++ = cp_type3_packet(CP_WAIT_FOR_ME, 1); + *cmds++ = 0; + + return (unsigned int) (cmds - cmds_orig); +} + + +static void a4xx_preemption_start(struct adreno_device *adreno_dev, + struct adreno_ringbuffer *rb) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + uint32_t val; + + /* + * Setup scratch registers from which the GPU will program the + * registers required to start execution of new ringbuffer + * set ringbuffer address + */ + kgsl_regwrite(device, A4XX_CP_SCRATCH_REG8, + rb->buffer_desc.gpuaddr); + kgsl_regread(device, A4XX_CP_RB_CNTL, &val); + /* scratch REG9 corresponds to CP_RB_CNTL register */ + kgsl_regwrite(device, A4XX_CP_SCRATCH_REG9, val); + /* scratch REG10 corresponds to rptr address */ + kgsl_regwrite(device, A4XX_CP_SCRATCH_REG10, + SCRATCH_RPTR_GPU_ADDR(device, rb->id)); + /* scratch REG11 corresponds to rptr */ + kgsl_regwrite(device, A4XX_CP_SCRATCH_REG11, adreno_get_rptr(rb)); + /* scratch REG12 corresponds to wptr */ + kgsl_regwrite(device, A4XX_CP_SCRATCH_REG12, rb->wptr); + /* + * scratch REG13 corresponds to IB1_BASE, + * 0 since we do not do switches in between IB's + */ + kgsl_regwrite(device, A4XX_CP_SCRATCH_REG13, 0); + /* scratch REG14 corresponds to IB1_BUFSZ */ + kgsl_regwrite(device, A4XX_CP_SCRATCH_REG14, 0); + /* scratch REG15 corresponds to IB2_BASE */ + kgsl_regwrite(device, A4XX_CP_SCRATCH_REG15, 0); + /* scratch REG16 corresponds to IB2_BUFSZ */ + kgsl_regwrite(device, A4XX_CP_SCRATCH_REG16, 0); + /* scratch REG17 corresponds to GPR11 */ + kgsl_regwrite(device, A4XX_CP_SCRATCH_REG17, rb->gpr11); +} + +static void a4xx_preemption_save(struct adreno_device *adreno_dev, + struct adreno_ringbuffer *rb) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + + kgsl_regread(device, A4XX_CP_SCRATCH_REG23, &rb->gpr11); +} + + +static int a4xx_submit_preempt_token(struct adreno_ringbuffer *rb, + struct adreno_ringbuffer *incoming_rb) +{ + struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb); + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + unsigned int *ringcmds, *start; + int ptname; + struct kgsl_pagetable *pt; + int pt_switch_sizedwords = 0, total_sizedwords = 20; + unsigned link[ADRENO_RB_PREEMPT_TOKEN_DWORDS]; + uint i; + + if (incoming_rb->preempted_midway) { + + kgsl_sharedmem_readl(&incoming_rb->pagetable_desc, + &ptname, PT_INFO_OFFSET(current_rb_ptname)); + pt = kgsl_mmu_get_pt_from_ptname(&(device->mmu), + ptname); + /* set the ringbuffer for incoming RB */ + pt_switch_sizedwords = + adreno_iommu_set_pt_generate_cmds(incoming_rb, + &link[0], pt); + total_sizedwords += pt_switch_sizedwords; + } + + /* + * Allocate total_sizedwords space in RB, this is the max space + * required. + */ + ringcmds = adreno_ringbuffer_allocspace(rb, total_sizedwords); + + if (IS_ERR(ringcmds)) + return PTR_ERR(ringcmds); + + start = ringcmds; + + *ringcmds++ = cp_packet(adreno_dev, CP_SET_PROTECTED_MODE, 1); + *ringcmds++ = 0; + + if (incoming_rb->preempted_midway) { + for (i = 0; i < pt_switch_sizedwords; i++) + *ringcmds++ = link[i]; + } + + *ringcmds++ = cp_register(adreno_dev, adreno_getreg(adreno_dev, + ADRENO_REG_CP_PREEMPT_DISABLE), 1); + *ringcmds++ = 0; + + *ringcmds++ = cp_packet(adreno_dev, CP_SET_PROTECTED_MODE, 1); + *ringcmds++ = 1; + + ringcmds += a4xx_preemption_token(adreno_dev, ringcmds, + device->memstore.gpuaddr + + MEMSTORE_RB_OFFSET(rb, preempted)); + + if ((uint)(ringcmds - start) > total_sizedwords) + KGSL_DRV_ERR(device, "Insufficient rb size allocated\n"); + + /* + * If we have commands less than the space reserved in RB + * adjust the wptr accordingly + */ + rb->wptr = rb->wptr - (total_sizedwords - (uint)(ringcmds - start)); + + /* submit just the preempt token */ + mb(); + kgsl_pwrscale_busy(device); + adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_WPTR, rb->wptr); + return 0; +} + +static void a4xx_preempt_trig_state(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + unsigned int rbbase, val; + int ret; + + /* + * Hardware not yet idle means that preemption interrupt + * may still occur, nothing to do here until interrupt signals + * completion of preemption, just return here + */ + if (!adreno_hw_isidle(adreno_dev)) + return; + + /* + * We just changed states, reschedule dispatcher to change + * preemption states + */ + + if (atomic_read(&adreno_dev->preempt.state) != + ADRENO_PREEMPT_TRIGGERED) { + adreno_dispatcher_schedule(device); + return; + } + + /* + * H/W is idle and we did not get a preemption interrupt, may + * be device went idle w/o encountering any preempt token or + * we already preempted w/o interrupt + */ + adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_BASE, &rbbase); + /* Did preemption occur, if so then change states and return */ + if (rbbase != adreno_dev->cur_rb->buffer_desc.gpuaddr) { + adreno_readreg(adreno_dev, ADRENO_REG_CP_PREEMPT_DEBUG, &val); + if (val && rbbase == adreno_dev->next_rb->buffer_desc.gpuaddr) { + KGSL_DRV_INFO(device, + "Preemption completed without interrupt\n"); + trace_adreno_hw_preempt_trig_to_comp(adreno_dev->cur_rb, + adreno_dev->next_rb, + adreno_get_rptr(adreno_dev->cur_rb), + adreno_get_rptr(adreno_dev->next_rb)); + adreno_set_preempt_state(adreno_dev, + ADRENO_PREEMPT_COMPLETE); + adreno_dispatcher_schedule(device); + return; + } + adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); + /* reschedule dispatcher to take care of the fault */ + adreno_dispatcher_schedule(device); + return; + } + /* + * Check if preempt token was submitted after preemption trigger, if so + * then preemption should have occurred, since device is already idle it + * means something went wrong - trigger FT + */ + if (adreno_dev->preempt.token_submit) { + adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); + /* reschedule dispatcher to take care of the fault */ + adreno_dispatcher_schedule(device); + return; + } + /* + * Preempt token was not submitted after preemption trigger so device + * may have gone idle before preemption could occur, if there are + * commands that got submitted to current RB after triggering preemption + * then submit them as those commands may have a preempt token in them + */ + if (!adreno_rb_empty(adreno_dev->cur_rb)) { + /* + * Memory barrier before informing the + * hardware of new commands + */ + mb(); + kgsl_pwrscale_busy(device); + adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_WPTR, + adreno_dev->cur_rb->wptr); + return; + } + + /* Submit preempt token to make preemption happen */ + ret = adreno_drawctxt_switch(adreno_dev, adreno_dev->cur_rb, + NULL, 0); + if (ret) + KGSL_DRV_ERR(device, + "Unable to switch context to NULL: %d\n", ret); + + ret = a4xx_submit_preempt_token(adreno_dev->cur_rb, + adreno_dev->next_rb); + if (ret) + KGSL_DRV_ERR(device, + "Unable to submit preempt token: %d\n", ret); + + adreno_dev->preempt.token_submit = true; + adreno_dev->cur_rb->wptr_preempt_end = adreno_dev->cur_rb->wptr; + trace_adreno_hw_preempt_token_submit(adreno_dev->cur_rb, + adreno_dev->next_rb, + adreno_get_rptr(adreno_dev->cur_rb), + adreno_get_rptr(adreno_dev->next_rb)); +} + +static struct adreno_ringbuffer *a4xx_next_ringbuffer( + struct adreno_device *adreno_dev) +{ + struct adreno_ringbuffer *rb, *next = NULL; + int i; + + FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { + if (!adreno_rb_empty(rb) && next == NULL) { + next = rb; + continue; + } + + if (!adreno_disp_preempt_fair_sched) + continue; + + switch (rb->starve_timer_state) { + case ADRENO_DISPATCHER_RB_STARVE_TIMER_UNINIT: + if (!adreno_rb_empty(rb) && + adreno_dev->cur_rb != rb) { + rb->starve_timer_state = + ADRENO_DISPATCHER_RB_STARVE_TIMER_INIT; + rb->sched_timer = jiffies; + } + break; + case ADRENO_DISPATCHER_RB_STARVE_TIMER_INIT: + if (time_after(jiffies, rb->sched_timer + + msecs_to_jiffies( + adreno_dispatch_starvation_time))) { + rb->starve_timer_state = + ADRENO_DISPATCHER_RB_STARVE_TIMER_ELAPSED; + /* halt dispatcher to remove starvation */ + adreno_get_gpu_halt(adreno_dev); + } + break; + case ADRENO_DISPATCHER_RB_STARVE_TIMER_SCHEDULED: + /* + * If the RB has not been running for the minimum + * time slice then allow it to run + */ + if (!adreno_rb_empty(rb) && time_before(jiffies, + adreno_dev->cur_rb->sched_timer + + msecs_to_jiffies(adreno_dispatch_time_slice))) + next = rb; + else + rb->starve_timer_state = + ADRENO_DISPATCHER_RB_STARVE_TIMER_UNINIT; + break; + case ADRENO_DISPATCHER_RB_STARVE_TIMER_ELAPSED: + default: + break; + } + } + + return next; +} + +static void a4xx_preempt_clear_state(struct adreno_device *adreno_dev) + +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct adreno_ringbuffer *highest_busy_rb; + int switch_low_to_high; + int ret; + + /* Device not awake means there is nothing to do */ + if (!kgsl_state_is_awake(device)) + return; + + highest_busy_rb = a4xx_next_ringbuffer(adreno_dev); + if (!highest_busy_rb || highest_busy_rb == adreno_dev->cur_rb) + return; + + switch_low_to_high = adreno_compare_prio_level( + highest_busy_rb->id, + adreno_dev->cur_rb->id); + + if (switch_low_to_high < 0) { + /* + * if switching to lower priority make sure that the rptr and + * wptr are equal, when the lower rb is not starved + */ + if (!adreno_rb_empty(adreno_dev->cur_rb)) + return; + /* + * switch to default context because when we switch back + * to higher context then its not known which pt will + * be current, so by making it default here the next + * commands submitted will set the right pt + */ + ret = adreno_drawctxt_switch(adreno_dev, + adreno_dev->cur_rb, + NULL, 0); + /* + * lower priority RB has to wait until space opens up in + * higher RB + */ + if (ret) { + KGSL_DRV_ERR(device, + "Unable to switch context to NULL: %d", + ret); + + return; + } + + adreno_writereg(adreno_dev, + ADRENO_REG_CP_PREEMPT_DISABLE, 1); + } + + /* + * setup registers to do the switch to highest priority RB + * which is not empty or may be starving away(poor thing) + */ + a4xx_preemption_start(adreno_dev, highest_busy_rb); + + adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_TRIGGERED); + + adreno_dev->next_rb = highest_busy_rb; + mod_timer(&adreno_dev->preempt.timer, jiffies + + msecs_to_jiffies(ADRENO_PREEMPT_TIMEOUT)); + + trace_adreno_hw_preempt_clear_to_trig(adreno_dev->cur_rb, + adreno_dev->next_rb, + adreno_get_rptr(adreno_dev->cur_rb), + adreno_get_rptr(adreno_dev->next_rb)); + /* issue PREEMPT trigger */ + adreno_writereg(adreno_dev, ADRENO_REG_CP_PREEMPT, 1); + + /* submit preempt token packet to ensure preemption */ + if (switch_low_to_high < 0) { + ret = a4xx_submit_preempt_token( + adreno_dev->cur_rb, adreno_dev->next_rb); + KGSL_DRV_ERR(device, + "Unable to submit preempt token: %d\n", ret); + adreno_dev->preempt.token_submit = true; + adreno_dev->cur_rb->wptr_preempt_end = adreno_dev->cur_rb->wptr; + } else { + adreno_dev->preempt.token_submit = false; + adreno_dispatcher_schedule(device); + adreno_dev->cur_rb->wptr_preempt_end = 0xFFFFFFFF; + } +} + +static void a4xx_preempt_complete_state(struct adreno_device *adreno_dev) + +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + unsigned int wptr, rbbase; + unsigned int val, val1; + unsigned int prevrptr; + + del_timer_sync(&adreno_dev->preempt.timer); + + adreno_readreg(adreno_dev, ADRENO_REG_CP_PREEMPT, &val); + adreno_readreg(adreno_dev, ADRENO_REG_CP_PREEMPT_DEBUG, &val1); + + if (val || !val1) { + KGSL_DRV_ERR(device, + "Invalid state after preemption CP_PREEMPT: %08x, CP_PREEMPT_DEBUG: %08x\n", + val, val1); + adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); + adreno_dispatcher_schedule(device); + return; + } + adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_BASE, &rbbase); + if (rbbase != adreno_dev->next_rb->buffer_desc.gpuaddr) { + KGSL_DRV_ERR(device, + "RBBASE incorrect after preemption, expected %x got %016llx\b", + rbbase, + adreno_dev->next_rb->buffer_desc.gpuaddr); + adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); + adreno_dispatcher_schedule(device); + return; + } + + a4xx_preemption_save(adreno_dev, adreno_dev->cur_rb); + + /* new RB is the current RB */ + trace_adreno_hw_preempt_comp_to_clear(adreno_dev->next_rb, + adreno_dev->cur_rb, + adreno_get_rptr(adreno_dev->next_rb), + adreno_get_rptr(adreno_dev->cur_rb)); + + adreno_dev->prev_rb = adreno_dev->cur_rb; + adreno_dev->cur_rb = adreno_dev->next_rb; + adreno_dev->cur_rb->preempted_midway = 0; + adreno_dev->cur_rb->wptr_preempt_end = 0xFFFFFFFF; + adreno_dev->next_rb = NULL; + + if (adreno_disp_preempt_fair_sched) { + /* starved rb is now scheduled so unhalt dispatcher */ + if (ADRENO_DISPATCHER_RB_STARVE_TIMER_ELAPSED == + adreno_dev->cur_rb->starve_timer_state) + adreno_put_gpu_halt(adreno_dev); + adreno_dev->cur_rb->starve_timer_state = + ADRENO_DISPATCHER_RB_STARVE_TIMER_SCHEDULED; + adreno_dev->cur_rb->sched_timer = jiffies; + /* + * If the outgoing RB is has commands then set the + * busy time for it + */ + if (!adreno_rb_empty(adreno_dev->prev_rb)) { + adreno_dev->prev_rb->starve_timer_state = + ADRENO_DISPATCHER_RB_STARVE_TIMER_INIT; + adreno_dev->prev_rb->sched_timer = jiffies; + } else { + adreno_dev->prev_rb->starve_timer_state = + ADRENO_DISPATCHER_RB_STARVE_TIMER_UNINIT; + } + } + adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE); + + prevrptr = adreno_get_rptr(adreno_dev->prev_rb); + + if (adreno_compare_prio_level(adreno_dev->prev_rb->id, + adreno_dev->cur_rb->id) < 0) { + if (adreno_dev->prev_rb->wptr_preempt_end != prevrptr) + adreno_dev->prev_rb->preempted_midway = 1; + } + + /* submit wptr if required for new rb */ + adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_WPTR, &wptr); + if (adreno_dev->cur_rb->wptr != wptr) { + kgsl_pwrscale_busy(device); + adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_WPTR, + adreno_dev->cur_rb->wptr); + } + /* clear preemption register */ + adreno_writereg(adreno_dev, ADRENO_REG_CP_PREEMPT_DEBUG, 0); +} + +void a4xx_preemption_schedule(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + + if (!adreno_is_preemption_enabled(adreno_dev)) + return; + + mutex_lock(&device->mutex); + + switch (atomic_read(&adreno_dev->preempt.state)) { + case ADRENO_PREEMPT_NONE: + a4xx_preempt_clear_state(adreno_dev); + break; + case ADRENO_PREEMPT_TRIGGERED: + a4xx_preempt_trig_state(adreno_dev); + /* + * if we transitioned to next state then fall-through + * processing to next state + */ + if (!adreno_in_preempt_state(adreno_dev, + ADRENO_PREEMPT_COMPLETE)) + break; + case ADRENO_PREEMPT_COMPLETE: + a4xx_preempt_complete_state(adreno_dev); + break; + default: + break; + } + + mutex_unlock(&device->mutex); +} + +int a4xx_preemption_init(struct adreno_device *adreno_dev) +{ + setup_timer(&adreno_dev->preempt.timer, a4xx_preemption_timer, + (unsigned long) adreno_dev); + + return 0; +} diff --git a/drivers/gpu/msm/adreno_a4xx_snapshot.c b/drivers/gpu/msm/adreno_a4xx_snapshot.c index b07e970aae32..6921af5c0ab5 100644 --- a/drivers/gpu/msm/adreno_a4xx_snapshot.c +++ b/drivers/gpu/msm/adreno_a4xx_snapshot.c @@ -534,9 +534,6 @@ void a4xx_snapshot(struct adreno_device *adreno_dev, kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL, 0); kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2, 0); - /* Turn on MMU clocks since we read MMU registers */ - kgsl_mmu_enable_clk(&device->mmu); - /* Master set of (non debug) registers */ SNAPSHOT_REGISTERS(device, snapshot, a4xx_registers); @@ -554,8 +551,6 @@ void a4xx_snapshot(struct adreno_device *adreno_dev, a4xx_vbif_snapshot_registers, ARRAY_SIZE(a4xx_vbif_snapshot_registers)); - kgsl_mmu_disable_clk(&device->mmu); - kgsl_snapshot_indexed_registers(device, snapshot, A4XX_CP_STATE_DEBUG_INDEX, A4XX_CP_STATE_DEBUG_DATA, 0, snap_data->sect_sizes->cp_pfp); diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c index 512dcd483f45..96f72c59e4cd 100644 --- a/drivers/gpu/msm/adreno_a5xx.c +++ b/drivers/gpu/msm/adreno_a5xx.c @@ -60,19 +60,12 @@ static const struct adreno_vbif_platform a5xx_vbif_platforms[] = { { adreno_is_a506, a530_vbif }, }; -#define PREEMPT_RECORD(_field) \ - offsetof(struct a5xx_cp_preemption_record, _field) - -#define PREEMPT_SMMU_RECORD(_field) \ - offsetof(struct a5xx_cp_smmu_info, _field) - static void a5xx_irq_storm_worker(struct work_struct *work); static int _read_fw2_block_header(uint32_t *header, uint32_t id, uint32_t major, uint32_t minor); static void a5xx_gpmu_reset(struct work_struct *work); static int a5xx_gpmu_init(struct adreno_device *adreno_dev); - /** * Number of times to check if the regulator enabled before * giving up and returning failure. @@ -108,8 +101,9 @@ static void spin_idle_debug(struct kgsl_device *device, kgsl_regread(device, A5XX_CP_HW_FAULT, &hwfault); dev_err(device->dev, - " rb=%X/%X rbbm_status=%8.8X/%8.8X int_0_status=%8.8X\n", - rptr, wptr, status, status3, intstatus); + "rb=%d pos=%X/%X rbbm_status=%8.8X/%8.8X int_0_status=%8.8X\n", + adreno_dev->cur_rb->id, rptr, wptr, status, status3, intstatus); + dev_err(device->dev, " hwfault=%8.8X\n", hwfault); kgsl_device_snapshot(device, NULL); @@ -179,277 +173,6 @@ static void a5xx_check_features(struct adreno_device *adreno_dev) adreno_efuse_unmap(adreno_dev); } -/* - * a5xx_preemption_start() - Setup state to start preemption - */ -static void a5xx_preemption_start(struct adreno_device *adreno_dev, - struct adreno_ringbuffer *rb) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct kgsl_iommu *iommu = KGSL_IOMMU_PRIV(device); - uint64_t ttbr0; - uint32_t contextidr; - struct kgsl_pagetable *pt; - bool switch_default_pt = true; - - kgsl_sharedmem_writel(device, &rb->preemption_desc, - PREEMPT_RECORD(wptr), rb->wptr); - kgsl_regwrite(device, A5XX_CP_CONTEXT_SWITCH_RESTORE_ADDR_LO, - lower_32_bits(rb->preemption_desc.gpuaddr)); - kgsl_regwrite(device, A5XX_CP_CONTEXT_SWITCH_RESTORE_ADDR_HI, - upper_32_bits(rb->preemption_desc.gpuaddr)); - kgsl_sharedmem_readq(&rb->pagetable_desc, &ttbr0, - offsetof(struct adreno_ringbuffer_pagetable_info, ttbr0)); - kgsl_sharedmem_readl(&rb->pagetable_desc, &contextidr, - offsetof(struct adreno_ringbuffer_pagetable_info, contextidr)); - - spin_lock(&kgsl_driver.ptlock); - list_for_each_entry(pt, &kgsl_driver.pagetable_list, list) { - if (kgsl_mmu_pagetable_get_ttbr0(pt) == ttbr0) { - switch_default_pt = false; - break; - } - } - spin_unlock(&kgsl_driver.ptlock); - - if (switch_default_pt) { - ttbr0 = kgsl_mmu_pagetable_get_ttbr0( - device->mmu.defaultpagetable); - contextidr = kgsl_mmu_pagetable_get_contextidr( - device->mmu.defaultpagetable); - } - - kgsl_sharedmem_writeq(device, &iommu->smmu_info, - offsetof(struct a5xx_cp_smmu_info, ttbr0), ttbr0); - kgsl_sharedmem_writel(device, &iommu->smmu_info, - offsetof(struct a5xx_cp_smmu_info, context_idr), contextidr); -} - -/* - * a5xx_preemption_save() - Save the state after preemption is done - */ -static void a5xx_preemption_save(struct adreno_device *adreno_dev, - struct adreno_ringbuffer *rb) -{ - /* save the rptr from ctxrecord here */ - kgsl_sharedmem_readl(&rb->preemption_desc, &rb->rptr, - PREEMPT_RECORD(rptr)); -} - -#ifdef CONFIG_QCOM_KGSL_IOMMU -static int a5xx_preemption_iommu_init(struct adreno_device *adreno_dev) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct kgsl_iommu *iommu = KGSL_IOMMU_PRIV(device); - - /* Allocate mem for storing preemption smmu record */ - return kgsl_allocate_global(device, &iommu->smmu_info, PAGE_SIZE, - KGSL_MEMFLAGS_GPUREADONLY, KGSL_MEMDESC_PRIVILEGED); -} -#else -static int a5xx_preemption_iommu_init(struct adreno_device *adreno_dev) -{ - return -ENODEV; -} -#endif - -static int a5xx_preemption_init(struct adreno_device *adreno_dev) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct adreno_ringbuffer *rb; - int ret; - unsigned int i; - uint64_t addr; - - /* We are dependent on IOMMU to make preemption go on the CP side */ - if (kgsl_mmu_get_mmutype(device) != KGSL_MMU_TYPE_IOMMU) - return -ENODEV; - - /* Allocate mem for storing preemption counters */ - ret = kgsl_allocate_global(device, &adreno_dev->preemption_counters, - adreno_dev->num_ringbuffers * - A5XX_CP_CTXRECORD_PREEMPTION_COUNTER_SIZE, 0, 0); - if (ret) - return ret; - - addr = adreno_dev->preemption_counters.gpuaddr; - - /* Allocate mem for storing preemption switch record */ - FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { - ret = kgsl_allocate_global(device, - &rb->preemption_desc, A5XX_CP_CTXRECORD_SIZE_IN_BYTES, - 0, KGSL_MEMDESC_PRIVILEGED); - if (ret) - return ret; - - /* Initialize the context switch record here */ - kgsl_sharedmem_writel(device, &rb->preemption_desc, - PREEMPT_RECORD(magic), A5XX_CP_CTXRECORD_MAGIC_REF); - kgsl_sharedmem_writel(device, &rb->preemption_desc, - PREEMPT_RECORD(info), 0); - kgsl_sharedmem_writel(device, &rb->preemption_desc, - PREEMPT_RECORD(data), 0); - kgsl_sharedmem_writel(device, &rb->preemption_desc, - PREEMPT_RECORD(cntl), 0x0800000C); - kgsl_sharedmem_writel(device, &rb->preemption_desc, - PREEMPT_RECORD(rptr), 0); - kgsl_sharedmem_writel(device, &rb->preemption_desc, - PREEMPT_RECORD(wptr), 0); - kgsl_sharedmem_writeq(device, &rb->preemption_desc, - PREEMPT_RECORD(rbase), - adreno_dev->ringbuffers[i].buffer_desc.gpuaddr); - kgsl_sharedmem_writeq(device, &rb->preemption_desc, - PREEMPT_RECORD(counter), addr); - - addr += A5XX_CP_CTXRECORD_PREEMPTION_COUNTER_SIZE; - } - - return a5xx_preemption_iommu_init(adreno_dev); -} - -/* - * a5xx_preemption_token() - Preempt token on a5xx - * PM4 commands for preempt token on a5xx. These commands are - * submitted to ringbuffer to trigger preemption. - */ -static int a5xx_preemption_token(struct adreno_device *adreno_dev, - struct adreno_ringbuffer *rb, unsigned int *cmds, - uint64_t gpuaddr) -{ - unsigned int *cmds_orig = cmds; - - *cmds++ = cp_type7_packet(CP_CONTEXT_SWITCH_YIELD, 4); - cmds += cp_gpuaddr(adreno_dev, cmds, gpuaddr); - *cmds++ = 1; - /* generate interrupt on preemption completion */ - *cmds++ = 1; - - return cmds - cmds_orig; - -} - -/* - * a5xx_preemption_pre_ibsubmit() - Below PM4 commands are - * added at the beginning of every cmdbatch submission. - */ -static int a5xx_preemption_pre_ibsubmit( - struct adreno_device *adreno_dev, - struct adreno_ringbuffer *rb, unsigned int *cmds, - struct kgsl_context *context, uint64_t cond_addr, - struct kgsl_memobj_node *ib) -{ - unsigned int *cmds_orig = cmds; - uint64_t gpuaddr = rb->preemption_desc.gpuaddr; - unsigned int preempt_style = 0; - - if (context) { - /* - * Preemption from secure to unsecure needs Zap shader to be - * run to clear all secure content. CP does not know during - * preemption if it is switching between secure and unsecure - * contexts so restrict Secure contexts to be preempted at - * ringbuffer level. - */ - if (context->flags & KGSL_CONTEXT_SECURE) - preempt_style = KGSL_CONTEXT_PREEMPT_STYLE_RINGBUFFER; - else - preempt_style = ADRENO_PREEMPT_STYLE(context->flags); - } - - /* - * CP_PREEMPT_ENABLE_GLOBAL(global preemption) can only be set by KMD - * in ringbuffer. - * 1) set global preemption to 0x0 to disable global preemption. - * Only RB level preemption is allowed in this mode - * 2) Set global preemption to defer(0x2) for finegrain preemption. - * when global preemption is set to defer(0x2), - * CP_PREEMPT_ENABLE_LOCAL(local preemption) determines the - * preemption point. Local preemption - * can be enabled by both UMD(within IB) and KMD. - */ - *cmds++ = cp_type7_packet(CP_PREEMPT_ENABLE_GLOBAL, 1); - *cmds++ = ((preempt_style == KGSL_CONTEXT_PREEMPT_STYLE_FINEGRAIN) - ? 2 : 0); - - /* Turn CP protection OFF */ - *cmds++ = cp_type7_packet(CP_SET_PROTECTED_MODE, 1); - *cmds++ = 0; - - /* - * CP during context switch will save context switch info to - * a5xx_cp_preemption_record pointed by CONTEXT_SWITCH_SAVE_ADDR - */ - *cmds++ = cp_type4_packet(A5XX_CP_CONTEXT_SWITCH_SAVE_ADDR_LO, 1); - *cmds++ = lower_32_bits(gpuaddr); - *cmds++ = cp_type4_packet(A5XX_CP_CONTEXT_SWITCH_SAVE_ADDR_HI, 1); - *cmds++ = upper_32_bits(gpuaddr); - - /* Turn CP protection ON */ - *cmds++ = cp_type7_packet(CP_SET_PROTECTED_MODE, 1); - *cmds++ = 1; - - /* - * Enable local preemption for finegrain preemption in case of - * a misbehaving IB - */ - if (preempt_style == KGSL_CONTEXT_PREEMPT_STYLE_FINEGRAIN) { - *cmds++ = cp_type7_packet(CP_PREEMPT_ENABLE_LOCAL, 1); - *cmds++ = 1; - } else { - *cmds++ = cp_type7_packet(CP_PREEMPT_ENABLE_LOCAL, 1); - *cmds++ = 0; - } - - /* Enable CP_CONTEXT_SWITCH_YIELD packets in the IB2s */ - *cmds++ = cp_type7_packet(CP_YIELD_ENABLE, 1); - *cmds++ = 2; - - return cmds - cmds_orig; -} - -/* - * a5xx_preemption_yield_enable() - Below PM4 commands are - * added after every cmdbatch submission. - */ -static int a5xx_preemption_yield_enable(unsigned int *cmds) -{ - /* - * SRM -- set render mode (ex binning, direct render etc) - * SRM is set by UMD usually at start of IB to tell CP the type of - * preemption. - * KMD needs to set SRM to NULL to indicate CP that rendering is - * done by IB. - */ - *cmds++ = cp_type7_packet(CP_SET_RENDER_MODE, 5); - *cmds++ = 0; - *cmds++ = 0; - *cmds++ = 0; - *cmds++ = 0; - *cmds++ = 0; - - *cmds++ = cp_type7_packet(CP_YIELD_ENABLE, 1); - *cmds++ = 1; - - return 8; -} - -/* - * a5xx_preemption_post_ibsubmit() - Below PM4 commands are - * added after every cmdbatch submission. - */ -static int a5xx_preemption_post_ibsubmit(struct adreno_device *adreno_dev, - struct adreno_ringbuffer *rb, unsigned int *cmds, - struct kgsl_context *context) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - unsigned int ctx_id = context ? context->id : 0; - - return a5xx_preemption_token(adreno_dev, rb, cmds, - device->memstore.gpuaddr + - KGSL_MEMSTORE_OFFSET(ctx_id, preempted)); - -} - static void a5xx_platform_setup(struct adreno_device *adreno_dev) { uint64_t addr; @@ -1972,12 +1695,8 @@ out: static void a5xx_start(struct adreno_device *adreno_dev) { struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct kgsl_iommu *iommu = KGSL_IOMMU_PRIV(device); struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); - unsigned int i, bit; - struct adreno_ringbuffer *rb; - uint64_t def_ttbr0; - uint32_t contextidr; + unsigned int bit; adreno_vbif_start(adreno_dev, a5xx_vbif_platforms, ARRAY_SIZE(a5xx_vbif_platforms)); @@ -2178,58 +1897,21 @@ static void a5xx_start(struct adreno_device *adreno_dev) } - if (adreno_is_preemption_enabled(adreno_dev)) { - struct kgsl_pagetable *pt = device->mmu.defaultpagetable; - - def_ttbr0 = kgsl_mmu_pagetable_get_ttbr0(pt); - contextidr = kgsl_mmu_pagetable_get_contextidr(pt); - - /* Initialize the context switch record here */ - kgsl_sharedmem_writel(device, &iommu->smmu_info, - PREEMPT_SMMU_RECORD(magic), - A5XX_CP_SMMU_INFO_MAGIC_REF); - kgsl_sharedmem_writeq(device, &iommu->smmu_info, - PREEMPT_SMMU_RECORD(ttbr0), def_ttbr0); - /* - * The CP doesn't actually use the asid field, so - * put a bad value into it until it is removed from - * the preemption record. - */ - kgsl_sharedmem_writeq(device, &iommu->smmu_info, - PREEMPT_SMMU_RECORD(asid), - 0xdecafbad); - kgsl_sharedmem_writeq(device, &iommu->smmu_info, - PREEMPT_SMMU_RECORD(context_idr), - contextidr); - adreno_writereg64(adreno_dev, - ADRENO_REG_CP_CONTEXT_SWITCH_SMMU_INFO_LO, - ADRENO_REG_CP_CONTEXT_SWITCH_SMMU_INFO_HI, - iommu->smmu_info.gpuaddr); - - FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { - kgsl_sharedmem_writel(device, &rb->preemption_desc, - PREEMPT_RECORD(rptr), 0); - kgsl_sharedmem_writel(device, &rb->preemption_desc, - PREEMPT_RECORD(wptr), 0); - kgsl_sharedmem_writeq(device, &rb->pagetable_desc, - offsetof(struct adreno_ringbuffer_pagetable_info, - ttbr0), def_ttbr0); - } - } - + a5xx_preemption_start(adreno_dev); a5xx_protect_init(adreno_dev); } +/* + * Follow the ME_INIT sequence with a preemption yield to allow the GPU to move + * to a different ringbuffer, if desired + */ static int _preemption_init( struct adreno_device *adreno_dev, struct adreno_ringbuffer *rb, unsigned int *cmds, struct kgsl_context *context) { - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); unsigned int *cmds_orig = cmds; uint64_t gpuaddr = rb->preemption_desc.gpuaddr; - uint64_t gpuaddr_token = device->memstore.gpuaddr + - KGSL_MEMSTORE_OFFSET(0, preempted); /* Turn CP protection OFF */ *cmds++ = cp_type7_packet(CP_SET_PROTECTED_MODE, 1); @@ -2258,8 +1940,8 @@ static int _preemption_init( *cmds++ = 1; *cmds++ = cp_type7_packet(CP_CONTEXT_SWITCH_YIELD, 4); - cmds += cp_gpuaddr(adreno_dev, cmds, gpuaddr_token); - *cmds++ = 1; + cmds += cp_gpuaddr(adreno_dev, cmds, 0x0); + *cmds++ = 0; /* generate interrupt on preemption completion */ *cmds++ = 1; @@ -2297,7 +1979,7 @@ static int a5xx_post_start(struct adreno_device *adreno_dev) if (adreno_is_preemption_enabled(adreno_dev)) cmds += _preemption_init(adreno_dev, rb, cmds, NULL); - rb->wptr = rb->wptr - (42 - (cmds - start)); + rb->_wptr = rb->_wptr - (42 - (cmds - start)); ret = adreno_ringbuffer_submit_spin(rb, NULL, 2000); if (ret) @@ -2595,8 +2277,15 @@ static int a5xx_rb_start(struct adreno_device *adreno_dev, unsigned int start_type) { struct adreno_ringbuffer *rb = ADRENO_CURRENT_RINGBUFFER(adreno_dev); + struct kgsl_device *device = &adreno_dev->dev; + uint64_t addr; int ret; + addr = SCRATCH_RPTR_GPU_ADDR(device, rb->id); + + adreno_writereg64(adreno_dev, ADRENO_REG_CP_RB_RPTR_ADDR_LO, + ADRENO_REG_CP_RB_RPTR_ADDR_HI, addr); + /* * The size of the ringbuffer in the hardware is the log2 * representation of the size in quadwords (sizedwords / 2). @@ -2605,8 +2294,7 @@ static int a5xx_rb_start(struct adreno_device *adreno_dev, */ adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_CNTL, - (ilog2(KGSL_RB_DWORDS >> 1) & 0x3F) | - (1 << 27)); + A5XX_CP_RB_CNTL_DEFAULT); adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_BASE, rb->buffer_desc.gpuaddr); @@ -3147,6 +2835,10 @@ static unsigned int a5xx_register_offsets[ADRENO_REG_REGISTER_MAX] = { ADRENO_REG_DEFINE(ADRENO_REG_CP_WFI_PEND_CTR, A5XX_CP_WFI_PEND_CTR), ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_BASE, A5XX_CP_RB_BASE), ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_BASE_HI, A5XX_CP_RB_BASE_HI), + ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_RPTR_ADDR_LO, + A5XX_CP_RB_RPTR_ADDR_LO), + ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_RPTR_ADDR_HI, + A5XX_CP_RB_RPTR_ADDR_HI), ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_RPTR, A5XX_CP_RB_RPTR), ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_WPTR, A5XX_CP_RB_WPTR), ADRENO_REG_DEFINE(ADRENO_REG_CP_CNTL, A5XX_CP_CNTL), @@ -3416,6 +3108,8 @@ static void a5xx_cp_callback(struct adreno_device *adreno_dev, int bit) prev = cur; } + a5xx_preemption_trigger(adreno_dev); + kgsl_schedule_work(&device->event_work); adreno_dispatcher_schedule(device); } @@ -3500,9 +3194,6 @@ void a5x_gpc_err_int_callback(struct adreno_device *adreno_dev, int bit) (1 << A5XX_INT_RBBM_ATB_ASYNC_OVERFLOW) | \ (1 << A5XX_INT_RBBM_GPC_ERROR) | \ (1 << A5XX_INT_CP_HW_ERROR) | \ - (1 << A5XX_INT_CP_IB1) | \ - (1 << A5XX_INT_CP_IB2) | \ - (1 << A5XX_INT_CP_RB) | \ (1 << A5XX_INT_CP_CACHE_FLUSH_TS) | \ (1 << A5XX_INT_RBBM_ATB_BUS_OVERFLOW) | \ (1 << A5XX_INT_UCHE_OOB_ACCESS) | \ @@ -3525,7 +3216,7 @@ static struct adreno_irq_funcs a5xx_irq_funcs[32] = { /* 6 - RBBM_ATB_ASYNC_OVERFLOW */ ADRENO_IRQ_CALLBACK(a5xx_err_callback), ADRENO_IRQ_CALLBACK(a5x_gpc_err_int_callback), /* 7 - GPC_ERR */ - ADRENO_IRQ_CALLBACK(adreno_dispatcher_preempt_callback),/* 8 - CP_SW */ + ADRENO_IRQ_CALLBACK(a5xx_preempt_callback),/* 8 - CP_SW */ ADRENO_IRQ_CALLBACK(a5xx_cp_hw_err_callback), /* 9 - CP_HW_ERROR */ /* 10 - CP_CCU_FLUSH_DEPTH_TS */ ADRENO_IRQ_CALLBACK(NULL), @@ -3533,9 +3224,9 @@ static struct adreno_irq_funcs a5xx_irq_funcs[32] = { ADRENO_IRQ_CALLBACK(NULL), /* 12 - CP_CCU_RESOLVE_TS */ ADRENO_IRQ_CALLBACK(NULL), - ADRENO_IRQ_CALLBACK(adreno_cp_callback), /* 13 - CP_IB2_INT */ - ADRENO_IRQ_CALLBACK(adreno_cp_callback), /* 14 - CP_IB1_INT */ - ADRENO_IRQ_CALLBACK(adreno_cp_callback), /* 15 - CP_RB_INT */ + ADRENO_IRQ_CALLBACK(NULL), /* 13 - CP_IB2_INT */ + ADRENO_IRQ_CALLBACK(NULL), /* 14 - CP_IB1_INT */ + ADRENO_IRQ_CALLBACK(NULL), /* 15 - CP_RB_INT */ /* 16 - CCP_UNUSED_1 */ ADRENO_IRQ_CALLBACK(NULL), ADRENO_IRQ_CALLBACK(NULL), /* 17 - CP_RB_DONE_TS */ @@ -3772,323 +3463,6 @@ static struct adreno_coresight a5xx_coresight = { .groups = a5xx_coresight_groups, }; -/** - * a5xx_preempt_trig_state() - Schedule preemption in TRIGGERRED - * state - * @adreno_dev: Device which is in TRIGGERRED state - */ -static void a5xx_preempt_trig_state( - struct adreno_device *adreno_dev) -{ - struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - unsigned int preempt_busy; - uint64_t rbbase; - - /* - * triggered preemption, check for busy bits, if not set go to complete - * bit 0: When high indicates CP is not done with preemption. - * bit 4: When high indicates that the CP is actively switching between - * application contexts. - * Check both the bits to make sure CP is done with preemption. - */ - adreno_readreg(adreno_dev, ADRENO_REG_CP_PREEMPT, &preempt_busy); - if (!(preempt_busy & 0x11)) { - - adreno_readreg64(adreno_dev, ADRENO_REG_CP_RB_BASE, - ADRENO_REG_CP_RB_BASE_HI, &rbbase); - /* Did preemption occur, if so then change states and return */ - if (rbbase != adreno_dev->cur_rb->buffer_desc.gpuaddr) { - if (rbbase == - adreno_dev->next_rb->buffer_desc.gpuaddr) { - KGSL_DRV_INFO(device, - "Preemption completed without interrupt\n"); - trace_adreno_hw_preempt_trig_to_comp( - adreno_dev->cur_rb, - adreno_dev->next_rb); - atomic_set(&dispatcher->preemption_state, - ADRENO_DISPATCHER_PREEMPT_COMPLETE); - } else { - /* - * Something wrong with preemption. - * Set fault and reschedule dispatcher to take - * care of fault. - */ - adreno_set_gpu_fault(adreno_dev, - ADRENO_PREEMPT_FAULT); - } - adreno_dispatcher_schedule(device); - return; - } - } - - /* - * Preemption is still happening. - * Hardware not yet idle means that preemption interrupt - * may still occur, nothing to do here until interrupt signals - * completion of preemption, just return here - */ - if (!adreno_hw_isidle(adreno_dev)) - return; - - /* - * We just changed states, reschedule dispatcher to change - * preemption states - */ - if (ADRENO_DISPATCHER_PREEMPT_TRIGGERED != - atomic_read(&dispatcher->preemption_state)) { - adreno_dispatcher_schedule(device); - return; - } - - - adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); - - /* reschedule dispatcher to take care of the fault */ - adreno_dispatcher_schedule(device); -} - -/** - * a5xx_preempt_clear_state() - Schedule preemption in CLEAR - * state. Preemption can be issued in this state. - * @adreno_dev: Device which is in CLEAR state - */ -static void a5xx_preempt_clear_state( - struct adreno_device *adreno_dev) - -{ - struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct adreno_ringbuffer *highest_busy_rb; - int switch_low_to_high; - int ret; - - /* Device not awake means there is nothing to do */ - if (!kgsl_state_is_awake(device)) - return; - - /* keep updating the current rptr when preemption is clear */ - adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_RPTR, - &(adreno_dev->cur_rb->rptr)); - - highest_busy_rb = adreno_dispatcher_get_highest_busy_rb(adreno_dev); - if (!highest_busy_rb) - return; - - switch_low_to_high = adreno_compare_prio_level( - highest_busy_rb->id, adreno_dev->cur_rb->id); - - /* already current then return */ - if (!switch_low_to_high) - return; - - if (switch_low_to_high < 0) { - - if (!adreno_hw_isidle(adreno_dev)) { - adreno_dispatcher_schedule(device); - return; - } - - /* - * if switching to lower priority make sure that the rptr and - * wptr are equal, when the lower rb is not starved - */ - if (adreno_dev->cur_rb->rptr != adreno_dev->cur_rb->wptr) - return; - /* - * switch to default context because when we switch back - * to higher context then its not known which pt will - * be current, so by making it default here the next - * commands submitted will set the right pt - */ - ret = adreno_drawctxt_switch(adreno_dev, - adreno_dev->cur_rb, - NULL, 0); - /* - * lower priority RB has to wait until space opens up in - * higher RB - */ - if (ret) - return; - } - - /* rptr could be updated in drawctxt switch above, update it here */ - adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_RPTR, - &(adreno_dev->cur_rb->rptr)); - - /* turn on IOMMU as the preemption may trigger pt switch */ - kgsl_mmu_enable_clk(&device->mmu); - - /* - * setup memory to do the switch to highest priority RB - * which is not empty or may be starving away(poor thing) - */ - a5xx_preemption_start(adreno_dev, highest_busy_rb); - - atomic_set(&dispatcher->preemption_state, - ADRENO_DISPATCHER_PREEMPT_TRIGGERED); - - adreno_dev->next_rb = highest_busy_rb; - mod_timer(&dispatcher->preempt_timer, jiffies + - msecs_to_jiffies(ADRENO_DISPATCH_PREEMPT_TIMEOUT)); - - trace_adreno_hw_preempt_clear_to_trig(adreno_dev->cur_rb, - adreno_dev->next_rb); - /* issue PREEMPT trigger */ - adreno_writereg(adreno_dev, ADRENO_REG_CP_PREEMPT, 1); - - adreno_dispatcher_schedule(device); -} - -/** - * a5xx_preempt_complete_state() - Schedule preemption in - * COMPLETE state - * @adreno_dev: Device which is in COMPLETE state - */ -static void a5xx_preempt_complete_state( - struct adreno_device *adreno_dev) - -{ - struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct adreno_dispatcher_cmdqueue *dispatch_q; - uint64_t rbbase; - unsigned int wptr; - unsigned int val; - static unsigned long wait_for_preemption_complete; - - del_timer_sync(&dispatcher->preempt_timer); - - adreno_readreg(adreno_dev, ADRENO_REG_CP_PREEMPT, &val); - - if (val) { - /* - * Wait for 50ms for preemption state to be updated by CP - * before triggering hang - */ - if (wait_for_preemption_complete == 0) - wait_for_preemption_complete = jiffies + - msecs_to_jiffies(50); - if (time_after(jiffies, wait_for_preemption_complete)) { - wait_for_preemption_complete = 0; - KGSL_DRV_ERR(device, - "Invalid state after preemption CP_PREEMPT:%08x STOP:%1x BUSY:%1x\n", - val, (val & 0x1), (val & 0x10)>>4); - adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); - } - adreno_dispatcher_schedule(device); - return; - } - - wait_for_preemption_complete = 0; - adreno_readreg64(adreno_dev, ADRENO_REG_CP_RB_BASE, - ADRENO_REG_CP_RB_BASE_HI, &rbbase); - if (rbbase != adreno_dev->next_rb->buffer_desc.gpuaddr) { - KGSL_DRV_ERR(device, - "RBBASE incorrect after preemption, expected %016llx got %016llx\b", - rbbase, - adreno_dev->next_rb->buffer_desc.gpuaddr); - adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); - adreno_dispatcher_schedule(device); - return; - } - - a5xx_preemption_save(adreno_dev, adreno_dev->cur_rb); - - dispatch_q = &(adreno_dev->cur_rb->dispatch_q); - /* new RB is the current RB */ - trace_adreno_hw_preempt_comp_to_clear(adreno_dev->next_rb, - adreno_dev->cur_rb); - adreno_dev->prev_rb = adreno_dev->cur_rb; - adreno_dev->cur_rb = adreno_dev->next_rb; - adreno_dev->cur_rb->preempted_midway = 0; - adreno_dev->cur_rb->wptr_preempt_end = 0xFFFFFFFF; - adreno_dev->next_rb = NULL; - - if (adreno_disp_preempt_fair_sched) { - /* starved rb is now scheduled so unhalt dispatcher */ - if (ADRENO_DISPATCHER_RB_STARVE_TIMER_ELAPSED == - adreno_dev->cur_rb->starve_timer_state) - adreno_put_gpu_halt(adreno_dev); - adreno_dev->cur_rb->starve_timer_state = - ADRENO_DISPATCHER_RB_STARVE_TIMER_SCHEDULED; - adreno_dev->cur_rb->sched_timer = jiffies; - /* - * If the outgoing RB is has commands then set the - * busy time for it - */ - if (adreno_dev->prev_rb->rptr != adreno_dev->prev_rb->wptr) { - adreno_dev->prev_rb->starve_timer_state = - ADRENO_DISPATCHER_RB_STARVE_TIMER_INIT; - adreno_dev->prev_rb->sched_timer = jiffies; - } else { - adreno_dev->prev_rb->starve_timer_state = - ADRENO_DISPATCHER_RB_STARVE_TIMER_UNINIT; - } - } - adreno_ringbuffer_mmu_disable_clk_on_ts(device, adreno_dev->cur_rb, - adreno_dev->cur_rb->timestamp); - - atomic_set(&dispatcher->preemption_state, - ADRENO_DISPATCHER_PREEMPT_CLEAR); - - /* submit wptr if required for new rb */ - adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_WPTR, &wptr); - if (adreno_dev->cur_rb->wptr != wptr) { - kgsl_pwrscale_busy(device); - adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_WPTR, - adreno_dev->cur_rb->wptr); - } - - adreno_preempt_process_dispatch_queue(adreno_dev, dispatch_q); -} - -static void a5xx_preemption_schedule( - struct adreno_device *adreno_dev) -{ - struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct adreno_ringbuffer *rb; - int i = 0; - - if (!adreno_is_preemption_enabled(adreno_dev)) - return; - - mutex_lock(&device->mutex); - - /* - * This barrier is needed for most updated preemption_state - * to be read. - */ - smp_mb(); - - if (KGSL_STATE_ACTIVE == device->state) - FOR_EACH_RINGBUFFER(adreno_dev, rb, i) - rb->rptr = adreno_get_rptr(rb); - - switch (atomic_read(&dispatcher->preemption_state)) { - case ADRENO_DISPATCHER_PREEMPT_CLEAR: - a5xx_preempt_clear_state(adreno_dev); - break; - case ADRENO_DISPATCHER_PREEMPT_TRIGGERED: - a5xx_preempt_trig_state(adreno_dev); - /* - * if we transitioned to next state then fall-through - * processing to next state - */ - if (!adreno_preempt_state(adreno_dev, - ADRENO_DISPATCHER_PREEMPT_COMPLETE)) - break; - case ADRENO_DISPATCHER_PREEMPT_COMPLETE: - a5xx_preempt_complete_state(adreno_dev); - break; - default: - BUG(); - } - - mutex_unlock(&device->mutex); -} - struct adreno_gpudev adreno_a5xx_gpudev = { .reg_offsets = &a5xx_reg_offsets, .ft_perf_counters = a5xx_ft_perf_counters, @@ -4116,7 +3490,6 @@ struct adreno_gpudev adreno_a5xx_gpudev = { a5xx_preemption_yield_enable, .preemption_post_ibsubmit = a5xx_preemption_post_ibsubmit, - .preemption_token = a5xx_preemption_token, .preemption_init = a5xx_preemption_init, .preemption_schedule = a5xx_preemption_schedule, .enable_64bit = a5xx_enable_64bit, diff --git a/drivers/gpu/msm/adreno_a5xx.h b/drivers/gpu/msm/adreno_a5xx.h index 6ce95ff7bdbf..7965bb7b5440 100644 --- a/drivers/gpu/msm/adreno_a5xx.h +++ b/drivers/gpu/msm/adreno_a5xx.h @@ -112,6 +112,8 @@ void a5xx_crashdump_init(struct adreno_device *adreno_dev); void a5xx_hwcg_set(struct adreno_device *adreno_dev, bool on); +#define A5XX_CP_RB_CNTL_DEFAULT (((ilog2(4) << 8) & 0x1F00) | \ + (ilog2(KGSL_RB_DWORDS >> 1) & 0x3F)) /* GPMU interrupt multiplexor */ #define FW_INTR_INFO (0) #define LLM_ACK_ERR_INTR (1) @@ -232,4 +234,22 @@ static inline bool lm_on(struct adreno_device *adreno_dev) return ADRENO_FEATURE(adreno_dev, ADRENO_LM) && test_bit(ADRENO_LM_CTRL, &adreno_dev->pwrctrl_flag); } + +/* Preemption functions */ +void a5xx_preemption_trigger(struct adreno_device *adreno_dev); +void a5xx_preemption_schedule(struct adreno_device *adreno_dev); +void a5xx_preemption_start(struct adreno_device *adreno_dev); +int a5xx_preemption_init(struct adreno_device *adreno_dev); +int a5xx_preemption_yield_enable(unsigned int *cmds); + +unsigned int a5xx_preemption_post_ibsubmit(struct adreno_device *adreno_dev, + unsigned int *cmds); +unsigned int a5xx_preemption_pre_ibsubmit( + struct adreno_device *adreno_dev, + struct adreno_ringbuffer *rb, + unsigned int *cmds, struct kgsl_context *context); + + +void a5xx_preempt_callback(struct adreno_device *adreno_dev, int bit); + #endif diff --git a/drivers/gpu/msm/adreno_a5xx_preempt.c b/drivers/gpu/msm/adreno_a5xx_preempt.c new file mode 100644 index 000000000000..c1463b824c67 --- /dev/null +++ b/drivers/gpu/msm/adreno_a5xx_preempt.c @@ -0,0 +1,574 @@ +/* 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 "adreno.h" +#include "adreno_a5xx.h" +#include "a5xx_reg.h" +#include "adreno_trace.h" +#include "adreno_pm4types.h" + +#define PREEMPT_RECORD(_field) \ + offsetof(struct a5xx_cp_preemption_record, _field) + +#define PREEMPT_SMMU_RECORD(_field) \ + offsetof(struct a5xx_cp_smmu_info, _field) + +static void _update_wptr(struct adreno_device *adreno_dev) +{ + struct adreno_ringbuffer *rb = adreno_dev->cur_rb; + unsigned int wptr; + unsigned long flags; + + spin_lock_irqsave(&rb->preempt_lock, flags); + + adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_WPTR, &wptr); + + if (wptr != rb->wptr) { + adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_WPTR, + rb->wptr); + + rb->dispatch_q.expires = jiffies + + msecs_to_jiffies(adreno_cmdbatch_timeout); + } + + spin_unlock_irqrestore(&rb->preempt_lock, flags); +} + +static inline bool adreno_move_preempt_state(struct adreno_device *adreno_dev, + enum adreno_preempt_states old, enum adreno_preempt_states new) +{ + return (atomic_cmpxchg(&adreno_dev->preempt.state, old, new) == old); +} + +static void _a5xx_preemption_done(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + unsigned int status; + + /* + * In the very unlikely case that the power is off, do nothing - the + * state will be reset on power up and everybody will be happy + */ + + if (!kgsl_state_is_awake(device)) + return; + + adreno_readreg(adreno_dev, ADRENO_REG_CP_PREEMPT, &status); + + if (status != 0) { + KGSL_DRV_ERR(device, + "Preemption not complete: status=%X cur=%d R/W=%X/%X next=%d R/W=%X/%X\n", + status, adreno_dev->cur_rb->id, + adreno_get_rptr(adreno_dev->cur_rb), + adreno_dev->cur_rb->wptr, adreno_dev->next_rb->id, + adreno_get_rptr(adreno_dev->next_rb), + adreno_dev->next_rb->wptr); + + /* Set a fault and restart */ + adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); + adreno_dispatcher_schedule(device); + + return; + } + + del_timer_sync(&adreno_dev->preempt.timer); + + trace_adreno_preempt_done(adreno_dev->cur_rb, adreno_dev->next_rb); + + /* Clean up all the bits */ + adreno_dev->prev_rb = adreno_dev->cur_rb; + adreno_dev->cur_rb = adreno_dev->next_rb; + adreno_dev->next_rb = NULL; + + /* Update the wptr for the new command queue */ + _update_wptr(adreno_dev); + + /* Update the dispatcher timer for the new command queue */ + mod_timer(&adreno_dev->dispatcher.timer, + adreno_dev->cur_rb->dispatch_q.expires); + + /* Clear the preempt state */ + adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE); +} + +static void _a5xx_preemption_fault(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + unsigned int status; + + /* + * If the power is on check the preemption status one more time - if it + * was successful then just transition to the complete state + */ + if (kgsl_state_is_awake(device)) { + adreno_readreg(adreno_dev, ADRENO_REG_CP_PREEMPT, &status); + + if (status == 0) { + adreno_set_preempt_state(adreno_dev, + ADRENO_PREEMPT_COMPLETE); + + adreno_dispatcher_schedule(device); + return; + } + } + + KGSL_DRV_ERR(device, + "Preemption timed out: cur=%d R/W=%X/%X, next=%d R/W=%X/%X\n", + adreno_dev->cur_rb->id, + adreno_get_rptr(adreno_dev->cur_rb), adreno_dev->cur_rb->wptr, + adreno_dev->next_rb->id, + adreno_get_rptr(adreno_dev->next_rb), + adreno_dev->next_rb->wptr); + + adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); + adreno_dispatcher_schedule(device); +} + +static void _a5xx_preemption_worker(struct work_struct *work) +{ + struct adreno_preemption *preempt = container_of(work, + struct adreno_preemption, work); + struct adreno_device *adreno_dev = container_of(preempt, + struct adreno_device, preempt); + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + + /* Need to take the mutex to make sure that the power stays on */ + mutex_lock(&device->mutex); + + if (adreno_in_preempt_state(adreno_dev, ADRENO_PREEMPT_FAULTED)) + _a5xx_preemption_fault(adreno_dev); + + mutex_unlock(&device->mutex); +} + +static void _a5xx_preemption_timer(unsigned long data) +{ + struct adreno_device *adreno_dev = (struct adreno_device *) data; + + /* We should only be here from a triggered state */ + if (!adreno_move_preempt_state(adreno_dev, + ADRENO_PREEMPT_TRIGGERED, ADRENO_PREEMPT_FAULTED)) + return; + + /* Schedule the worker to take care of the details */ + queue_work(system_unbound_wq, &adreno_dev->preempt.work); +} + +/* Find the highest priority active ringbuffer */ +static struct adreno_ringbuffer *a5xx_next_ringbuffer( + struct adreno_device *adreno_dev) +{ + struct adreno_ringbuffer *rb; + unsigned long flags; + unsigned int i; + + FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { + bool empty; + + spin_lock_irqsave(&rb->preempt_lock, flags); + empty = adreno_rb_empty(rb); + spin_unlock_irqrestore(&rb->preempt_lock, flags); + + if (empty == false) + return rb; + } + + return NULL; +} + +void a5xx_preemption_trigger(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct kgsl_iommu *iommu = KGSL_IOMMU_PRIV(device); + struct adreno_ringbuffer *next; + uint64_t ttbr0; + unsigned int contextidr; + unsigned long flags; + + /* Put ourselves into a possible trigger state */ + if (!adreno_move_preempt_state(adreno_dev, + ADRENO_PREEMPT_NONE, ADRENO_PREEMPT_START)) + return; + + /* Get the next ringbuffer to preempt in */ + next = a5xx_next_ringbuffer(adreno_dev); + + /* + * Nothing to do if every ringbuffer is empty or if the current + * ringbuffer is the only active one + */ + if (next == NULL || next == adreno_dev->cur_rb) { + /* + * Update any critical things that might have been skipped while + * we were looking for a new ringbuffer + */ + + if (next != NULL) { + _update_wptr(adreno_dev); + + mod_timer(&adreno_dev->dispatcher.timer, + adreno_dev->cur_rb->dispatch_q.expires); + } + + adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE); + return; + } + + /* Turn off the dispatcher timer */ + del_timer(&adreno_dev->dispatcher.timer); + + /* + * This is the most critical section - we need to take care not to race + * until we have programmed the CP for the switch + */ + + spin_lock_irqsave(&next->preempt_lock, flags); + + /* Get the pagetable from the pagetable info */ + kgsl_sharedmem_readq(&next->pagetable_desc, &ttbr0, + PT_INFO_OFFSET(ttbr0)); + kgsl_sharedmem_readl(&next->pagetable_desc, &contextidr, + PT_INFO_OFFSET(contextidr)); + + kgsl_sharedmem_writel(device, &next->preemption_desc, + PREEMPT_RECORD(wptr), next->wptr); + + spin_unlock_irqrestore(&next->preempt_lock, flags); + + /* And write it to the smmu info */ + kgsl_sharedmem_writeq(device, &iommu->smmu_info, + PREEMPT_SMMU_RECORD(ttbr0), ttbr0); + kgsl_sharedmem_writel(device, &iommu->smmu_info, + PREEMPT_SMMU_RECORD(context_idr), contextidr); + + kgsl_regwrite(device, A5XX_CP_CONTEXT_SWITCH_RESTORE_ADDR_LO, + lower_32_bits(next->preemption_desc.gpuaddr)); + kgsl_regwrite(device, A5XX_CP_CONTEXT_SWITCH_RESTORE_ADDR_HI, + upper_32_bits(next->preemption_desc.gpuaddr)); + + adreno_dev->next_rb = next; + + /* Start the timer to detect a stuck preemption */ + mod_timer(&adreno_dev->preempt.timer, + jiffies + msecs_to_jiffies(ADRENO_PREEMPT_TIMEOUT)); + + trace_adreno_preempt_trigger(adreno_dev->cur_rb, adreno_dev->next_rb); + + adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_TRIGGERED); + + /* Trigger the preemption */ + adreno_writereg(adreno_dev, ADRENO_REG_CP_PREEMPT, 1); +} + +void a5xx_preempt_callback(struct adreno_device *adreno_dev, int bit) +{ + unsigned int status; + + if (!adreno_move_preempt_state(adreno_dev, + ADRENO_PREEMPT_TRIGGERED, ADRENO_PREEMPT_PENDING)) + return; + + adreno_readreg(adreno_dev, ADRENO_REG_CP_PREEMPT, &status); + + if (status != 0) { + KGSL_DRV_ERR(KGSL_DEVICE(adreno_dev), + "preempt interrupt with non-zero status: %X\n", status); + + /* + * Under the assumption that this is a race between the + * interrupt and the register, schedule the worker to clean up. + * If the status still hasn't resolved itself by the time we get + * there then we have to assume something bad happened + */ + adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_COMPLETE); + adreno_dispatcher_schedule(KGSL_DEVICE(adreno_dev)); + return; + } + + del_timer(&adreno_dev->preempt.timer); + + trace_adreno_preempt_done(adreno_dev->cur_rb, + adreno_dev->next_rb); + + adreno_dev->prev_rb = adreno_dev->cur_rb; + adreno_dev->cur_rb = adreno_dev->next_rb; + adreno_dev->next_rb = NULL; + + /* Update the wptr if it changed while preemption was ongoing */ + _update_wptr(adreno_dev); + + /* Update the dispatcher timer for the new command queue */ + mod_timer(&adreno_dev->dispatcher.timer, + adreno_dev->cur_rb->dispatch_q.expires); + + adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE); +} + +void a5xx_preemption_schedule(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + + if (!adreno_is_preemption_enabled(adreno_dev)) + return; + + mutex_lock(&device->mutex); + + if (adreno_in_preempt_state(adreno_dev, ADRENO_PREEMPT_COMPLETE)) + _a5xx_preemption_done(adreno_dev); + + a5xx_preemption_trigger(adreno_dev); + + mutex_unlock(&device->mutex); +} + +unsigned int a5xx_preemption_pre_ibsubmit( + struct adreno_device *adreno_dev, + struct adreno_ringbuffer *rb, + unsigned int *cmds, struct kgsl_context *context) +{ + unsigned int *cmds_orig = cmds; + uint64_t gpuaddr = rb->preemption_desc.gpuaddr; + unsigned int preempt_style = 0; + + if (context) { + /* + * Preemption from secure to unsecure needs Zap shader to be + * run to clear all secure content. CP does not know during + * preemption if it is switching between secure and unsecure + * contexts so restrict Secure contexts to be preempted at + * ringbuffer level. + */ + if (context->flags & KGSL_CONTEXT_SECURE) + preempt_style = KGSL_CONTEXT_PREEMPT_STYLE_RINGBUFFER; + else + preempt_style = ADRENO_PREEMPT_STYLE(context->flags); + } + + /* + * CP_PREEMPT_ENABLE_GLOBAL(global preemption) can only be set by KMD + * in ringbuffer. + * 1) set global preemption to 0x0 to disable global preemption. + * Only RB level preemption is allowed in this mode + * 2) Set global preemption to defer(0x2) for finegrain preemption. + * when global preemption is set to defer(0x2), + * CP_PREEMPT_ENABLE_LOCAL(local preemption) determines the + * preemption point. Local preemption + * can be enabled by both UMD(within IB) and KMD. + */ + *cmds++ = cp_type7_packet(CP_PREEMPT_ENABLE_GLOBAL, 1); + *cmds++ = ((preempt_style == KGSL_CONTEXT_PREEMPT_STYLE_FINEGRAIN) + ? 2 : 0); + + /* Turn CP protection OFF */ + *cmds++ = cp_type7_packet(CP_SET_PROTECTED_MODE, 1); + *cmds++ = 0; + + /* + * CP during context switch will save context switch info to + * a5xx_cp_preemption_record pointed by CONTEXT_SWITCH_SAVE_ADDR + */ + *cmds++ = cp_type4_packet(A5XX_CP_CONTEXT_SWITCH_SAVE_ADDR_LO, 1); + *cmds++ = lower_32_bits(gpuaddr); + *cmds++ = cp_type4_packet(A5XX_CP_CONTEXT_SWITCH_SAVE_ADDR_HI, 1); + *cmds++ = upper_32_bits(gpuaddr); + + /* Turn CP protection ON */ + *cmds++ = cp_type7_packet(CP_SET_PROTECTED_MODE, 1); + *cmds++ = 1; + + /* + * Enable local preemption for finegrain preemption in case of + * a misbehaving IB + */ + if (preempt_style == KGSL_CONTEXT_PREEMPT_STYLE_FINEGRAIN) { + *cmds++ = cp_type7_packet(CP_PREEMPT_ENABLE_LOCAL, 1); + *cmds++ = 1; + } else { + *cmds++ = cp_type7_packet(CP_PREEMPT_ENABLE_LOCAL, 1); + *cmds++ = 0; + } + + /* Enable CP_CONTEXT_SWITCH_YIELD packets in the IB2s */ + *cmds++ = cp_type7_packet(CP_YIELD_ENABLE, 1); + *cmds++ = 2; + + return (unsigned int) (cmds - cmds_orig); +} + +int a5xx_preemption_yield_enable(unsigned int *cmds) +{ + /* + * SRM -- set render mode (ex binning, direct render etc) + * SRM is set by UMD usually at start of IB to tell CP the type of + * preemption. + * KMD needs to set SRM to NULL to indicate CP that rendering is + * done by IB. + */ + *cmds++ = cp_type7_packet(CP_SET_RENDER_MODE, 5); + *cmds++ = 0; + *cmds++ = 0; + *cmds++ = 0; + *cmds++ = 0; + *cmds++ = 0; + + *cmds++ = cp_type7_packet(CP_YIELD_ENABLE, 1); + *cmds++ = 1; + + return 8; +} + +unsigned int a5xx_preemption_post_ibsubmit(struct adreno_device *adreno_dev, + unsigned int *cmds) +{ + int dwords = 0; + + cmds[dwords++] = cp_type7_packet(CP_CONTEXT_SWITCH_YIELD, 4); + /* Write NULL to the address to skip the data write */ + dwords += cp_gpuaddr(adreno_dev, &cmds[dwords], 0x0); + cmds[dwords++] = 1; + /* generate interrupt on preemption completion */ + cmds[dwords++] = 1; + + return dwords; +} + +void a5xx_preemption_start(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct kgsl_iommu *iommu = KGSL_IOMMU_PRIV(device); + struct adreno_ringbuffer *rb; + unsigned int i; + + if (!adreno_is_preemption_enabled(adreno_dev)) + return; + + /* Force the state to be clear */ + adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE); + + kgsl_sharedmem_writel(device, &iommu->smmu_info, + PREEMPT_SMMU_RECORD(magic), A5XX_CP_SMMU_INFO_MAGIC_REF); + kgsl_sharedmem_writeq(device, &iommu->smmu_info, + PREEMPT_SMMU_RECORD(ttbr0), MMU_DEFAULT_TTBR0(device)); + + /* The CP doesn't use the asid record, so poison it */ + kgsl_sharedmem_writel(device, &iommu->smmu_info, + PREEMPT_SMMU_RECORD(asid), 0xDECAFBAD); + kgsl_sharedmem_writel(device, &iommu->smmu_info, + PREEMPT_SMMU_RECORD(context_idr), + MMU_DEFAULT_CONTEXTIDR(device)); + + adreno_writereg64(adreno_dev, + ADRENO_REG_CP_CONTEXT_SWITCH_SMMU_INFO_LO, + ADRENO_REG_CP_CONTEXT_SWITCH_SMMU_INFO_HI, + iommu->smmu_info.gpuaddr); + + FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { + kgsl_sharedmem_writel(device, &rb->preemption_desc, + PREEMPT_RECORD(rptr), 0); + kgsl_sharedmem_writel(device, &rb->preemption_desc, + PREEMPT_RECORD(wptr), 0); + + adreno_ringbuffer_set_pagetable(rb, + device->mmu.defaultpagetable); + } + +} + +static int a5xx_preemption_ringbuffer_init(struct adreno_device *adreno_dev, + struct adreno_ringbuffer *rb, uint64_t counteraddr) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + int ret; + + ret = kgsl_allocate_global(device, &rb->preemption_desc, + A5XX_CP_CTXRECORD_SIZE_IN_BYTES, 0, KGSL_MEMDESC_PRIVILEGED); + if (ret) + return ret; + + kgsl_sharedmem_writel(device, &rb->preemption_desc, + PREEMPT_RECORD(magic), A5XX_CP_CTXRECORD_MAGIC_REF); + kgsl_sharedmem_writel(device, &rb->preemption_desc, + PREEMPT_RECORD(info), 0); + kgsl_sharedmem_writel(device, &rb->preemption_desc, + PREEMPT_RECORD(data), 0); + kgsl_sharedmem_writel(device, &rb->preemption_desc, + PREEMPT_RECORD(cntl), A5XX_CP_RB_CNTL_DEFAULT); + kgsl_sharedmem_writel(device, &rb->preemption_desc, + PREEMPT_RECORD(rptr), 0); + kgsl_sharedmem_writel(device, &rb->preemption_desc, + PREEMPT_RECORD(wptr), 0); + kgsl_sharedmem_writeq(device, &rb->preemption_desc, + PREEMPT_RECORD(rptr_addr), SCRATCH_RPTR_GPU_ADDR(device, + rb->id)); + kgsl_sharedmem_writeq(device, &rb->preemption_desc, + PREEMPT_RECORD(rbase), rb->buffer_desc.gpuaddr); + kgsl_sharedmem_writeq(device, &rb->preemption_desc, + PREEMPT_RECORD(counter), counteraddr); + + return 0; +} + +#ifdef CONFIG_QCOM_KGSL_IOMMU +static int a5xx_preemption_iommu_init(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct kgsl_iommu *iommu = KGSL_IOMMU_PRIV(device); + + /* Allocate mem for storing preemption smmu record */ + return kgsl_allocate_global(device, &iommu->smmu_info, PAGE_SIZE, + KGSL_MEMFLAGS_GPUREADONLY, KGSL_MEMDESC_PRIVILEGED); +} +#else +static int a5xx_preemption_iommu_init(struct adreno_device *adreno_dev) +{ + return -ENODEV; +} +#endif + +int a5xx_preemption_init(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct adreno_preemption *preempt = &adreno_dev->preempt; + struct adreno_ringbuffer *rb; + int ret; + unsigned int i; + uint64_t addr; + + /* We are dependent on IOMMU to make preemption go on the CP side */ + if (kgsl_mmu_get_mmutype(device) != KGSL_MMU_TYPE_IOMMU) + return -ENODEV; + + INIT_WORK(&preempt->work, _a5xx_preemption_worker); + + setup_timer(&preempt->timer, _a5xx_preemption_timer, + (unsigned long) adreno_dev); + + /* Allocate mem for storing preemption counters */ + ret = kgsl_allocate_global(device, &preempt->counters, + adreno_dev->num_ringbuffers * + A5XX_CP_CTXRECORD_PREEMPTION_COUNTER_SIZE, 0, 0); + if (ret) + return ret; + + addr = preempt->counters.gpuaddr; + + /* Allocate mem for storing preemption switch record */ + FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { + ret = a5xx_preemption_ringbuffer_init(adreno_dev, rb, addr); + if (ret) + return ret; + + addr += A5XX_CP_CTXRECORD_PREEMPTION_COUNTER_SIZE; + } + + return a5xx_preemption_iommu_init(adreno_dev); +} diff --git a/drivers/gpu/msm/adreno_debugfs.c b/drivers/gpu/msm/adreno_debugfs.c index 1a1db3ab3dc9..9cbcd06d7658 100644 --- a/drivers/gpu/msm/adreno_debugfs.c +++ b/drivers/gpu/msm/adreno_debugfs.c @@ -226,8 +226,7 @@ static void cmdbatch_print(struct seq_file *s, struct kgsl_cmdbatch *cmdbatch) if (cmdbatch->flags & KGSL_CONTEXT_SYNC) return; - seq_printf(s, "\t%d: ib: expires: %lu", - cmdbatch->timestamp, cmdbatch->expires); + seq_printf(s, "\t%d: ", cmdbatch->timestamp); seq_puts(s, " flags: "); print_flags(s, cmdbatch_flags, ARRAY_SIZE(cmdbatch_flags), diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c index 3f36a93ea110..ac3805800691 100644 --- a/drivers/gpu/msm/adreno_dispatch.c +++ b/drivers/gpu/msm/adreno_dispatch.c @@ -28,10 +28,10 @@ #define CMDQUEUE_NEXT(_i, _s) (((_i) + 1) % (_s)) /* Time in ms after which the dispatcher tries to schedule an unscheduled RB */ -static unsigned int _dispatch_starvation_time = 2000; +unsigned int adreno_dispatch_starvation_time = 2000; /* Amount of time in ms that a starved RB is permitted to execute for */ -static unsigned int _dispatch_time_slice = 25; +unsigned int adreno_dispatch_time_slice = 25; /* * If set then dispatcher tries to schedule lower priority RB's after if they @@ -78,6 +78,24 @@ unsigned int adreno_cmdbatch_timeout = 2000; /* Interval for reading and comparing fault detection registers */ static unsigned int _fault_timer_interval = 200; +#define CMDQUEUE_RB(_cmdqueue) \ + ((struct adreno_ringbuffer *) \ + container_of((_cmdqueue), struct adreno_ringbuffer, dispatch_q)) + +#define CMDQUEUE(_ringbuffer) (&(_ringbuffer)->dispatch_q) + +static int adreno_dispatch_retire_cmdqueue(struct adreno_device *adreno_dev, + struct adreno_dispatcher_cmdqueue *cmdqueue); + +static inline bool cmdqueue_is_current( + struct adreno_dispatcher_cmdqueue *cmdqueue) +{ + struct adreno_ringbuffer *rb = CMDQUEUE_RB(cmdqueue); + struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb); + + return (adreno_dev->cur_rb == rb); +} + static void _add_context(struct adreno_device *adreno_dev, struct adreno_context *drawctxt) { @@ -283,7 +301,8 @@ static void _retire_marker(struct kgsl_cmdbatch *cmdbatch) /* Retire pending GPU events for the object */ kgsl_process_event_group(device, &context->events); - trace_adreno_cmdbatch_retired(cmdbatch, -1, 0, 0, drawctxt->rb); + trace_adreno_cmdbatch_retired(cmdbatch, -1, 0, 0, drawctxt->rb, + adreno_get_rptr(drawctxt->rb)); kgsl_cmdbatch_destroy(cmdbatch); } @@ -576,8 +595,15 @@ static int sendcmd(struct adreno_device *adreno_dev, if (dispatcher->inflight == 1) { if (ret == 0) { + + /* Stop fault timer before reading fault registers */ + del_timer_sync(&dispatcher->fault_timer); + fault_detect_read(adreno_dev); + /* Start the fault timer on first submission */ + start_fault_timer(adreno_dev); + if (!test_and_set_bit(ADRENO_DISPATCHER_ACTIVE, &dispatcher->priv)) reinit_completion(&dispatcher->idle_gate); @@ -594,11 +620,15 @@ static int sendcmd(struct adreno_device *adreno_dev, dispatch_q->inflight--; /* + * Don't log a message in case of: * -ENOENT means that the context was detached before the - * command was submitted - don't log a message in that case + * command was submitted + * -ENOSPC means that there temporarily isn't any room in the + * ringbuffer + * -PROTO means that a fault is currently being worked */ - if (ret != -ENOENT) + if (ret != -ENOENT && ret != -ENOSPC && ret != -EPROTO) KGSL_DRV_ERR(device, "Unable to submit command to the ringbuffer %d\n", ret); @@ -609,7 +639,8 @@ static int sendcmd(struct adreno_device *adreno_dev, nsecs = do_div(secs, 1000000000); trace_adreno_cmdbatch_submitted(cmdbatch, (int) dispatcher->inflight, - time.ticks, (unsigned long) secs, nsecs / 1000, drawctxt->rb); + time.ticks, (unsigned long) secs, nsecs / 1000, drawctxt->rb, + adreno_get_rptr(drawctxt->rb)); cmdbatch->submit_ticks = time.ticks; @@ -618,28 +649,26 @@ static int sendcmd(struct adreno_device *adreno_dev, ADRENO_DISPATCH_CMDQUEUE_SIZE; /* - * If this is the first command in the pipe then the GPU will - * immediately start executing it so we can start the expiry timeout on - * the command batch here. Subsequent command batches will have their - * timer started when the previous command batch is retired. - * Set the timer if the cmdbatch was submitted to current - * active RB else this timer will need to be set when the - * RB becomes active, also if dispatcher is not is CLEAR - * state then the cmdbatch it is currently executing is - * unclear so do not set timer in that case either. + * For the first submission in any given command queue update the + * expected expire time - this won't actually be used / updated until + * the command queue in question goes current, but universally setting + * it here avoids the possibilty of some race conditions with preempt */ - if (1 == dispatch_q->inflight && - (&(adreno_dev->cur_rb->dispatch_q)) == dispatch_q && - adreno_preempt_state(adreno_dev, - ADRENO_DISPATCHER_PREEMPT_CLEAR)) { - cmdbatch->expires = jiffies + + + if (dispatch_q->inflight == 1) + dispatch_q->expires = jiffies + msecs_to_jiffies(adreno_cmdbatch_timeout); - mod_timer(&dispatcher->timer, cmdbatch->expires); + + /* + * If we believe ourselves to be current and preemption isn't a thing, + * then set up the timer. If this misses, then preemption is indeed a + * thing and the timer will be set up in due time + */ + if (!adreno_in_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE)) { + if (cmdqueue_is_current(dispatch_q)) + mod_timer(&dispatcher->timer, dispatch_q->expires); } - /* Start the fault detection timer on the first submission */ - if (dispatcher->inflight == 1) - start_fault_timer(adreno_dev); /* * we just submitted something, readjust ringbuffer @@ -924,87 +953,6 @@ static int get_timestamp(struct adreno_context *drawctxt, } /** - * adreno_dispatcher_preempt_timer() - Timer that triggers when preemption has - * not completed - * @data: Pointer to adreno device that did not preempt in timely manner - */ -static void adreno_dispatcher_preempt_timer(unsigned long data) -{ - struct adreno_device *adreno_dev = (struct adreno_device *) data; - struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; - - KGSL_DRV_ERR(KGSL_DEVICE(adreno_dev), - "Preemption timed out. cur_rb rptr/wptr %x/%x id %d, next_rb rptr/wptr %x/%x id %d, disp_state: %d\n", - adreno_dev->cur_rb->rptr, adreno_dev->cur_rb->wptr, - adreno_dev->cur_rb->id, adreno_dev->next_rb->rptr, - adreno_dev->next_rb->wptr, adreno_dev->next_rb->id, - atomic_read(&dispatcher->preemption_state)); - adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); - adreno_dispatcher_schedule(KGSL_DEVICE(adreno_dev)); -} - -/** - * adreno_dispatcher_get_highest_busy_rb() - Returns the highest priority RB - * which is busy - * @adreno_dev: Device whose RB is returned - */ -struct adreno_ringbuffer *adreno_dispatcher_get_highest_busy_rb( - struct adreno_device *adreno_dev) -{ - struct adreno_ringbuffer *rb, *highest_busy_rb = NULL; - int i; - - FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { - if (rb->rptr != rb->wptr && !highest_busy_rb) { - highest_busy_rb = rb; - goto done; - } - - if (!adreno_disp_preempt_fair_sched) - continue; - - switch (rb->starve_timer_state) { - case ADRENO_DISPATCHER_RB_STARVE_TIMER_UNINIT: - if (rb->rptr != rb->wptr && - adreno_dev->cur_rb != rb) { - rb->starve_timer_state = - ADRENO_DISPATCHER_RB_STARVE_TIMER_INIT; - rb->sched_timer = jiffies; - } - break; - case ADRENO_DISPATCHER_RB_STARVE_TIMER_INIT: - if (time_after(jiffies, rb->sched_timer + - msecs_to_jiffies(_dispatch_starvation_time))) { - rb->starve_timer_state = - ADRENO_DISPATCHER_RB_STARVE_TIMER_ELAPSED; - /* halt dispatcher to remove starvation */ - adreno_get_gpu_halt(adreno_dev); - } - break; - case ADRENO_DISPATCHER_RB_STARVE_TIMER_SCHEDULED: - BUG_ON(adreno_dev->cur_rb != rb); - /* - * If the RB has not been running for the minimum - * time slice then allow it to run - */ - if ((rb->rptr != rb->wptr) && time_before(jiffies, - adreno_dev->cur_rb->sched_timer + - msecs_to_jiffies(_dispatch_time_slice))) - highest_busy_rb = rb; - else - rb->starve_timer_state = - ADRENO_DISPATCHER_RB_STARVE_TIMER_UNINIT; - break; - case ADRENO_DISPATCHER_RB_STARVE_TIMER_ELAPSED: - default: - break; - } - } -done: - return highest_busy_rb; -} - -/** * adreno_dispactcher_queue_cmd() - Queue a new command in the context * @adreno_dev: Pointer to the adreno device struct * @drawctxt: Pointer to the adreno draw context @@ -1433,7 +1381,7 @@ static void adreno_fault_header(struct kgsl_device *device, if (rb != NULL) pr_fault(device, cmdbatch, "gpu fault rb %d rb sw r/w %4.4x/%4.4x\n", - rb->id, rb->rptr, rb->wptr); + rb->id, rptr, rb->wptr); } else { int id = (rb != NULL) ? rb->id : -1; @@ -1444,7 +1392,7 @@ static void adreno_fault_header(struct kgsl_device *device, if (rb != NULL) dev_err(device->dev, "RB[%d] gpu fault rb sw r/w %4.4x/%4.4x\n", - rb->id, rb->rptr, rb->wptr); + rb->id, rptr, rb->wptr); } } @@ -1751,6 +1699,27 @@ replay: kfree(replay); } +static void do_header_and_snapshot(struct kgsl_device *device, + struct adreno_ringbuffer *rb, struct kgsl_cmdbatch *cmdbatch) +{ + /* Always dump the snapshot on a non-cmdbatch failure */ + if (cmdbatch == NULL) { + adreno_fault_header(device, rb, NULL); + kgsl_device_snapshot(device, NULL); + return; + } + + /* Skip everything if the PMDUMP flag is set */ + if (test_bit(KGSL_FT_SKIP_PMDUMP, &cmdbatch->fault_policy)) + return; + + /* Print the fault header */ + adreno_fault_header(device, rb, cmdbatch); + + if (!(cmdbatch->context->flags & KGSL_CONTEXT_NO_SNAPSHOT)) + kgsl_device_snapshot(device, cmdbatch->context); +} + static int dispatcher_do_fault(struct adreno_device *adreno_dev) { struct kgsl_device *device = KGSL_DEVICE(adreno_dev); @@ -1787,7 +1756,7 @@ static int dispatcher_do_fault(struct adreno_device *adreno_dev) /* Turn off all the timers */ del_timer_sync(&dispatcher->timer); del_timer_sync(&dispatcher->fault_timer); - del_timer_sync(&dispatcher->preempt_timer); + del_timer_sync(&adreno_dev->preempt.timer); mutex_lock(&device->mutex); @@ -1813,14 +1782,12 @@ static int dispatcher_do_fault(struct adreno_device *adreno_dev) * retire cmdbatches from all the dispatch_q's before starting recovery */ FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { - adreno_dispatch_process_cmdqueue(adreno_dev, - &(rb->dispatch_q), 0); + adreno_dispatch_retire_cmdqueue(adreno_dev, + &(rb->dispatch_q)); /* Select the active dispatch_q */ if (base == rb->buffer_desc.gpuaddr) { dispatch_q = &(rb->dispatch_q); hung_rb = rb; - adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_RPTR, - &hung_rb->rptr); if (adreno_dev->cur_rb != hung_rb) { adreno_dev->prev_rb = adreno_dev->cur_rb; adreno_dev->cur_rb = hung_rb; @@ -1834,7 +1801,7 @@ static int dispatcher_do_fault(struct adreno_device *adreno_dev) } } - if (dispatch_q && (dispatch_q->tail != dispatch_q->head)) { + if (!adreno_cmdqueue_is_empty(dispatch_q)) { cmdbatch = dispatch_q->cmd_q[dispatch_q->head]; trace_adreno_cmdbatch_fault(cmdbatch, fault); } @@ -1842,17 +1809,7 @@ static int dispatcher_do_fault(struct adreno_device *adreno_dev) adreno_readreg64(adreno_dev, ADRENO_REG_CP_IB1_BASE, ADRENO_REG_CP_IB1_BASE_HI, &base); - /* - * Dump the snapshot information if this is the first - * detected fault for the oldest active command batch - */ - - if (cmdbatch == NULL || - !test_bit(KGSL_FT_SKIP_PMDUMP, &cmdbatch->fault_policy)) { - adreno_fault_header(device, hung_rb, cmdbatch); - kgsl_device_snapshot(device, - cmdbatch ? cmdbatch->context : NULL); - } + do_header_and_snapshot(device, hung_rb, cmdbatch); /* Terminate the stalled transaction and resume the IOMMU */ if (fault & ADRENO_IOMMU_PAGE_FAULT) @@ -1860,8 +1817,6 @@ static int dispatcher_do_fault(struct adreno_device *adreno_dev) /* Reset the dispatcher queue */ dispatcher->inflight = 0; - atomic_set(&dispatcher->preemption_state, - ADRENO_DISPATCHER_PREEMPT_CLEAR); /* Reset the GPU and make sure halt is not set during recovery */ halt = adreno_gpu_halt(adreno_dev); @@ -1875,12 +1830,12 @@ static int dispatcher_do_fault(struct adreno_device *adreno_dev) if (hung_rb != NULL) { kgsl_sharedmem_writel(device, &device->memstore, - KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_MAX + hung_rb->id, - soptimestamp), hung_rb->timestamp); + MEMSTORE_RB_OFFSET(hung_rb, soptimestamp), + hung_rb->timestamp); kgsl_sharedmem_writel(device, &device->memstore, - KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_MAX + hung_rb->id, - eoptimestamp), hung_rb->timestamp); + MEMSTORE_RB_OFFSET(hung_rb, eoptimestamp), + hung_rb->timestamp); /* Schedule any pending events to be run */ kgsl_process_event_group(device, &hung_rb->events); @@ -1953,139 +1908,170 @@ static void cmdbatch_profile_ticks(struct adreno_device *adreno_dev, *retire = entry->retired; } -int adreno_dispatch_process_cmdqueue(struct adreno_device *adreno_dev, - struct adreno_dispatcher_cmdqueue *dispatch_q, - int long_ib_detect) +static void retire_cmdbatch(struct adreno_device *adreno_dev, + struct kgsl_cmdbatch *cmdbatch) { - struct adreno_dispatcher *dispatcher = &(adreno_dev->dispatcher); - uint64_t start_ticks = 0, retire_ticks = 0; + struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; + struct adreno_context *drawctxt = ADRENO_CONTEXT(cmdbatch->context); + uint64_t start = 0, end = 0; - struct adreno_dispatcher_cmdqueue *active_q = - &(adreno_dev->cur_rb->dispatch_q); + if (cmdbatch->fault_recovery != 0) { + set_bit(ADRENO_CONTEXT_FAULT, &cmdbatch->context->priv); + _print_recovery(KGSL_DEVICE(adreno_dev), cmdbatch); + } + + if (test_bit(CMDBATCH_FLAG_PROFILE, &cmdbatch->priv)) + cmdbatch_profile_ticks(adreno_dev, cmdbatch, &start, &end); + + trace_adreno_cmdbatch_retired(cmdbatch, (int) dispatcher->inflight, + start, end, ADRENO_CMDBATCH_RB(cmdbatch), + adreno_get_rptr(drawctxt->rb)); + + drawctxt->submit_retire_ticks[drawctxt->ticks_index] = + end - cmdbatch->submit_ticks; + + drawctxt->ticks_index = (drawctxt->ticks_index + 1) % + SUBMIT_RETIRE_TICKS_SIZE; + + kgsl_cmdbatch_destroy(cmdbatch); +} + +static int adreno_dispatch_retire_cmdqueue(struct adreno_device *adreno_dev, + struct adreno_dispatcher_cmdqueue *cmdqueue) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; int count = 0; - while (dispatch_q->head != dispatch_q->tail) { + while (!adreno_cmdqueue_is_empty(cmdqueue)) { struct kgsl_cmdbatch *cmdbatch = - dispatch_q->cmd_q[dispatch_q->head]; - struct adreno_context *drawctxt; - BUG_ON(cmdbatch == NULL); + cmdqueue->cmd_q[cmdqueue->head]; - drawctxt = ADRENO_CONTEXT(cmdbatch->context); + if (!kgsl_check_timestamp(device, cmdbatch->context, + cmdbatch->timestamp)) + break; - /* - * First try to expire the timestamp. This happens if the - * context is valid and the timestamp expired normally or if the - * context was destroyed before the command batch was finished - * in the GPU. Either way retire the command batch advance the - * pointers and continue processing the queue - */ + retire_cmdbatch(adreno_dev, cmdbatch); - if (kgsl_check_timestamp(KGSL_DEVICE(adreno_dev), - cmdbatch->context, cmdbatch->timestamp)) { + dispatcher->inflight--; + cmdqueue->inflight--; - /* - * If the cmdbatch in question had faulted announce its - * successful completion to the world - */ + cmdqueue->cmd_q[cmdqueue->head] = NULL; - if (cmdbatch->fault_recovery != 0) { - /* Mark the context as faulted and recovered */ - set_bit(ADRENO_CONTEXT_FAULT, - &cmdbatch->context->priv); + cmdqueue->head = CMDQUEUE_NEXT(cmdqueue->head, + ADRENO_DISPATCH_CMDQUEUE_SIZE); - _print_recovery(KGSL_DEVICE(adreno_dev), - cmdbatch); - } + count++; + } - /* Reduce the number of inflight command batches */ - dispatcher->inflight--; - dispatch_q->inflight--; + return count; +} - /* - * If kernel profiling is enabled get the submit and - * retired ticks from the buffer - */ +static void _adreno_dispatch_check_timeout(struct adreno_device *adreno_dev, + struct adreno_dispatcher_cmdqueue *cmdqueue) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct kgsl_cmdbatch *cmdbatch = cmdqueue->cmd_q[cmdqueue->head]; - if (test_bit(CMDBATCH_FLAG_PROFILE, &cmdbatch->priv)) - cmdbatch_profile_ticks(adreno_dev, cmdbatch, - &start_ticks, &retire_ticks); + /* Don't timeout if the timer hasn't expired yet (duh) */ + if (time_is_after_jiffies(cmdqueue->expires)) + return; - trace_adreno_cmdbatch_retired(cmdbatch, - (int) dispatcher->inflight, start_ticks, - retire_ticks, ADRENO_CMDBATCH_RB(cmdbatch)); + /* Don't timeout if the IB timeout is disabled globally */ + if (!adreno_long_ib_detect(adreno_dev)) + return; - /* Record the delta between submit and retire ticks */ - drawctxt->submit_retire_ticks[drawctxt->ticks_index] = - retire_ticks - cmdbatch->submit_ticks; + /* Don't time out if the context has disabled it */ + if (cmdbatch->context->flags & KGSL_CONTEXT_NO_FAULT_TOLERANCE) + return; - drawctxt->ticks_index = (drawctxt->ticks_index + 1) - % SUBMIT_RETIRE_TICKS_SIZE; + pr_context(device, cmdbatch->context, "gpu timeout ctx %d ts %d\n", + cmdbatch->context->id, cmdbatch->timestamp); - /* Zero the old entry*/ - dispatch_q->cmd_q[dispatch_q->head] = NULL; + adreno_set_gpu_fault(adreno_dev, ADRENO_TIMEOUT_FAULT); +} - /* Advance the buffer head */ - dispatch_q->head = CMDQUEUE_NEXT(dispatch_q->head, - ADRENO_DISPATCH_CMDQUEUE_SIZE); +static int adreno_dispatch_process_cmdqueue(struct adreno_device *adreno_dev, + struct adreno_dispatcher_cmdqueue *cmdqueue) +{ + int count = adreno_dispatch_retire_cmdqueue(adreno_dev, cmdqueue); - /* Destroy the retired command batch */ - kgsl_cmdbatch_destroy(cmdbatch); + /* Nothing to do if there are no pending commands */ + if (adreno_cmdqueue_is_empty(cmdqueue)) + return count; - /* Update the expire time for the next command batch */ + /* Don't update the cmdqueue timeout if we are about to preempt out */ + if (!adreno_in_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE)) + return count; - if (dispatch_q->inflight > 0 && - dispatch_q == active_q) { - cmdbatch = - dispatch_q->cmd_q[dispatch_q->head]; - cmdbatch->expires = jiffies + - msecs_to_jiffies( - adreno_cmdbatch_timeout); - } + /* Don't update the cmdqueue timeout if it isn't active */ + if (!cmdqueue_is_current(cmdqueue)) + return count; - count++; - continue; - } - /* - * Break here if fault detection is disabled for the context or - * if the long running IB detection is disaled device wide or - * if the dispatch q is not active - * Long running command buffers will be allowed to run to - * completion - but badly behaving command buffers (infinite - * shaders etc) can end up running forever. - */ + /* + * If the current ringbuffer retired any commands then universally + * reset the timeout + */ - if (!long_ib_detect || - drawctxt->base.flags & KGSL_CONTEXT_NO_FAULT_TOLERANCE - || dispatch_q != active_q) - break; + if (count) { + cmdqueue->expires = jiffies + + msecs_to_jiffies(adreno_cmdbatch_timeout); + return count; + } - /* - * The last line of defense is to check if the command batch has - * timed out. If we get this far but the timeout hasn't expired - * yet then the GPU is still ticking away - */ + /* + * If we get here then 1) the ringbuffer is current and 2) we haven't + * retired anything. Check to see if the timeout if valid for the + * current cmdbatch and fault if it has expired + */ + _adreno_dispatch_check_timeout(adreno_dev, cmdqueue); + return 0; +} - if (time_is_after_jiffies(cmdbatch->expires)) - break; +/* Update the dispatcher timers */ +static void _dispatcher_update_timers(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; - /* Boom goes the dynamite */ + /* Kick the idle timer */ + mutex_lock(&device->mutex); + kgsl_pwrscale_update(device); + mod_timer(&device->idle_timer, + jiffies + device->pwrctrl.interval_timeout); + mutex_unlock(&device->mutex); - pr_context(KGSL_DEVICE(adreno_dev), cmdbatch->context, - "gpu timeout ctx %d ts %d\n", - cmdbatch->context->id, cmdbatch->timestamp); + /* Check to see if we need to update the command timer */ + if (adreno_in_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE)) { + struct adreno_dispatcher_cmdqueue *cmdqueue = + CMDQUEUE(adreno_dev->cur_rb); - adreno_set_gpu_fault(adreno_dev, ADRENO_TIMEOUT_FAULT); - break; + if (!adreno_cmdqueue_is_empty(cmdqueue)) + mod_timer(&dispatcher->timer, cmdqueue->expires); } - return count; } -/** - * adreno_dispatcher_work() - Master work handler for the dispatcher - * @work: Pointer to the work struct for the current work queue - * - * Process expired commands and send new ones. - */ +/* Take down the dispatcher and release any power states */ +static void _dispatcher_power_down(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; + + mutex_lock(&device->mutex); + + if (test_and_clear_bit(ADRENO_DISPATCHER_ACTIVE, &dispatcher->priv)) + complete_all(&dispatcher->idle_gate); + + del_timer_sync(&dispatcher->fault_timer); + + if (test_bit(ADRENO_DISPATCHER_POWER, &dispatcher->priv)) { + kgsl_active_count_put(device); + clear_bit(ADRENO_DISPATCHER_POWER, &dispatcher->priv); + } + + mutex_unlock(&device->mutex); +} + static void adreno_dispatcher_work(struct work_struct *work) { struct adreno_dispatcher *dispatcher = @@ -2095,95 +2081,50 @@ static void adreno_dispatcher_work(struct work_struct *work) struct kgsl_device *device = KGSL_DEVICE(adreno_dev); struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); int count = 0; - int cur_rb_id = adreno_dev->cur_rb->id; + unsigned int i = 0; mutex_lock(&dispatcher->mutex); - if (ADRENO_DISPATCHER_PREEMPT_CLEAR == - atomic_read(&dispatcher->preemption_state)) - /* process the active q*/ - count = adreno_dispatch_process_cmdqueue(adreno_dev, - &(adreno_dev->cur_rb->dispatch_q), - adreno_long_ib_detect(adreno_dev)); - - else if (ADRENO_DISPATCHER_PREEMPT_TRIGGERED == - atomic_read(&dispatcher->preemption_state)) - count = adreno_dispatch_process_cmdqueue(adreno_dev, - &(adreno_dev->cur_rb->dispatch_q), 0); - - /* Check if gpu fault occurred */ - if (dispatcher_do_fault(adreno_dev)) - goto done; - - if (gpudev->preemption_schedule) - gpudev->preemption_schedule(adreno_dev); - - if (cur_rb_id != adreno_dev->cur_rb->id) { - struct adreno_dispatcher_cmdqueue *dispatch_q = - &(adreno_dev->cur_rb->dispatch_q); - /* active level switched, clear new level cmdbatches */ - count = adreno_dispatch_process_cmdqueue(adreno_dev, - dispatch_q, - adreno_long_ib_detect(adreno_dev)); - /* - * If GPU has already completed all the commands in new incoming - * RB then we may not get another interrupt due to which - * dispatcher may not run again. Schedule dispatcher here so - * we can come back and process the other RB's if required - */ - if (dispatch_q->head == dispatch_q->tail) - adreno_dispatcher_schedule(device); - } /* - * If inflight went to 0, queue back up the event processor to catch - * stragglers + * As long as there are inflight commands, process retired comamnds from + * all cmdqueues */ - if (dispatcher->inflight == 0 && count) - kgsl_schedule_work(&device->event_work); - - /* Try to dispatch new commands */ - _adreno_dispatcher_issuecmds(adreno_dev); - -done: - /* Either update the timer for the next command batch or disable it */ - if (dispatcher->inflight) { - struct kgsl_cmdbatch *cmdbatch = - adreno_dev->cur_rb->dispatch_q.cmd_q[ - adreno_dev->cur_rb->dispatch_q.head]; - if (cmdbatch && adreno_preempt_state(adreno_dev, - ADRENO_DISPATCHER_PREEMPT_CLEAR)) - /* Update the timeout timer for the next cmdbatch */ - mod_timer(&dispatcher->timer, cmdbatch->expires); - - /* There are still things in flight - update the idle counts */ - mutex_lock(&device->mutex); - kgsl_pwrscale_update(device); - mod_timer(&device->idle_timer, jiffies + - device->pwrctrl.interval_timeout); - mutex_unlock(&device->mutex); - } else { - /* There is nothing left in the pipeline. Shut 'er down boys */ - mutex_lock(&device->mutex); + for (i = 0; i < adreno_dev->num_ringbuffers; i++) { + struct adreno_dispatcher_cmdqueue *cmdqueue = + CMDQUEUE(&adreno_dev->ringbuffers[i]); - if (test_and_clear_bit(ADRENO_DISPATCHER_ACTIVE, - &dispatcher->priv)) - complete_all(&dispatcher->idle_gate); + count += adreno_dispatch_process_cmdqueue(adreno_dev, + cmdqueue); + if (dispatcher->inflight == 0) + break; + } - /* - * Stop the fault timer before decrementing the active count to - * avoid reading the hardware registers while we are trying to - * turn clocks off - */ - del_timer_sync(&dispatcher->fault_timer); + /* + * dispatcher_do_fault() returns 0 if no faults occurred. If that is the + * case, then clean up preemption and try to schedule more work + */ + if (dispatcher_do_fault(adreno_dev) == 0) { + /* Clean up after preemption */ + if (gpudev->preemption_schedule) + gpudev->preemption_schedule(adreno_dev); - if (test_bit(ADRENO_DISPATCHER_POWER, &dispatcher->priv)) { - kgsl_active_count_put(device); - clear_bit(ADRENO_DISPATCHER_POWER, &dispatcher->priv); - } + /* Re-kick the event engine to catch stragglers */ + if (dispatcher->inflight == 0 && count != 0) + kgsl_schedule_work(&device->event_work); - mutex_unlock(&device->mutex); + /* Run the scheduler for to dispatch new commands */ + _adreno_dispatcher_issuecmds(adreno_dev); } + /* + * If there are commands pending, update the timers, otherwise release + * the power state to prepare for power down + */ + if (dispatcher->inflight > 0) + _dispatcher_update_timers(adreno_dev); + else + _dispatcher_power_down(adreno_dev); + mutex_unlock(&dispatcher->mutex); } @@ -2305,7 +2246,7 @@ void adreno_dispatcher_close(struct adreno_device *adreno_dev) FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { struct adreno_dispatcher_cmdqueue *dispatch_q = &(rb->dispatch_q); - while (dispatch_q->head != dispatch_q->tail) { + while (!adreno_cmdqueue_is_empty(dispatch_q)) { kgsl_cmdbatch_destroy( dispatch_q->cmd_q[dispatch_q->head]); dispatch_q->head = (dispatch_q->head + 1) @@ -2395,9 +2336,9 @@ static DISPATCHER_UINT_ATTR(fault_throttle_burst, 0644, 0, static DISPATCHER_UINT_ATTR(disp_preempt_fair_sched, 0644, 0, adreno_disp_preempt_fair_sched); static DISPATCHER_UINT_ATTR(dispatch_time_slice, 0644, 0, - _dispatch_time_slice); + adreno_dispatch_time_slice); static DISPATCHER_UINT_ATTR(dispatch_starvation_time, 0644, 0, - _dispatch_starvation_time); + adreno_dispatch_starvation_time); static struct attribute *dispatcher_attrs[] = { &dispatcher_attr_inflight.attr, @@ -2474,9 +2415,6 @@ int adreno_dispatcher_init(struct adreno_device *adreno_dev) setup_timer(&dispatcher->fault_timer, adreno_dispatcher_fault_timer, (unsigned long) adreno_dev); - setup_timer(&dispatcher->preempt_timer, adreno_dispatcher_preempt_timer, - (unsigned long) adreno_dev); - INIT_WORK(&dispatcher->work, adreno_dispatcher_work); init_completion(&dispatcher->idle_gate); @@ -2485,9 +2423,6 @@ int adreno_dispatcher_init(struct adreno_device *adreno_dev) plist_head_init(&dispatcher->pending); spin_lock_init(&dispatcher->plist_lock); - atomic_set(&dispatcher->preemption_state, - ADRENO_DISPATCHER_PREEMPT_CLEAR); - ret = kobject_init_and_add(&dispatcher->kobj, &ktype_dispatcher, &device->dev->kobj, "dispatch"); @@ -2544,49 +2479,3 @@ int adreno_dispatcher_idle(struct adreno_device *adreno_dev) adreno_dispatcher_schedule(device); return ret; } - -void adreno_preempt_process_dispatch_queue(struct adreno_device *adreno_dev, - struct adreno_dispatcher_cmdqueue *dispatch_q) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct kgsl_cmdbatch *cmdbatch; - - if (dispatch_q->head != dispatch_q->tail) { - /* - * retire cmdbacthes from previous q, and don't check for - * timeout since the cmdbatch may have been preempted - */ - adreno_dispatch_process_cmdqueue(adreno_dev, - dispatch_q, 0); - } - - /* set the timer for the first cmdbatch of active dispatch_q */ - dispatch_q = &(adreno_dev->cur_rb->dispatch_q); - if (dispatch_q->head != dispatch_q->tail) { - cmdbatch = dispatch_q->cmd_q[dispatch_q->head]; - cmdbatch->expires = jiffies + - msecs_to_jiffies(adreno_cmdbatch_timeout); - } - kgsl_schedule_work(&device->event_work); -} - -/** - * adreno_dispatcher_preempt_callback() - Callback funcion for CP_SW interrupt - * @adreno_dev: The device on which the interrupt occurred - * @bit: Interrupt bit in the interrupt status register - */ -void adreno_dispatcher_preempt_callback(struct adreno_device *adreno_dev, - int bit) -{ - struct adreno_dispatcher *dispatcher = &(adreno_dev->dispatcher); - - if (ADRENO_DISPATCHER_PREEMPT_TRIGGERED != - atomic_read(&dispatcher->preemption_state)) - return; - - trace_adreno_hw_preempt_trig_to_comp_int(adreno_dev->cur_rb, - adreno_dev->next_rb); - atomic_set(&dispatcher->preemption_state, - ADRENO_DISPATCHER_PREEMPT_COMPLETE); - adreno_dispatcher_schedule(KGSL_DEVICE(adreno_dev)); -} diff --git a/drivers/gpu/msm/adreno_dispatch.h b/drivers/gpu/msm/adreno_dispatch.h index 308d5b936819..699c3e4adb27 100644 --- a/drivers/gpu/msm/adreno_dispatch.h +++ b/drivers/gpu/msm/adreno_dispatch.h @@ -11,29 +11,13 @@ * */ - #ifndef ____ADRENO_DISPATCHER_H #define ____ADRENO_DISPATCHER_H -/* Time to allow preemption to complete (in ms) */ -#define ADRENO_DISPATCH_PREEMPT_TIMEOUT 10000 - extern unsigned int adreno_disp_preempt_fair_sched; extern unsigned int adreno_cmdbatch_timeout; - -/** - * enum adreno_dispatcher_preempt_states - States of dispatcher for ringbuffer - * preemption - * @ADRENO_DISPATCHER_PREEMPT_CLEAR: No preemption is underway, - * only 1 preemption can be underway at any point - * @ADRENO_DISPATCHER_PREEMPT_TRIGGERED: A preemption is underway - * @ADRENO_DISPATCHER_PREEMPT_COMPLETE: A preemption has just completed - */ -enum adreno_dispatcher_preempt_states { - ADRENO_DISPATCHER_PREEMPT_CLEAR = 0, - ADRENO_DISPATCHER_PREEMPT_TRIGGERED, - ADRENO_DISPATCHER_PREEMPT_COMPLETE, -}; +extern unsigned int adreno_dispatch_starvation_time; +extern unsigned int adreno_dispatch_time_slice; /** * enum adreno_dispatcher_starve_timer_states - Starvation control states of @@ -71,6 +55,7 @@ enum adreno_dispatcher_starve_timer_states { * @head: Head pointer to the q * @tail: Queues tail pointer * @active_context_count: Number of active contexts seen in this rb cmdqueue + * @expires: The jiffies value at which this cmdqueue has run too long */ struct adreno_dispatcher_cmdqueue { struct kgsl_cmdbatch *cmd_q[ADRENO_DISPATCH_CMDQUEUE_SIZE]; @@ -78,6 +63,7 @@ struct adreno_dispatcher_cmdqueue { unsigned int head; unsigned int tail; int active_context_count; + unsigned long expires; }; /** @@ -92,11 +78,6 @@ struct adreno_dispatcher_cmdqueue { * @work: work_struct to put the dispatcher in a work queue * @kobj: kobject for the dispatcher directory in the device sysfs node * @idle_gate: Gate to wait on for dispatcher to idle - * @preemption_state: Indicated what state the dispatcher is in, states are - * defined by enum adreno_dispatcher_preempt_states - * @preempt_token_submit: Indicates if a preempt token has been subnitted in - * current ringbuffer. - * @preempt_timer: Timer to track if preemption occured within specified time * @disp_preempt_fair_sched: If set then dispatcher will try to be fair to * starving RB's by scheduling them in and enforcing a minimum time slice * for every RB that is scheduled to run on the device @@ -113,9 +94,6 @@ struct adreno_dispatcher { struct work_struct work; struct kobject kobj; struct completion idle_gate; - atomic_t preemption_state; - int preempt_token_submit; - struct timer_list preempt_timer; unsigned int disp_preempt_fair_sched; }; @@ -141,12 +119,12 @@ void adreno_dispatcher_queue_context(struct kgsl_device *device, struct adreno_context *drawctxt); void adreno_dispatcher_preempt_callback(struct adreno_device *adreno_dev, int bit); -struct adreno_ringbuffer *adreno_dispatcher_get_highest_busy_rb( - struct adreno_device *adreno_dev); -int adreno_dispatch_process_cmdqueue(struct adreno_device *adreno_dev, - struct adreno_dispatcher_cmdqueue *dispatch_q, - int long_ib_detect); void adreno_preempt_process_dispatch_queue(struct adreno_device *adreno_dev, struct adreno_dispatcher_cmdqueue *dispatch_q); +static inline bool adreno_cmdqueue_is_empty( + struct adreno_dispatcher_cmdqueue *cmdqueue) +{ + return (cmdqueue != NULL && cmdqueue->head == cmdqueue->tail); +} #endif /* __ADRENO_DISPATCHER_H */ diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c index d8498d938b6a..fb95f6108fb8 100644 --- a/drivers/gpu/msm/adreno_drawctxt.c +++ b/drivers/gpu/msm/adreno_drawctxt.c @@ -346,7 +346,8 @@ adreno_drawctxt_create(struct kgsl_device_private *dev_priv, KGSL_CONTEXT_PWR_CONSTRAINT | KGSL_CONTEXT_IFH_NOP | KGSL_CONTEXT_SECURE | - KGSL_CONTEXT_PREEMPT_STYLE_MASK); + KGSL_CONTEXT_PREEMPT_STYLE_MASK | + KGSL_CONTEXT_NO_SNAPSHOT); /* Check for errors before trying to initialize */ @@ -466,20 +467,6 @@ void adreno_drawctxt_detach(struct kgsl_context *context) list_del_init(&drawctxt->active_node); spin_unlock(&adreno_dev->active_list_lock); - /* deactivate context */ - mutex_lock(&device->mutex); - if (rb->drawctxt_active == drawctxt) { - if (adreno_dev->cur_rb == rb) { - if (!kgsl_active_count_get(device)) { - adreno_drawctxt_switch(adreno_dev, rb, NULL, 0); - kgsl_active_count_put(device); - } else - BUG(); - } else - adreno_drawctxt_switch(adreno_dev, rb, NULL, 0); - } - mutex_unlock(&device->mutex); - spin_lock(&drawctxt->lock); count = drawctxt_detach_cmdbatches(drawctxt, list); spin_unlock(&drawctxt->lock); @@ -548,12 +535,21 @@ void adreno_drawctxt_destroy(struct kgsl_context *context) kfree(drawctxt); } +static void _drawctxt_switch_wait_callback(struct kgsl_device *device, + struct kgsl_event_group *group, + void *priv, int result) +{ + struct adreno_context *drawctxt = (struct adreno_context *) priv; + + kgsl_context_put(&drawctxt->base); +} + /** * adreno_drawctxt_switch - switch the current draw context in a given RB * @adreno_dev - The 3D device that owns the context * @rb: The ringubffer pointer on which the current context is being changed * @drawctxt - the 3D context to switch to - * @flags - Flags to accompany the switch (from user space) + * @flags: Control flags for the switch * * Switch the current draw context in given RB */ @@ -583,8 +579,7 @@ int adreno_drawctxt_switch(struct adreno_device *adreno_dev, if (drawctxt != NULL && kgsl_context_detached(&drawctxt->base)) return -ENOENT; - trace_adreno_drawctxt_switch(rb, - drawctxt, flags); + trace_adreno_drawctxt_switch(rb, drawctxt); /* Get a refcount to the new instance */ if (drawctxt) { @@ -596,16 +591,18 @@ int adreno_drawctxt_switch(struct adreno_device *adreno_dev, /* No context - set the default pagetable and thats it. */ new_pt = device->mmu.defaultpagetable; } - ret = adreno_ringbuffer_set_pt_ctx(rb, new_pt, drawctxt); - if (ret) { - KGSL_DRV_ERR(device, - "Failed to set pagetable on rb %d\n", rb->id); + ret = adreno_ringbuffer_set_pt_ctx(rb, new_pt, drawctxt, flags); + if (ret) return ret; - } - /* Put the old instance of the active drawctxt */ - if (rb->drawctxt_active) - kgsl_context_put(&rb->drawctxt_active->base); + if (rb->drawctxt_active) { + /* Wait for the timestamp to expire */ + if (kgsl_add_event(device, &rb->events, rb->timestamp, + _drawctxt_switch_wait_callback, + rb->drawctxt_active)) { + kgsl_context_put(&rb->drawctxt_active->base); + } + } rb->drawctxt_active = drawctxt; return 0; diff --git a/drivers/gpu/msm/adreno_drawctxt.h b/drivers/gpu/msm/adreno_drawctxt.h index 7e80247e9322..5ea911954991 100644 --- a/drivers/gpu/msm/adreno_drawctxt.h +++ b/drivers/gpu/msm/adreno_drawctxt.h @@ -104,6 +104,9 @@ enum adreno_context_priv { ADRENO_CONTEXT_SKIP_CMD, }; +/* Flags for adreno_drawctxt_switch() */ +#define ADRENO_CONTEXT_SWITCH_FORCE_GPU BIT(0) + struct kgsl_context *adreno_drawctxt_create(struct kgsl_device_private *, uint32_t *flags); diff --git a/drivers/gpu/msm/adreno_ioctl.c b/drivers/gpu/msm/adreno_ioctl.c index 519087a77b83..0d5e3e094c36 100644 --- a/drivers/gpu/msm/adreno_ioctl.c +++ b/drivers/gpu/msm/adreno_ioctl.c @@ -103,7 +103,7 @@ static long adreno_ioctl_preemption_counters_query( levels_to_copy = gpudev->num_prio_levels; if (copy_to_user((void __user *) (uintptr_t) read->counters, - adreno_dev->preemption_counters.hostptr, + adreno_dev->preempt.counters.hostptr, levels_to_copy * size_level)) return -EFAULT; diff --git a/drivers/gpu/msm/adreno_iommu.c b/drivers/gpu/msm/adreno_iommu.c index 2eeda01b3c4d..aa00dcb84185 100644 --- a/drivers/gpu/msm/adreno_iommu.c +++ b/drivers/gpu/msm/adreno_iommu.c @@ -275,6 +275,7 @@ static bool _ctx_switch_use_cpu_path( struct adreno_ringbuffer *rb) { struct kgsl_mmu *mmu = KGSL_MMU(adreno_dev); + /* * If rb is current, we can use cpu path when GPU is * idle and we are switching to default pt. @@ -284,7 +285,7 @@ static bool _ctx_switch_use_cpu_path( if (adreno_dev->cur_rb == rb) return adreno_isidle(KGSL_DEVICE(adreno_dev)) && (new_pt == mmu->defaultpagetable); - else if ((rb->wptr == rb->rptr) && + else if (adreno_rb_empty(rb) && (new_pt == mmu->defaultpagetable)) return true; @@ -360,8 +361,7 @@ static unsigned int _adreno_mmu_set_pt_update_condition( */ *cmds++ = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 2, 1); cmds += cp_gpuaddr(adreno_dev, cmds, (rb->pagetable_desc.gpuaddr + - offsetof(struct adreno_ringbuffer_pagetable_info, - switch_pt_enable))); + PT_INFO_OFFSET(switch_pt_enable))); *cmds++ = 1; *cmds++ = cp_packet(adreno_dev, CP_WAIT_MEM_WRITES, 1); *cmds++ = 0; @@ -375,14 +375,11 @@ static unsigned int _adreno_mmu_set_pt_update_condition( *cmds++ = (1 << 8) | (1 << 4) | 3; cmds += cp_gpuaddr(adreno_dev, cmds, (adreno_dev->ringbuffers[0].pagetable_desc.gpuaddr + - offsetof(struct adreno_ringbuffer_pagetable_info, - current_global_ptname))); + PT_INFO_OFFSET(current_global_ptname))); *cmds++ = ptname; *cmds++ = 0xFFFFFFFF; - cmds += cp_gpuaddr(adreno_dev, cmds, - (rb->pagetable_desc.gpuaddr + - offsetof(struct adreno_ringbuffer_pagetable_info, - switch_pt_enable))); + cmds += cp_gpuaddr(adreno_dev, cmds, (rb->pagetable_desc.gpuaddr + + PT_INFO_OFFSET(switch_pt_enable))); *cmds++ = 0; *cmds++ = cp_packet(adreno_dev, CP_WAIT_MEM_WRITES, 1); *cmds++ = 0; @@ -406,23 +403,18 @@ static unsigned int _adreno_iommu_pt_update_pid_to_mem( unsigned int *cmds_orig = cmds; *cmds++ = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 2, 1); - cmds += cp_gpuaddr(adreno_dev, cmds, - (rb->pagetable_desc.gpuaddr + - offsetof(struct adreno_ringbuffer_pagetable_info, - current_rb_ptname))); + cmds += cp_gpuaddr(adreno_dev, cmds, (rb->pagetable_desc.gpuaddr + + PT_INFO_OFFSET(current_rb_ptname))); *cmds++ = ptname; *cmds++ = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 2, 1); cmds += cp_gpuaddr(adreno_dev, cmds, - (adreno_dev->ringbuffers[0].pagetable_desc.gpuaddr + - offsetof(struct adreno_ringbuffer_pagetable_info, - current_global_ptname))); + (adreno_dev->ringbuffers[0].pagetable_desc.gpuaddr + + PT_INFO_OFFSET(current_global_ptname))); *cmds++ = ptname; /* pagetable switch done, Housekeeping: set the switch_pt_enable to 0 */ *cmds++ = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 2, 1); - cmds += cp_gpuaddr(adreno_dev, cmds, - (rb->pagetable_desc.gpuaddr + - offsetof(struct adreno_ringbuffer_pagetable_info, - switch_pt_enable))); + cmds += cp_gpuaddr(adreno_dev, cmds, (rb->pagetable_desc.gpuaddr + + PT_INFO_OFFSET(switch_pt_enable))); *cmds++ = 0; *cmds++ = cp_packet(adreno_dev, CP_WAIT_MEM_WRITES, 1); *cmds++ = 0; @@ -444,14 +436,10 @@ static unsigned int _adreno_iommu_set_pt_v1(struct adreno_ringbuffer *rb, /* set flag that indicates whether pt switch is required*/ cmds += _adreno_mmu_set_pt_update_condition(rb, cmds, ptname); *cmds++ = cp_mem_packet(adreno_dev, CP_COND_EXEC, 4, 2); - cmds += cp_gpuaddr(adreno_dev, cmds, - (rb->pagetable_desc.gpuaddr + - offsetof(struct adreno_ringbuffer_pagetable_info, - switch_pt_enable))); - cmds += cp_gpuaddr(adreno_dev, cmds, - (rb->pagetable_desc.gpuaddr + - offsetof(struct adreno_ringbuffer_pagetable_info, - switch_pt_enable))); + cmds += cp_gpuaddr(adreno_dev, cmds, (rb->pagetable_desc.gpuaddr + + PT_INFO_OFFSET(switch_pt_enable))); + cmds += cp_gpuaddr(adreno_dev, cmds, (rb->pagetable_desc.gpuaddr + + PT_INFO_OFFSET(switch_pt_enable))); *cmds++ = 1; /* Exec count to be filled later */ cond_exec_ptr = cmds; @@ -566,7 +554,7 @@ static unsigned int _adreno_iommu_set_pt_v2_a5xx(struct kgsl_device *device, *cmds++ = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 4, 1); cmds += cp_gpuaddr(adreno_dev, cmds, (rb->pagetable_desc.gpuaddr + - offsetof(struct adreno_ringbuffer_pagetable_info, ttbr0))); + PT_INFO_OFFSET(ttbr0))); *cmds++ = lower_32_bits(ttbr0); *cmds++ = upper_32_bits(ttbr0); *cmds++ = contextidr; @@ -651,14 +639,14 @@ static unsigned int __add_curr_ctxt_cmds(struct adreno_ringbuffer *rb, *cmds++ = KGSL_CONTEXT_TO_MEM_IDENTIFIER; *cmds++ = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 2, 1); - cmds += cp_gpuaddr(adreno_dev, cmds, device->memstore.gpuaddr + - KGSL_MEMSTORE_RB_OFFSET(rb, current_context)); + cmds += cp_gpuaddr(adreno_dev, cmds, + MEMSTORE_RB_GPU_ADDR(device, rb, current_context)); *cmds++ = (drawctxt ? drawctxt->base.id : 0); *cmds++ = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 2, 1); - cmds += cp_gpuaddr(adreno_dev, cmds, device->memstore.gpuaddr + - KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, - current_context)); + cmds += cp_gpuaddr(adreno_dev, cmds, + MEMSTORE_ID_GPU_ADDR(device, + KGSL_MEMSTORE_GLOBAL, current_context)); *cmds++ = (drawctxt ? drawctxt->base.id : 0); /* Invalidate UCHE for new context */ @@ -706,7 +694,7 @@ static void _set_ctxt_cpu(struct adreno_ringbuffer *rb, } /* Update rb memstore with current context */ kgsl_sharedmem_writel(device, &device->memstore, - KGSL_MEMSTORE_RB_OFFSET(rb, current_context), + MEMSTORE_RB_OFFSET(rb, current_context), drawctxt ? drawctxt->base.id : 0); } @@ -746,26 +734,11 @@ static int _set_pagetable_cpu(struct adreno_ringbuffer *rb, if (result) return result; /* write the new pt set to memory var */ - kgsl_sharedmem_writel(device, - &adreno_dev->ringbuffers[0].pagetable_desc, - offsetof( - struct adreno_ringbuffer_pagetable_info, - current_global_ptname), new_pt->name); + adreno_ringbuffer_set_global(adreno_dev, new_pt->name); } /* Update the RB pagetable info here */ - kgsl_sharedmem_writel(device, &rb->pagetable_desc, - offsetof( - struct adreno_ringbuffer_pagetable_info, - current_rb_ptname), new_pt->name); - kgsl_sharedmem_writeq(device, &rb->pagetable_desc, - offsetof( - struct adreno_ringbuffer_pagetable_info, - ttbr0), kgsl_mmu_pagetable_get_ttbr0(new_pt)); - kgsl_sharedmem_writel(device, &rb->pagetable_desc, - offsetof( - struct adreno_ringbuffer_pagetable_info, - contextidr), kgsl_mmu_pagetable_get_contextidr(new_pt)); + adreno_ringbuffer_set_pagetable(rb, new_pt); return 0; } @@ -795,8 +768,6 @@ static int _set_pagetable_gpu(struct adreno_ringbuffer *rb, return 0; } - kgsl_mmu_enable_clk(KGSL_MMU(adreno_dev)); - cmds += adreno_iommu_set_pt_generate_cmds(rb, cmds, new_pt); if ((unsigned int) (cmds - link) > (PAGE_SIZE / sizeof(unsigned int))) { @@ -812,16 +783,6 @@ static int _set_pagetable_gpu(struct adreno_ringbuffer *rb, KGSL_CMD_FLAGS_PMODE, link, (unsigned int)(cmds - link)); - /* - * On error disable the IOMMU clock right away otherwise turn it off - * after the command has been retired - */ - if (result) - kgsl_mmu_disable_clk(KGSL_MMU(adreno_dev)); - else - adreno_ringbuffer_mmu_disable_clk_on_ts(KGSL_DEVICE(adreno_dev), - rb, rb->timestamp); - kfree(link); return result; } @@ -886,7 +847,8 @@ int adreno_iommu_init(struct adreno_device *adreno_dev) */ int adreno_iommu_set_pt_ctx(struct adreno_ringbuffer *rb, struct kgsl_pagetable *new_pt, - struct adreno_context *drawctxt) + struct adreno_context *drawctxt, + unsigned long flags) { struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb); struct kgsl_device *device = KGSL_DEVICE(adreno_dev); @@ -897,7 +859,8 @@ int adreno_iommu_set_pt_ctx(struct adreno_ringbuffer *rb, if (rb->drawctxt_active) cur_pt = rb->drawctxt_active->base.proc_priv->pagetable; - cpu_path = _ctx_switch_use_cpu_path(adreno_dev, new_pt, rb); + cpu_path = !(flags & ADRENO_CONTEXT_SWITCH_FORCE_GPU) && + _ctx_switch_use_cpu_path(adreno_dev, new_pt, rb); /* Pagetable switch */ if (new_pt != cur_pt) { @@ -907,10 +870,8 @@ int adreno_iommu_set_pt_ctx(struct adreno_ringbuffer *rb, result = _set_pagetable_gpu(rb, new_pt); } - if (result) { - KGSL_DRV_ERR(device, "Error switching pagetable %d\n", result); + if (result) return result; - } /* Context switch */ if (cpu_path) @@ -918,8 +879,5 @@ int adreno_iommu_set_pt_ctx(struct adreno_ringbuffer *rb, else result = _set_ctxt_gpu(rb, drawctxt); - if (result) - KGSL_DRV_ERR(device, "Error switching context %d\n", result); - return result; } diff --git a/drivers/gpu/msm/adreno_iommu.h b/drivers/gpu/msm/adreno_iommu.h index c557c65bb4c9..5a6c2c549370 100644 --- a/drivers/gpu/msm/adreno_iommu.h +++ b/drivers/gpu/msm/adreno_iommu.h @@ -17,7 +17,8 @@ #ifdef CONFIG_QCOM_KGSL_IOMMU int adreno_iommu_set_pt_ctx(struct adreno_ringbuffer *rb, struct kgsl_pagetable *new_pt, - struct adreno_context *drawctxt); + struct adreno_context *drawctxt, + unsigned long flags); int adreno_iommu_init(struct adreno_device *adreno_dev); @@ -33,7 +34,8 @@ static inline int adreno_iommu_init(struct adreno_device *adreno_dev) static inline int adreno_iommu_set_pt_ctx(struct adreno_ringbuffer *rb, struct kgsl_pagetable *new_pt, - struct adreno_context *drawctxt) + struct adreno_context *drawctxt, + unsigned long flags) { return 0; } diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c index dceb8fb93461..0160939e97f9 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.c +++ b/drivers/gpu/msm/adreno_ringbuffer.c @@ -30,8 +30,6 @@ #include "a3xx_reg.h" #include "adreno_a5xx.h" -#define GSL_RB_NOP_SIZEDWORDS 2 - #define RB_HOSTPTR(_rb, _pos) \ ((unsigned int *) ((_rb)->buffer_desc.hostptr + \ ((_pos) * sizeof(unsigned int)))) @@ -50,86 +48,89 @@ static void _cff_write_ringbuffer(struct adreno_ringbuffer *rb) if (device->cff_dump_enable == 0) return; - /* - * This code is predicated on the fact that we write a full block of - * stuff without wrapping - */ - BUG_ON(rb->wptr < rb->last_wptr); - - size = (rb->wptr - rb->last_wptr) * sizeof(unsigned int); + size = (rb->_wptr - rb->last_wptr) * sizeof(unsigned int); hostptr = RB_HOSTPTR(rb, rb->last_wptr); gpuaddr = RB_GPUADDR(rb, rb->last_wptr); kgsl_cffdump_memcpy(device, gpuaddr, hostptr, size); + rb->last_wptr = rb->_wptr; } -void adreno_ringbuffer_submit(struct adreno_ringbuffer *rb, +static void adreno_get_submit_time(struct adreno_device *adreno_dev, struct adreno_submit_time *time) { - struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb); - BUG_ON(rb->wptr == 0); - - /* Write the changes to CFF if so enabled */ - _cff_write_ringbuffer(rb); - + unsigned long flags; /* - * Read the current GPU ticks and wallclock for most accurate - * profiling + * Here we are attempting to create a mapping between the + * GPU time domain (alwayson counter) and the CPU time domain + * (local_clock) by sampling both values as close together as + * possible. This is useful for many types of debugging and + * profiling. In order to make this mapping as accurate as + * possible, we must turn off interrupts to avoid running + * interrupt handlers between the two samples. */ - if (time != NULL) { - /* - * Here we are attempting to create a mapping between the - * GPU time domain (alwayson counter) and the CPU time domain - * (local_clock) by sampling both values as close together as - * possible. This is useful for many types of debugging and - * profiling. In order to make this mapping as accurate as - * possible, we must turn off interrupts to avoid running - * interrupt handlers between the two samples. - */ - unsigned long flags; - local_irq_save(flags); + local_irq_save(flags); - /* Read always on registers */ - if (!adreno_is_a3xx(adreno_dev)) { - adreno_readreg64(adreno_dev, - ADRENO_REG_RBBM_ALWAYSON_COUNTER_LO, - ADRENO_REG_RBBM_ALWAYSON_COUNTER_HI, - &time->ticks); + /* Read always on registers */ + if (!adreno_is_a3xx(adreno_dev)) { + adreno_readreg64(adreno_dev, + ADRENO_REG_RBBM_ALWAYSON_COUNTER_LO, + ADRENO_REG_RBBM_ALWAYSON_COUNTER_HI, + &time->ticks); - /* - * Mask hi bits as they may be incorrect on - * a4x and some a5x - */ - if (ADRENO_GPUREV(adreno_dev) >= 400 && + /* Mask hi bits as they may be incorrect on some targets */ + if (ADRENO_GPUREV(adreno_dev) >= 400 && ADRENO_GPUREV(adreno_dev) <= ADRENO_REV_A530) - time->ticks &= 0xFFFFFFFF; - } - else - time->ticks = 0; + time->ticks &= 0xFFFFFFFF; + } else + time->ticks = 0; - /* Get the kernel clock for time since boot */ - time->ktime = local_clock(); + /* Get the kernel clock for time since boot */ + time->ktime = local_clock(); - /* Get the timeofday for the wall time (for the user) */ - getnstimeofday(&time->utime); + /* Get the timeofday for the wall time (for the user) */ + getnstimeofday(&time->utime); - local_irq_restore(flags); - } + local_irq_restore(flags); +} + +void adreno_ringbuffer_wptr(struct adreno_device *adreno_dev, + struct adreno_ringbuffer *rb) +{ + unsigned long flags; - /* Memory barrier before informing the hardware of new commands */ - mb(); + spin_lock_irqsave(&rb->preempt_lock, flags); + if (adreno_in_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE)) { - if (adreno_preempt_state(adreno_dev, ADRENO_DISPATCHER_PREEMPT_CLEAR) && - (adreno_dev->cur_rb == rb)) { - /* - * Let the pwrscale policy know that new commands have - * been submitted. - */ - kgsl_pwrscale_busy(KGSL_DEVICE(adreno_dev)); - adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_WPTR, rb->wptr); + if (adreno_dev->cur_rb == rb) { + /* + * Let the pwrscale policy know that new commands have + * been submitted. + */ + kgsl_pwrscale_busy(KGSL_DEVICE(adreno_dev)); + adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_WPTR, + rb->_wptr); + } } + + rb->wptr = rb->_wptr; + spin_unlock_irqrestore(&rb->preempt_lock, flags); +} + +void adreno_ringbuffer_submit(struct adreno_ringbuffer *rb, + struct adreno_submit_time *time) +{ + struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb); + + /* Write the changes to CFF if so enabled */ + _cff_write_ringbuffer(rb); + + if (time != NULL) + adreno_get_submit_time(adreno_dev, time); + + adreno_ringbuffer_wptr(adreno_dev, rb); } int adreno_ringbuffer_submit_spin(struct adreno_ringbuffer *rb, @@ -141,125 +142,36 @@ int adreno_ringbuffer_submit_spin(struct adreno_ringbuffer *rb, return adreno_spin_idle(adreno_dev, timeout); } -static int -adreno_ringbuffer_waitspace(struct adreno_ringbuffer *rb, - unsigned int numcmds, int wptr_ahead) +unsigned int *adreno_ringbuffer_allocspace(struct adreno_ringbuffer *rb, + unsigned int dwords) { - int nopcount = 0; - unsigned int freecmds; - unsigned int wptr = rb->wptr; - unsigned int *cmds = NULL; - uint64_t gpuaddr; - unsigned long wait_time; - unsigned long wait_timeout = msecs_to_jiffies(ADRENO_IDLE_TIMEOUT); - unsigned int rptr; struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb); + unsigned int rptr = adreno_get_rptr(rb); + unsigned int ret; - /* if wptr ahead, fill the remaining with NOPs */ - if (wptr_ahead) { - /* -1 for header */ - nopcount = KGSL_RB_DWORDS - rb->wptr - 1; - - cmds = RB_HOSTPTR(rb, rb->wptr); - gpuaddr = RB_GPUADDR(rb, rb->wptr); - - rptr = adreno_get_rptr(rb); - /* For non current rb we don't expect the rptr to move */ - if ((adreno_dev->cur_rb != rb || - !adreno_preempt_state(adreno_dev, - ADRENO_DISPATCHER_PREEMPT_CLEAR)) && - !rptr) - return -ENOSPC; - - /* Make sure that rptr is not 0 before submitting - * commands at the end of ringbuffer. We do not - * want the rptr and wptr to become equal when - * the ringbuffer is not empty */ - wait_time = jiffies + wait_timeout; - while (!rptr) { - rptr = adreno_get_rptr(rb); - if (time_after(jiffies, wait_time)) - return -ETIMEDOUT; - } - - rb->wptr = 0; - } - - rptr = adreno_get_rptr(rb); - freecmds = rptr - rb->wptr; - if (freecmds == 0 || freecmds > numcmds) - goto done; + if (rptr <= rb->_wptr) { + unsigned int *cmds; - /* non current rptr will not advance anyway or if preemption underway */ - if (adreno_dev->cur_rb != rb || - !adreno_preempt_state(adreno_dev, - ADRENO_DISPATCHER_PREEMPT_CLEAR)) { - rb->wptr = wptr; - return -ENOSPC; - } - - wait_time = jiffies + wait_timeout; - /* wait for space in ringbuffer */ - while (1) { - rptr = adreno_get_rptr(rb); - - freecmds = rptr - rb->wptr; - - if (freecmds == 0 || freecmds > numcmds) - break; - - if (time_after(jiffies, wait_time)) { - KGSL_DRV_ERR(KGSL_DEVICE(adreno_dev), - "Timed out waiting for freespace in RB rptr: 0x%x, wptr: 0x%x, rb id %d\n", - rptr, wptr, rb->id); - return -ETIMEDOUT; + if (rb->_wptr + dwords <= (KGSL_RB_DWORDS - 2)) { + ret = rb->_wptr; + rb->_wptr = (rb->_wptr + dwords) % KGSL_RB_DWORDS; + return RB_HOSTPTR(rb, ret); } - } -done: - if (wptr_ahead) { - *cmds = cp_packet(adreno_dev, CP_NOP, nopcount); - kgsl_cffdump_write(KGSL_DEVICE(adreno_dev), gpuaddr, *cmds); - } - return 0; -} + cmds = RB_HOSTPTR(rb, rb->_wptr); + *cmds = cp_packet(adreno_dev, CP_NOP, + KGSL_RB_DWORDS - rb->_wptr - 1); -unsigned int *adreno_ringbuffer_allocspace(struct adreno_ringbuffer *rb, - unsigned int numcmds) -{ - unsigned int *ptr = NULL; - int ret = 0; - unsigned int rptr; - BUG_ON(numcmds >= KGSL_RB_DWORDS); - - rptr = adreno_get_rptr(rb); - /* check for available space */ - if (rb->wptr >= rptr) { - /* wptr ahead or equal to rptr */ - /* reserve dwords for nop packet */ - if ((rb->wptr + numcmds) > (KGSL_RB_DWORDS - - GSL_RB_NOP_SIZEDWORDS)) - ret = adreno_ringbuffer_waitspace(rb, numcmds, 1); - } else { - /* wptr behind rptr */ - if ((rb->wptr + numcmds) >= rptr) - ret = adreno_ringbuffer_waitspace(rb, numcmds, 0); - /* check for remaining space */ - /* reserve dwords for nop packet */ - if (!ret && (rb->wptr + numcmds) > (KGSL_RB_DWORDS - - GSL_RB_NOP_SIZEDWORDS)) - ret = adreno_ringbuffer_waitspace(rb, numcmds, 1); + rb->_wptr = 0; } - if (!ret) { - rb->last_wptr = rb->wptr; - - ptr = (unsigned int *)rb->buffer_desc.hostptr + rb->wptr; - rb->wptr += numcmds; - } else - ptr = ERR_PTR(ret); + if (rb->_wptr + dwords < rptr) { + ret = rb->_wptr; + rb->_wptr = (rb->_wptr + dwords) % KGSL_RB_DWORDS; + return RB_HOSTPTR(rb, ret); + } - return ptr; + return ERR_PTR(-ENOSPC); } /** @@ -279,8 +191,10 @@ int adreno_ringbuffer_start(struct adreno_device *adreno_dev, FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { kgsl_sharedmem_set(device, &(rb->buffer_desc), 0, 0xAA, KGSL_RB_SIZE); + kgsl_sharedmem_writel(device, &device->scratch, + SCRATCH_RPTR_OFFSET(rb->id), 0); rb->wptr = 0; - rb->rptr = 0; + rb->_wptr = 0; rb->wptr_preempt_end = 0xFFFFFFFF; rb->starve_timer_state = ADRENO_DISPATCHER_RB_STARVE_TIMER_UNINIT; @@ -322,6 +236,8 @@ static int _adreno_ringbuffer_probe(struct adreno_device *adreno_dev, rb->timestamp = 0; init_waitqueue_head(&rb->ts_expire_waitq); + spin_lock_init(&rb->preempt_lock); + /* * Allocate mem for storing RB pagetables and commands to * switch pagetable @@ -433,6 +349,18 @@ int cp_secure_mode(struct adreno_device *adreno_dev, uint *cmds, return cmds - start; } +static inline int cp_mem_write(struct adreno_device *adreno_dev, + unsigned int *cmds, uint64_t gpuaddr, unsigned int value) +{ + int dwords = 0; + + cmds[dwords++] = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 2, 1); + dwords += cp_gpuaddr(adreno_dev, &cmds[dwords], gpuaddr); + cmds[dwords++] = value; + + return dwords; +} + static int adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, unsigned int flags, unsigned int *cmds, @@ -446,18 +374,20 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, unsigned int total_sizedwords = sizedwords; unsigned int i; unsigned int context_id = 0; - uint64_t gpuaddr = device->memstore.gpuaddr; bool profile_ready; struct adreno_context *drawctxt = rb->drawctxt_active; struct kgsl_context *context = NULL; bool secured_ctxt = false; - uint64_t cond_addr; static unsigned int _seq_cnt; if (drawctxt != NULL && kgsl_context_detached(&drawctxt->base) && !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) return -ENOENT; + /* On fault return error so that we don't keep submitting */ + if (adreno_gpu_fault(adreno_dev) != 0) + return -EPROTO; + rb->timestamp++; /* If this is a internal IB, use the global timestamp for it */ @@ -529,7 +459,7 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, * required in ringbuffer and adjust the write pointer depending on * gpucore at the end of this function. */ - total_sizedwords += 4; /* sop timestamp */ + total_sizedwords += 8; /* sop timestamp */ total_sizedwords += 5; /* eop timestamp */ if (drawctxt && !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) { @@ -564,14 +494,9 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, *ringcmds++ = KGSL_CMD_IDENTIFIER; if (adreno_is_preemption_enabled(adreno_dev) && - gpudev->preemption_pre_ibsubmit) { - cond_addr = device->memstore.gpuaddr + - KGSL_MEMSTORE_OFFSET(context_id, - preempted); + gpudev->preemption_pre_ibsubmit) ringcmds += gpudev->preemption_pre_ibsubmit( - adreno_dev, rb, ringcmds, context, - cond_addr, NULL); - } + adreno_dev, rb, ringcmds, context); if (flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE) { *ringcmds++ = cp_packet(adreno_dev, CP_NOP, 1); @@ -601,16 +526,15 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, adreno_profile_preib_processing(adreno_dev, drawctxt, &flags, &ringcmds); - /* start-of-pipeline timestamp */ - *ringcmds++ = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 2, 1); + /* start-of-pipeline timestamp for the context */ if (drawctxt && !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) - ringcmds += cp_gpuaddr(adreno_dev, ringcmds, - gpuaddr + KGSL_MEMSTORE_OFFSET(context_id, - soptimestamp)); - else - ringcmds += cp_gpuaddr(adreno_dev, ringcmds, - gpuaddr + KGSL_MEMSTORE_RB_OFFSET(rb, soptimestamp)); - *ringcmds++ = timestamp; + ringcmds += cp_mem_write(adreno_dev, ringcmds, + MEMSTORE_ID_GPU_ADDR(device, context_id, soptimestamp), + timestamp); + + /* start-of-pipeline timestamp for the ringbuffer */ + ringcmds += cp_mem_write(adreno_dev, ringcmds, + MEMSTORE_RB_GPU_ADDR(device, rb, soptimestamp), rb->timestamp); if (secured_ctxt) ringcmds += cp_secure_mode(adreno_dev, ringcmds, 1); @@ -659,11 +583,9 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, * early detection of timestamp interrupt storms to stave * off system collapse. */ - *ringcmds++ = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 2, 1); - ringcmds += cp_gpuaddr(adreno_dev, ringcmds, gpuaddr + - KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, - ref_wait_ts)); - *ringcmds++ = ++_seq_cnt; + ringcmds += cp_mem_write(adreno_dev, ringcmds, + MEMSTORE_ID_GPU_ADDR(device, KGSL_MEMSTORE_GLOBAL, + ref_wait_ts), ++_seq_cnt); /* * end-of-pipeline timestamp. If per context timestamps is not @@ -677,16 +599,17 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, *ringcmds++ = CACHE_FLUSH_TS; if (drawctxt && !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) { - ringcmds += cp_gpuaddr(adreno_dev, ringcmds, gpuaddr + - KGSL_MEMSTORE_OFFSET(context_id, eoptimestamp)); + ringcmds += cp_gpuaddr(adreno_dev, ringcmds, + MEMSTORE_ID_GPU_ADDR(device, context_id, eoptimestamp)); *ringcmds++ = timestamp; - *ringcmds++ = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 2, 1); - ringcmds += cp_gpuaddr(adreno_dev, ringcmds, gpuaddr + - KGSL_MEMSTORE_RB_OFFSET(rb, eoptimestamp)); - *ringcmds++ = rb->timestamp; + + /* Write the end of pipeline timestamp to the ringbuffer too */ + ringcmds += cp_mem_write(adreno_dev, ringcmds, + MEMSTORE_RB_GPU_ADDR(device, rb, eoptimestamp), + rb->timestamp); } else { - ringcmds += cp_gpuaddr(adreno_dev, ringcmds, gpuaddr + - KGSL_MEMSTORE_RB_OFFSET(rb, eoptimestamp)); + ringcmds += cp_gpuaddr(adreno_dev, ringcmds, + MEMSTORE_RB_GPU_ADDR(device, rb, eoptimestamp)); *ringcmds++ = timestamp; } @@ -707,8 +630,8 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, if (gpudev->preemption_post_ibsubmit && adreno_is_preemption_enabled(adreno_dev)) - ringcmds += gpudev->preemption_post_ibsubmit(adreno_dev, rb, - ringcmds, &drawctxt->base); + ringcmds += gpudev->preemption_post_ibsubmit(adreno_dev, + ringcmds); /* * If we have more ringbuffer commands than space reserved @@ -722,7 +645,7 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, * required. If we have commands less than the space reserved in RB * adjust the wptr accordingly. */ - rb->wptr = rb->wptr - (total_sizedwords - (ringcmds - start)); + rb->_wptr = rb->_wptr - (total_sizedwords - (ringcmds - start)); adreno_ringbuffer_submit(rb, time); @@ -1063,14 +986,24 @@ int adreno_ringbuffer_submitcmd(struct adreno_device *adreno_dev, *cmds++ = cp_packet(adreno_dev, CP_NOP, 1); *cmds++ = KGSL_END_OF_IB_IDENTIFIER; - ret = adreno_drawctxt_switch(adreno_dev, rb, drawctxt, cmdbatch->flags); + /* Context switches commands should *always* be on the GPU */ + ret = adreno_drawctxt_switch(adreno_dev, rb, drawctxt, + ADRENO_CONTEXT_SWITCH_FORCE_GPU); /* * In the unlikely event of an error in the drawctxt switch, * treat it like a hang */ - if (ret) + if (ret) { + /* + * It is "normal" to get a -ENOSPC or a -ENOENT. Don't log it, + * the upper layers know how to handle it + */ + if (ret != -ENOSPC && ret != -ENOENT) + KGSL_DRV_ERR(device, + "Unable to switch draw context: %d\n", ret); goto done; + } if (test_bit(CMDBATCH_FLAG_WFI, &cmdbatch->priv)) flags = KGSL_CMD_FLAGS_WFI; @@ -1138,44 +1071,6 @@ done: } /** - * adreno_ringbuffer_mmu_clk_disable_event() - Callback function that - * disables the MMU clocks. - * @device: Device pointer - * @context: The ringbuffer context pointer - * @data: Pointer containing the adreno_mmu_disable_clk_param structure - * @type: The event call type (RETIRED or CANCELLED) - */ -static void adreno_ringbuffer_mmu_clk_disable_event(struct kgsl_device *device, - struct kgsl_event_group *group, void *data, int type) -{ - kgsl_mmu_disable_clk(&device->mmu); -} - -/* - * adreno_ringbuffer_mmu_disable_clk_on_ts() - Sets up event to disable MMU - * clocks - * @device - The kgsl device pointer - * @rb: The ringbuffer in whose event list the event is added - * @timestamp: The timestamp on which the event should trigger - * - * Creates an event to disable the MMU clocks on timestamp and if event - * already exists then updates the timestamp of disabling the MMU clocks - * with the passed in ts if it is greater than the current value at which - * the clocks will be disabled - * Return - void - */ -void -adreno_ringbuffer_mmu_disable_clk_on_ts(struct kgsl_device *device, - struct adreno_ringbuffer *rb, unsigned int timestamp) -{ - if (kgsl_add_event(device, &(rb->events), timestamp, - adreno_ringbuffer_mmu_clk_disable_event, NULL)) { - KGSL_DRV_ERR(device, - "Failed to add IOMMU disable clk event\n"); - } -} - -/** * adreno_ringbuffer_wait_callback() - Callback function for event registered * on a ringbuffer timestamp * @device: Device for which the the callback is valid diff --git a/drivers/gpu/msm/adreno_ringbuffer.h b/drivers/gpu/msm/adreno_ringbuffer.h index f1980fd92961..b126f710b5e6 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.h +++ b/drivers/gpu/msm/adreno_ringbuffer.h @@ -73,13 +73,16 @@ struct adreno_ringbuffer_pagetable_info { unsigned int contextidr; }; +#define PT_INFO_OFFSET(_field) \ + offsetof(struct adreno_ringbuffer_pagetable_info, _field) + /** * struct adreno_ringbuffer - Definition for an adreno ringbuffer object * @flags: Internal control flags for the ringbuffer - * @buffer_desc: Pointer to the ringbuffer memory descriptor - * @wptr: Local copy of the wptr offset - * @rptr: Read pointer offset in dwords from baseaddr - * @last_wptr: offset of the last H/W committed wptr + * @buffer_desc: Pointer to the ringbuffer memory descripto + * @_wptr: The next value of wptr to be written to the hardware on submit + * @wptr: Local copy of the wptr offset last written to hardware + * @last_wptr: offset of the last wptr that was written to CFF * @rb_ctx: The context that represents a ringbuffer * @id: Priority level of the ringbuffer, also used as an ID * @fault_detect_ts: The last retired global timestamp read during fault detect @@ -101,12 +104,13 @@ struct adreno_ringbuffer_pagetable_info { * @sched_timer: Timer that tracks how long RB has been waiting to be scheduled * or how long it has been scheduled for after preempting in * @starve_timer_state: Indicates the state of the wait. + * @preempt_lock: Lock to protect the wptr pointer while it is being updated */ struct adreno_ringbuffer { uint32_t flags; struct kgsl_memdesc buffer_desc; + unsigned int _wptr; unsigned int wptr; - unsigned int rptr; unsigned int last_wptr; int id; unsigned int fault_detect_ts; @@ -122,14 +126,12 @@ struct adreno_ringbuffer { int preempted_midway; unsigned long sched_timer; enum adreno_dispatcher_starve_timer_states starve_timer_state; + spinlock_t preempt_lock; }; /* Returns the current ringbuffer */ #define ADRENO_CURRENT_RINGBUFFER(a) ((a)->cur_rb) -#define KGSL_MEMSTORE_RB_OFFSET(rb, field) \ - KGSL_MEMSTORE_OFFSET((rb->id + KGSL_MEMSTORE_MAX), field) - int cp_secure_mode(struct adreno_device *adreno_dev, uint *cmds, int set); int adreno_ringbuffer_issueibcmds(struct kgsl_device_private *dev_priv, @@ -170,9 +172,6 @@ void adreno_ringbuffer_read_pfp_ucode(struct kgsl_device *device); void adreno_ringbuffer_read_pm4_ucode(struct kgsl_device *device); -void adreno_ringbuffer_mmu_disable_clk_on_ts(struct kgsl_device *device, - struct adreno_ringbuffer *rb, unsigned int ts); - int adreno_ringbuffer_waittimestamp(struct adreno_ringbuffer *rb, unsigned int timestamp, unsigned int msecs); @@ -204,9 +203,10 @@ static inline unsigned int adreno_ringbuffer_dec_wrapped(unsigned int val, } static inline int adreno_ringbuffer_set_pt_ctx(struct adreno_ringbuffer *rb, - struct kgsl_pagetable *pt, struct adreno_context *context) + struct kgsl_pagetable *pt, struct adreno_context *context, + unsigned long flags) { - return adreno_iommu_set_pt_ctx(rb, pt, context); + return adreno_iommu_set_pt_ctx(rb, pt, context, flags); } #endif /* __ADRENO_RINGBUFFER_H */ diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c index ca61d36a1384..b069b16c75ef 100644 --- a/drivers/gpu/msm/adreno_snapshot.c +++ b/drivers/gpu/msm/adreno_snapshot.c @@ -467,7 +467,7 @@ static size_t snapshot_rb(struct kgsl_device *device, u8 *buf, header->start = 0; header->end = KGSL_RB_DWORDS; header->wptr = rb->wptr; - header->rptr = rb->rptr; + header->rptr = adreno_get_rptr(rb); header->rbsize = KGSL_RB_DWORDS; header->count = KGSL_RB_DWORDS; adreno_rb_readtimestamp(adreno_dev, rb, KGSL_TIMESTAMP_QUEUED, @@ -741,8 +741,7 @@ static size_t snapshot_global(struct kgsl_device *device, u8 *buf, header->size = memdesc->size >> 2; header->gpuaddr = memdesc->gpuaddr; - header->ptbase = - kgsl_mmu_pagetable_get_ttbr0(device->mmu.defaultpagetable); + header->ptbase = MMU_DEFAULT_TTBR0(device); header->type = SNAPSHOT_GPU_OBJECT_GLOBAL; memcpy(ptr, memdesc->hostptr, memdesc->size); diff --git a/drivers/gpu/msm/adreno_trace.h b/drivers/gpu/msm/adreno_trace.h index 5f1bbb9a83b3..f52ddfa894d5 100644 --- a/drivers/gpu/msm/adreno_trace.h +++ b/drivers/gpu/msm/adreno_trace.h @@ -55,8 +55,8 @@ TRACE_EVENT(adreno_cmdbatch_queued, TRACE_EVENT(adreno_cmdbatch_submitted, TP_PROTO(struct kgsl_cmdbatch *cmdbatch, int inflight, uint64_t ticks, unsigned long secs, unsigned long usecs, - struct adreno_ringbuffer *rb), - TP_ARGS(cmdbatch, inflight, ticks, secs, usecs, rb), + struct adreno_ringbuffer *rb, unsigned int rptr), + TP_ARGS(cmdbatch, inflight, ticks, secs, usecs, rb, rptr), TP_STRUCT__entry( __field(unsigned int, id) __field(unsigned int, timestamp) @@ -81,7 +81,7 @@ TRACE_EVENT(adreno_cmdbatch_submitted, __entry->usecs = usecs; __entry->prio = cmdbatch->context->priority; __entry->rb_id = rb->id; - __entry->rptr = rb->rptr; + __entry->rptr = rptr; __entry->wptr = rb->wptr; __entry->q_inflight = rb->dispatch_q.inflight; ), @@ -100,8 +100,8 @@ TRACE_EVENT(adreno_cmdbatch_submitted, TRACE_EVENT(adreno_cmdbatch_retired, TP_PROTO(struct kgsl_cmdbatch *cmdbatch, int inflight, uint64_t start, uint64_t retire, - struct adreno_ringbuffer *rb), - TP_ARGS(cmdbatch, inflight, start, retire, rb), + struct adreno_ringbuffer *rb, unsigned int rptr), + TP_ARGS(cmdbatch, inflight, start, retire, rb, rptr), TP_STRUCT__entry( __field(unsigned int, id) __field(unsigned int, timestamp) @@ -126,7 +126,7 @@ TRACE_EVENT(adreno_cmdbatch_retired, __entry->retire = retire; __entry->prio = cmdbatch->context->priority; __entry->rb_id = rb->id; - __entry->rptr = rb->rptr; + __entry->rptr = rptr; __entry->wptr = rb->wptr; __entry->q_inflight = rb->dispatch_q.inflight; ), @@ -267,9 +267,8 @@ TRACE_EVENT(adreno_drawctxt_wait_done, TRACE_EVENT(adreno_drawctxt_switch, TP_PROTO(struct adreno_ringbuffer *rb, - struct adreno_context *newctx, - unsigned int flags), - TP_ARGS(rb, newctx, flags), + struct adreno_context *newctx), + TP_ARGS(rb, newctx), TP_STRUCT__entry( __field(int, rb_level) __field(unsigned int, oldctx) @@ -283,8 +282,8 @@ TRACE_EVENT(adreno_drawctxt_switch, __entry->newctx = newctx ? newctx->base.id : 0; ), TP_printk( - "rb level=%d oldctx=%u newctx=%u flags=%X", - __entry->rb_level, __entry->oldctx, __entry->newctx, flags + "rb level=%d oldctx=%u newctx=%u", + __entry->rb_level, __entry->oldctx, __entry->newctx ) ); @@ -427,8 +426,9 @@ TRACE_EVENT(kgsl_a5xx_irq_status, DECLARE_EVENT_CLASS(adreno_hw_preempt_template, TP_PROTO(struct adreno_ringbuffer *cur_rb, - struct adreno_ringbuffer *new_rb), - TP_ARGS(cur_rb, new_rb), + struct adreno_ringbuffer *new_rb, + unsigned int cur_rptr, unsigned int new_rptr), + TP_ARGS(cur_rb, new_rb, cur_rptr, new_rptr), TP_STRUCT__entry(__field(int, cur_level) __field(int, new_level) __field(unsigned int, cur_rptr) @@ -440,8 +440,8 @@ DECLARE_EVENT_CLASS(adreno_hw_preempt_template, ), TP_fast_assign(__entry->cur_level = cur_rb->id; __entry->new_level = new_rb->id; - __entry->cur_rptr = cur_rb->rptr; - __entry->new_rptr = new_rb->rptr; + __entry->cur_rptr = cur_rptr; + __entry->new_rptr = new_rptr; __entry->cur_wptr = cur_rb->wptr; __entry->new_wptr = new_rb->wptr; __entry->cur_rbbase = cur_rb->buffer_desc.gpuaddr; @@ -458,26 +458,30 @@ DECLARE_EVENT_CLASS(adreno_hw_preempt_template, DEFINE_EVENT(adreno_hw_preempt_template, adreno_hw_preempt_clear_to_trig, TP_PROTO(struct adreno_ringbuffer *cur_rb, - struct adreno_ringbuffer *new_rb), - TP_ARGS(cur_rb, new_rb) + struct adreno_ringbuffer *new_rb, + unsigned int cur_rptr, unsigned int new_rptr), + TP_ARGS(cur_rb, new_rb, cur_rptr, new_rptr) ); DEFINE_EVENT(adreno_hw_preempt_template, adreno_hw_preempt_trig_to_comp, TP_PROTO(struct adreno_ringbuffer *cur_rb, - struct adreno_ringbuffer *new_rb), - TP_ARGS(cur_rb, new_rb) + struct adreno_ringbuffer *new_rb, + unsigned int cur_rptr, unsigned int new_rptr), + TP_ARGS(cur_rb, new_rb, cur_rptr, new_rptr) ); DEFINE_EVENT(adreno_hw_preempt_template, adreno_hw_preempt_trig_to_comp_int, TP_PROTO(struct adreno_ringbuffer *cur_rb, - struct adreno_ringbuffer *new_rb), - TP_ARGS(cur_rb, new_rb) + struct adreno_ringbuffer *new_rb, + unsigned int cur_rptr, unsigned int new_rptr), + TP_ARGS(cur_rb, new_rb, cur_rptr, new_rptr) ); TRACE_EVENT(adreno_hw_preempt_comp_to_clear, TP_PROTO(struct adreno_ringbuffer *cur_rb, - struct adreno_ringbuffer *new_rb), - TP_ARGS(cur_rb, new_rb), + struct adreno_ringbuffer *new_rb, + unsigned int cur_rptr, unsigned int new_rptr), + TP_ARGS(cur_rb, new_rb, cur_rptr, new_rptr), TP_STRUCT__entry(__field(int, cur_level) __field(int, new_level) __field(unsigned int, cur_rptr) @@ -490,8 +494,8 @@ TRACE_EVENT(adreno_hw_preempt_comp_to_clear, ), TP_fast_assign(__entry->cur_level = cur_rb->id; __entry->new_level = new_rb->id; - __entry->cur_rptr = cur_rb->rptr; - __entry->new_rptr = new_rb->rptr; + __entry->cur_rptr = cur_rptr; + __entry->new_rptr = new_rptr; __entry->cur_wptr = cur_rb->wptr; __entry->new_wptr_end = new_rb->wptr_preempt_end; __entry->new_wptr = new_rb->wptr; @@ -509,8 +513,9 @@ TRACE_EVENT(adreno_hw_preempt_comp_to_clear, TRACE_EVENT(adreno_hw_preempt_token_submit, TP_PROTO(struct adreno_ringbuffer *cur_rb, - struct adreno_ringbuffer *new_rb), - TP_ARGS(cur_rb, new_rb), + struct adreno_ringbuffer *new_rb, + unsigned int cur_rptr, unsigned int new_rptr), + TP_ARGS(cur_rb, new_rb, cur_rptr, new_rptr), TP_STRUCT__entry(__field(int, cur_level) __field(int, new_level) __field(unsigned int, cur_rptr) @@ -523,8 +528,8 @@ TRACE_EVENT(adreno_hw_preempt_token_submit, ), TP_fast_assign(__entry->cur_level = cur_rb->id; __entry->new_level = new_rb->id; - __entry->cur_rptr = cur_rb->rptr; - __entry->new_rptr = new_rb->rptr; + __entry->cur_rptr = cur_rptr; + __entry->new_rptr = new_rptr; __entry->cur_wptr = cur_rb->wptr; __entry->cur_wptr_end = cur_rb->wptr_preempt_end; __entry->new_wptr = new_rb->wptr; @@ -541,23 +546,37 @@ TRACE_EVENT(adreno_hw_preempt_token_submit, ) ); -TRACE_EVENT(adreno_rb_starve, - TP_PROTO(struct adreno_ringbuffer *rb), - TP_ARGS(rb), - TP_STRUCT__entry(__field(int, id) - __field(unsigned int, rptr) - __field(unsigned int, wptr) +TRACE_EVENT(adreno_preempt_trigger, + TP_PROTO(struct adreno_ringbuffer *cur, struct adreno_ringbuffer *next), + TP_ARGS(cur, next), + TP_STRUCT__entry( + __field(struct adreno_ringbuffer *, cur) + __field(struct adreno_ringbuffer *, next) ), - TP_fast_assign(__entry->id = rb->id; - __entry->rptr = rb->rptr; - __entry->wptr = rb->wptr; + TP_fast_assign( + __entry->cur = cur; + __entry->next = next; ), - TP_printk( - "rb %d r/w %x/%x starved", __entry->id, __entry->rptr, - __entry->wptr + TP_printk("trigger from id=%d to id=%d", + __entry->cur->id, __entry->next->id ) ); +TRACE_EVENT(adreno_preempt_done, + TP_PROTO(struct adreno_ringbuffer *cur, struct adreno_ringbuffer *next), + TP_ARGS(cur, next), + TP_STRUCT__entry( + __field(struct adreno_ringbuffer *, cur) + __field(struct adreno_ringbuffer *, next) + ), + TP_fast_assign( + __entry->cur = cur; + __entry->next = next; + ), + TP_printk("done switch to id=%d from id=%d", + __entry->next->id, __entry->cur->id + ) +); #endif /* _ADRENO_TRACE_H */ /* This part must be outside protection */ diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 2563591f376e..f77dbb7f20af 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -233,6 +233,8 @@ int kgsl_readtimestamp(struct kgsl_device *device, void *priv, } EXPORT_SYMBOL(kgsl_readtimestamp); +static long gpumem_free_entry(struct kgsl_mem_entry *entry); + /* Scheduled by kgsl_mem_entry_put_deferred() */ static void _deferred_put(struct work_struct *work) { @@ -247,10 +249,8 @@ kgsl_mem_entry_create(void) { struct kgsl_mem_entry *entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (entry != NULL) { + if (entry != NULL) kref_init(&entry->refcount); - INIT_WORK(&entry->work, _deferred_put); - } return entry; } @@ -1150,6 +1150,8 @@ static int kgsl_open_device(struct kgsl_device *device) atomic_inc(&device->active_cnt); kgsl_sharedmem_set(device, &device->memstore, 0, 0, device->memstore.size); + kgsl_sharedmem_set(device, &device->scratch, 0, 0, + device->scratch.size); result = device->ftbl->init(device); if (result) @@ -1855,7 +1857,10 @@ static long gpuobj_free_on_timestamp(struct kgsl_device_private *dev_priv, static void gpuobj_free_fence_func(void *priv) { - kgsl_mem_entry_put_deferred((struct kgsl_mem_entry *) priv); + struct kgsl_mem_entry *entry = priv; + + INIT_WORK(&entry->work, _deferred_put); + queue_work(kgsl_driver.mem_workqueue, &entry->work); } static long gpuobj_free_on_fence(struct kgsl_device_private *dev_priv, @@ -3910,11 +3915,13 @@ int kgsl_device_platform_probe(struct kgsl_device *device) status = kgsl_allocate_global(device, &device->memstore, KGSL_MEMSTORE_SIZE, 0, 0); - if (status != 0) { - KGSL_DRV_ERR(device, "kgsl_allocate_global failed %d\n", - status); + if (status != 0) goto error_close_mmu; - } + + status = kgsl_allocate_global(device, &device->scratch, + PAGE_SIZE, 0, 0); + if (status != 0) + goto error_free_memstore; /* * The default request type PM_QOS_REQ_ALL_CORES is @@ -3964,6 +3971,8 @@ int kgsl_device_platform_probe(struct kgsl_device *device) return 0; +error_free_memstore: + kgsl_free_global(device, &device->memstore); error_close_mmu: kgsl_mmu_close(device); error_pwrctrl_close: @@ -3990,6 +3999,8 @@ void kgsl_device_platform_remove(struct kgsl_device *device) idr_destroy(&device->context_idr); + kgsl_free_global(device, &device->scratch); + kgsl_free_global(device, &device->memstore); kgsl_mmu_close(device); @@ -4091,8 +4102,9 @@ static int __init kgsl_core_init(void) INIT_LIST_HEAD(&kgsl_driver.pagetable_list); kgsl_driver.workqueue = create_singlethread_workqueue("kgsl-workqueue"); - kgsl_driver.mem_workqueue = - create_singlethread_workqueue("kgsl-mementry"); + + kgsl_driver.mem_workqueue = alloc_workqueue("kgsl-mementry", + WQ_UNBOUND | WQ_MEM_RECLAIM, 0); kgsl_events_init(); diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index dfe83be799b3..c172021c8944 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -37,6 +37,32 @@ #define KGSL_MEMSTORE_MAX (KGSL_MEMSTORE_SIZE / \ sizeof(struct kgsl_devmemstore) - 1 - KGSL_PRIORITY_MAX_RB_LEVELS) +#define MEMSTORE_RB_OFFSET(rb, field) \ + KGSL_MEMSTORE_OFFSET(((rb)->id + KGSL_MEMSTORE_MAX), field) + +#define MEMSTORE_ID_GPU_ADDR(dev, iter, field) \ + ((dev)->memstore.gpuaddr + KGSL_MEMSTORE_OFFSET(iter, field)) + +#define MEMSTORE_RB_GPU_ADDR(dev, rb, field) \ + ((dev)->memstore.gpuaddr + \ + KGSL_MEMSTORE_OFFSET(((rb)->id + KGSL_MEMSTORE_MAX), field)) + +/* + * SCRATCH MEMORY: The scratch memory is one page worth of data that + * is mapped into the GPU. This allows for some 'shared' data between + * the GPU and CPU. For example, it will be used by the GPU to write + * each updated RPTR for each RB. + * + * Used Data: + * Offset: Length(bytes): What + * 0x0: 4 * KGSL_PRIORITY_MAX_RB_LEVELS: RB0 RPTR + */ + +/* Shadow global helpers */ +#define SCRATCH_RPTR_OFFSET(id) ((id) * sizeof(unsigned int)) +#define SCRATCH_RPTR_GPU_ADDR(dev, id) \ + ((dev)->scratch.gpuaddr + SCRATCH_RPTR_OFFSET(id)) + /* Timestamp window used to detect rollovers (half of integer range) */ #define KGSL_TIMESTAMP_WINDOW 0x80000000 @@ -447,21 +473,6 @@ kgsl_mem_entry_put(struct kgsl_mem_entry *entry) kref_put(&entry->refcount, kgsl_mem_entry_destroy); } -/** - * kgsl_mem_entry_put_deferred() - Schedule a task to put the memory entry - * @entry: Mem entry to put - * - * This function is for atomic contexts where a normal kgsl_mem_entry_put() - * would result in the memory entry getting destroyed and possibly taking - * mutexes along the way. Schedule the work to happen outside of the atomic - * context. - */ -static inline void kgsl_mem_entry_put_deferred(struct kgsl_mem_entry *entry) -{ - if (entry != NULL) - queue_work(kgsl_driver.mem_workqueue, &entry->work); -} - /* * kgsl_addr_range_overlap() - Checks if 2 ranges overlap * @gpuaddr1: Start of first address range diff --git a/drivers/gpu/msm/kgsl_cmdbatch.h b/drivers/gpu/msm/kgsl_cmdbatch.h index 1547ac02fdbf..d5cbf375b5d3 100644 --- a/drivers/gpu/msm/kgsl_cmdbatch.h +++ b/drivers/gpu/msm/kgsl_cmdbatch.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-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 @@ -31,7 +31,6 @@ * @fault_policy: Internal policy describing how to handle this command in case * of a fault * @fault_recovery: recovery actions actually tried for this batch - * @expires: Point in time when the cmdbatch is considered to be hung * @refcount: kref structure to maintain the reference count * @cmdlist: List of IBs to issue * @memlist: List of all memory used in this command batch @@ -61,7 +60,6 @@ struct kgsl_cmdbatch { unsigned long priv; unsigned long fault_policy; unsigned long fault_recovery; - unsigned long expires; struct kref refcount; struct list_head cmdlist; struct list_head memlist; diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index c3fb2b81fcbd..4159a5fe375f 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -227,6 +227,7 @@ struct kgsl_device { /* GPU shader memory size */ unsigned int shader_mem_len; struct kgsl_memdesc memstore; + struct kgsl_memdesc scratch; const char *iomemname; const char *shadermemname; diff --git a/drivers/gpu/msm/kgsl_events.c b/drivers/gpu/msm/kgsl_events.c index e1f9ad17d0ff..6f70b9ddd376 100644 --- a/drivers/gpu/msm/kgsl_events.c +++ b/drivers/gpu/msm/kgsl_events.c @@ -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 @@ -56,6 +56,23 @@ static void _kgsl_event_worker(struct work_struct *work) kmem_cache_free(events_cache, event); } +/* return true if the group needs to be processed */ +static bool _do_process_group(unsigned int processed, unsigned int cur) +{ + if (processed == cur) + return false; + + /* + * This ensures that the timestamp didn't slip back accidently, maybe + * due to a memory barrier issue. This is highly unlikely but we've + * been burned here in the past. + */ + if ((cur < processed) && ((processed - cur) < KGSL_TIMESTAMP_WINDOW)) + return false; + + return true; +} + static void _process_event_group(struct kgsl_device *device, struct kgsl_event_group *group, bool flush) { @@ -80,11 +97,7 @@ static void _process_event_group(struct kgsl_device *device, group->readtimestamp(device, group->priv, KGSL_TIMESTAMP_RETIRED, ×tamp); - /* - * If no timestamps have been retired since the last time we were here - * then we can avoid going through this loop - */ - if (!flush && timestamp_cmp(timestamp, group->processed) <= 0) + if (!flush && _do_process_group(group->processed, timestamp) == false) goto out; list_for_each_entry_safe(event, tmp, &group->events, node) { diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c index a338559ac0bb..103d290eb681 100644 --- a/drivers/gpu/msm/kgsl_iommu.c +++ b/drivers/gpu/msm/kgsl_iommu.c @@ -220,9 +220,6 @@ static int _attach_pt(struct kgsl_iommu_pt *iommu_pt, if (ret == 0) iommu_pt->attached = true; - else - KGSL_CORE_ERR("iommu_attach_device(%s) failed: %d\n", - ctx->name, ret); return ret; } @@ -1452,25 +1449,25 @@ done: return ret; } +static int kgsl_iommu_set_pt(struct kgsl_mmu *mmu, struct kgsl_pagetable *pt); + static int kgsl_iommu_start(struct kgsl_mmu *mmu) { int status; struct kgsl_iommu *iommu = _IOMMU_PRIV(mmu); - struct kgsl_iommu_context *ctx = &iommu->ctx[KGSL_IOMMU_CONTEXT_USER]; status = _setup_user_context(mmu); if (status) return status; status = _setup_secure_context(mmu); - if (status) + if (status) { _detach_context(&iommu->ctx[KGSL_IOMMU_CONTEXT_USER]); - else { - kgsl_iommu_enable_clk(mmu); - KGSL_IOMMU_SET_CTX_REG(ctx, TLBIALL, 1); - kgsl_iommu_disable_clk(mmu); + return status; } - return status; + + /* Make sure the hardware is programmed to the default pagetable */ + return kgsl_iommu_set_pt(mmu, mmu->defaultpagetable); } static int @@ -1707,23 +1704,15 @@ kgsl_iommu_get_current_ttbr0(struct kgsl_mmu *mmu) * * Return - void */ -static int kgsl_iommu_set_pt(struct kgsl_mmu *mmu, - struct kgsl_pagetable *pt) +static int kgsl_iommu_set_pt(struct kgsl_mmu *mmu, struct kgsl_pagetable *pt) { - struct kgsl_device *device = KGSL_MMU_DEVICE(mmu); struct kgsl_iommu *iommu = _IOMMU_PRIV(mmu); struct kgsl_iommu_context *ctx = &iommu->ctx[KGSL_IOMMU_CONTEXT_USER]; - int ret = 0; uint64_t ttbr0, temp; unsigned int contextidr; unsigned long wait_for_flush; - /* - * If using a global pagetable, we can skip all this - * because the pagetable will be set up by the iommu - * driver and never changed at runtime. - */ - if (!kgsl_mmu_is_perprocess(mmu)) + if ((pt != mmu->defaultpagetable) && !kgsl_mmu_is_perprocess(mmu)) return 0; kgsl_iommu_enable_clk(mmu); @@ -1731,14 +1720,6 @@ static int kgsl_iommu_set_pt(struct kgsl_mmu *mmu, ttbr0 = kgsl_mmu_pagetable_get_ttbr0(pt); contextidr = kgsl_mmu_pagetable_get_contextidr(pt); - /* - * Taking the liberty to spin idle since this codepath - * is invoked when we can spin safely for it to be idle - */ - ret = adreno_spin_idle(ADRENO_DEVICE(device), ADRENO_IDLE_TIMEOUT); - if (ret) - return ret; - KGSL_IOMMU_SET_CTX_REG_Q(ctx, TTBR0, ttbr0); KGSL_IOMMU_SET_CTX_REG(ctx, CONTEXTIDR, contextidr); @@ -1767,10 +1748,8 @@ static int kgsl_iommu_set_pt(struct kgsl_mmu *mmu, cpu_relax(); } - /* Disable smmu clock */ kgsl_iommu_disable_clk(mmu); - - return ret; + return 0; } /* @@ -1788,8 +1767,6 @@ static int kgsl_iommu_set_pf_policy(struct kgsl_mmu *mmu, struct kgsl_iommu_context *ctx = &iommu->ctx[KGSL_IOMMU_CONTEXT_USER]; struct kgsl_device *device = KGSL_MMU_DEVICE(mmu); struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - int ret = 0; - unsigned int sctlr_val; if ((adreno_dev->ft_pf_policy & BIT(KGSL_FT_PAGEFAULT_GPUHALT_ENABLE)) == @@ -1798,10 +1775,7 @@ static int kgsl_iommu_set_pf_policy(struct kgsl_mmu *mmu, /* If not attached, policy will be updated during the next attach */ if (ctx->default_pt != NULL) { - /* Need to idle device before changing options */ - ret = device->ftbl->idle(device); - if (ret) - return ret; + unsigned int sctlr_val; kgsl_iommu_enable_clk(mmu); @@ -1820,7 +1794,7 @@ static int kgsl_iommu_set_pf_policy(struct kgsl_mmu *mmu, kgsl_iommu_disable_clk(mmu); } - return ret; + return 0; } static struct kgsl_protected_registers * diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h index 3652aa2e6ec4..5339917911b1 100644 --- a/drivers/gpu/msm/kgsl_mmu.h +++ b/drivers/gpu/msm/kgsl_mmu.h @@ -21,6 +21,12 @@ #define KGSL_MMU_GLOBAL_PT 0 #define KGSL_MMU_SECURE_PT 1 +#define MMU_DEFAULT_TTBR0(_d) \ + (kgsl_mmu_pagetable_get_ttbr0((_d)->mmu.defaultpagetable)) + +#define MMU_DEFAULT_CONTEXTIDR(_d) \ + (kgsl_mmu_pagetable_get_contextidr((_d)->mmu.defaultpagetable)) + struct kgsl_device; enum kgsl_mmutype { diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index da8c8585d31e..2b9eef8b6351 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -1381,6 +1381,9 @@ static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state, _isense_clk_set_rate(pwr, pwr->num_pwrlevels - 1); } + + /* Turn off the IOMMU clocks */ + kgsl_mmu_disable_clk(&device->mmu); } else if (requested_state == KGSL_STATE_SLEEP) { /* High latency clock maintenance. */ for (i = KGSL_MAX_CLKS - 1; i > 0; i--) @@ -1428,7 +1431,11 @@ static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state, pwr->gpu_bimc_interface_enabled = 1; } } + + /* Turn on the IOMMU clocks */ + kgsl_mmu_enable_clk(&device->mmu); } + } } diff --git a/drivers/input/touchscreen/it7258_ts_i2c.c b/drivers/input/touchscreen/it7258_ts_i2c.c new file mode 100644 index 000000000000..048358e2ef9d --- /dev/null +++ b/drivers/input/touchscreen/it7258_ts_i2c.c @@ -0,0 +1,851 @@ +/* drivers/input/touchscreen/it7258_ts_i2c.c + * + * Copyright (C) 2014 ITE Tech. Inc. + * + * 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/delay.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/firmware.h> +#include <linux/gpio.h> +#include <linux/slab.h> +#include <linux/wakelock.h> + +#define MAX_BUFFER_SIZE 144 +#define DEVICE_NAME "IT7260" +#define SCREEN_X_RESOLUTION 320 +#define SCREEN_Y_RESOLUTION 320 + +#define BUF_COMMAND 0x20 /* all commands writes go to this idx */ +#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 */ +#define BUF_SYS_RESPONSE 0xC0 +#define BUF_POINT_INFO 0xE0 /* reads of "point" go through here and produce 14 bytes of data */ + +/* 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 */ +#define CMD_FIRMWARE_REINIT_C 0x0C +#define CMD_CALIBRATE 0x13 /* needs to be followed by 4 bytes of zeroes */ +#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 CMD_FIRMWARE_REINIT_6F 0x6F + +#define FW_WRITE_CHUNK_SIZE 128 +#define FW_WRITE_RETRY_COUNT 4 +#define CHIP_FLASH_SIZE 0x8000 +#define SYSFS_FW_UPLOAD_MODE_MANUAL 2 +#define SYSFS_RESULT_FAIL (-1) +#define SYSFS_RESULT_NOT_DONE 0 +#define SYSFS_RESULT_SUCCESS 1 +#define DEVICE_READY_MAX_WAIT 500 + +/* result of reading with BUF_QUERY bits */ +#define CMD_STATUS_BITS 0x07 +#define CMD_STATUS_DONE 0x00 +#define CMD_STATUS_BUSY 0x01 +#define CMD_STATUS_ERROR 0x02 +#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 */ + +/* use this to include integers in commands */ +#define CMD_UINT16(v) ((uint8_t)(v)) , ((uint8_t)((v) >> 8)) + + +struct FingerData { + uint8_t xLo; + uint8_t hi; + uint8_t yLo; + uint8_t pressure; +} __attribute__((packed)); + +struct PointData { + uint8_t flags; + uint8_t palm; + struct FingerData fd[3]; +} __attribute__((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) */ +#define PD_PALM_FLAG_BIT 0x01 +#define FD_PRESSURE_BITS 0x0F +#define FD_PRESSURE_NONE 0x00 +#define FD_PRESSURE_HOVER 0x01 +#define FD_PRESSURE_LIGHT 0x02 +#define FD_PRESSURE_NORMAL 0x04 +#define FD_PRESSURE_HIGH 0x08 +#define FD_PRESSURE_HEAVY 0x0F + +struct IT7260_ts_data { + struct i2c_client *client; + 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 DEFINE_MUTEX(sleepModeMutex); +static bool chipAwake = true; +static bool hadFingerDown = false; +static bool isDeviceSleeping = false; +static bool isDeviceSuspend = false; +static struct input_dev *input_dev; +static struct IT7260_ts_data *gl_ts; + +#define LOGE(...) pr_err(DEVICE_NAME ": " __VA_ARGS__) +#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) +{ + struct i2c_msg msgs[2] = { + { + .addr = gl_ts->client->addr, + .flags = I2C_M_NOSTART, + .len = 1, + .buf = &bufferIndex + }, + { + .addr = gl_ts->client->addr, + .flags = I2C_M_RD, + .len = dataLength, + .buf = dataBuffer + } + }; + + memset(dataBuffer, 0xFF, dataLength); + + return i2c_transfer(gl_ts->client->adapter, msgs, 2); +} + +static bool i2cWriteNoReadyCheck(uint8_t bufferIndex, const uint8_t *dataBuffer, uint16_t dataLength) +{ + uint8_t txbuf[257]; + struct i2c_msg msg = { + .addr = gl_ts->client->addr, + .flags = 0, + .len = dataLength + 1, + .buf = txbuf + }; + + /* just to be careful */ + BUG_ON(dataLength > sizeof(txbuf) - 1); + + txbuf[0] = bufferIndex; + memcpy(txbuf + 1, dataBuffer, dataLength); + + return i2c_transfer(gl_ts->client->adapter, &msg, 1); +} + +/* + * 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{ + if (!i2cReadNoReadyCheck(BUF_QUERY, &ucQuery, sizeof(ucQuery))) + ucQuery = CMD_STATUS_BUSY; + + if (slowly) + mdelay(1000); + if (!forever) + count--; + + }while((ucQuery & CMD_STATUS_BUSY) && count); + + return !ucQuery; +} + +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) +{ + waitDeviceReady(false, false); + return i2cWriteNoReadyCheck(bufferIndex, dataBuffer, dataLength); +} + +static bool chipFirmwareReinitialize(uint8_t cmdOfChoice) +{ + uint8_t cmd[] = {cmdOfChoice}; + uint8_t rsp[2]; + + if (!i2cWrite(BUF_COMMAND, cmd, sizeof(cmd))) + return false; + + if (!i2cRead(BUF_RESPONSE, rsp, sizeof(rsp))) + return false; + + /* a reply of two zero bytes signifies success */ + return !rsp[0] && !rsp[1]; +} + +static bool chipFirmwareUpgradeModeEnterExit(bool enter) +{ + 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; + if (!i2cWrite(BUF_COMMAND, cmd, sizeof(cmd))) + return false; + + if (!i2cRead(BUF_RESPONSE, resp, sizeof(resp))) + return false; + + /* a reply of two zero bytes signifies success */ + return !resp[0] && !resp[1]; +} + +static bool chipSetStartOffset(uint16_t offset) +{ + uint8_t cmd[] = {CMD_SET_START_OFFSET, 0, CMD_UINT16(offset)}; + uint8_t resp[2]; + + if (!i2cWrite(BUF_COMMAND, cmd, 4)) + return false; + + + if (!i2cRead(BUF_RESPONSE, resp, sizeof(resp))) + return false; + + + /* a reply of two zero bytes signifies success */ + return !resp[0] && !resp[1]; +} + + +/* write fwLength bytes from fwData at chip offset 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) { + + uint8_t cmdWrite[2 + FW_WRITE_CHUNK_SIZE] = {CMD_FW_WRITE}; + uint8_t bufRead[FW_WRITE_CHUNK_SIZE]; + uint8_t cmdRead[2] = {CMD_FW_READ}; + unsigned i, nRetries; + uint32_t curWriteSz; + + /* figure out how much to write */ + curWriteSz = fwLength - curDataOfst; + if (curWriteSz > FW_WRITE_CHUNK_SIZE) + curWriteSz = FW_WRITE_CHUNK_SIZE; + + /* prepare the write command */ + cmdWrite[1] = curWriteSz; + for (i = 0; i < curWriteSz; i++) + cmdWrite[i + 2] = fwData[curDataOfst + i]; + + /* prepare the read command */ + cmdRead[1] = curWriteSz; + + for (nRetries = 0; nRetries < FW_WRITE_RETRY_COUNT; nRetries++) { + + /* set write offset and write the data*/ + chipSetStartOffset(writeStartOffset + curDataOfst); + i2cWrite(BUF_COMMAND, cmdWrite, 2 + curWriteSz); + + /* set offset and read the data back */ + chipSetStartOffset(writeStartOffset + curDataOfst); + i2cWrite(BUF_COMMAND, cmdRead, sizeof(cmdRead)); + i2cRead(BUF_RESPONSE, bufRead, curWriteSz); + + /* verify. If success break out of retry loop */ + for (i = 0; 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); + } + /* if we've failed after all the retries, tell the caller */ + if (nRetries == FW_WRITE_RETRY_COUNT) + return false; + } + + return true; +} + +static bool chipFirmwareUpload(uint32_t fwLen, const uint8_t *fwData, uint32_t cfgLen, const uint8_t *cfgData) +{ + bool success = false; + + /* enter fw upload mode */ + if (!chipFirmwareUpgradeModeEnterExit(true)) + return false; + + /* flash the firmware if requested */ + if (fwLen && fwData && !chipFlashWriteAndVerify(fwLen, fwData, 0)) { + LOGE("failed to upload touch firmware\n"); + goto out; + } + + /* flash config data if requested */ + if (fwLen && fwData && !chipFlashWriteAndVerify(cfgLen, cfgData, CHIP_FLASH_SIZE - cfgLen)) { + LOGE("failed to upload touch cfg data\n"); + goto out; + } + + success = true; + +out: + 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 */ + 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 */ + 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 = i2cRead(BUF_RESPONSE, verCfg, VERSION_LENGTH) && ret; + + if (logIt) + LOGI("current versions: fw@{%X,%X,%X,%X}, cfg@{%X,%X,%X,%X}\n", + verFw[5], verFw[6], verFw[7], verFw[8], + verCfg[1], verCfg[2], verCfg[3], verCfg[4]); + + return ret; +} + +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; + + if (request_firmware(&fw, "it7260.fw", dev)) + LOGE("failed to get firmware for it7260\n"); + else + fwLen = fw->size; + + + if (request_firmware(&cfg, "it7260.cfg", dev)) + LOGE("failed to get config data for it7260\n"); + else + cfgLen = cfg->size; + + sscanf(buf, "%d", &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"); + + chipGetVersions(verFw, verCfg, true); + + fwUploadResult = SYSFS_RESULT_NOT_DONE; + if (fwLen && cfgLen) { + if (manualUpgrade || (verFw[5] < fw->data[8] || verFw[6] < + fw->data[9] || verFw[7] < fw->data[10] || verFw[8] < + fw->data[11]) || (verCfg[1] < cfg->data[cfgLen - 8] + || verCfg[2] < cfg->data[cfgLen - 7] || verCfg[3] < + cfg->data[cfgLen - 6] || + 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); + enable_irq(gl_ts->client->irq); + + fwUploadResult = success ? SYSFS_RESULT_SUCCESS : SYSFS_RESULT_FAIL; + LOGI("upload %s\n", success ? "success" : "failed"); + } + else { + LOGI("firmware/config upgrade not needed\n"); + } + } + + if (fwLen) + release_firmware(fw); + + if (cfgLen) + release_firmware(cfg); + + return count; +} + +static ssize_t sysfsUpgradeShow(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d", fwUploadResult); +} + +static ssize_t sysfsCalibrationShow(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d", calibrationWasSuccessful); +} + +static bool chipSendCalibrationCmd(bool autoTuneOn) +{ + uint8_t cmdCalibrate[] = {CMD_CALIBRATE, 0, autoTuneOn ? 1 : 0, 0, 0}; + return i2cWrite(BUF_COMMAND, cmdCalibrate, sizeof(cmdCalibrate)); +} + +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 */ + if (!resp) + 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) +{ + 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]); + + + LOGI("%s", buf); + + return ret; +} + +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) +{ + return sprintf(buf, "%d\n", devicePresent ? 1 : 0); +} + +static ssize_t sysfsStatusStore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + uint8_t verFw[10], verCfg[10]; + + chipGetVersions(verFw, verCfg, true); + + return count; +} + +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]); +} + +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) +{ + /* + * 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 + return -EINTR; +} + +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; + bool goToWake; + uint8_t dummy; + + sscanf(buf, "%d", &goToSleepVal); + goToWake = !goToSleepVal; /* convert to bool of proper polarity */ + + if (!mutex_lock_interruptible(&sleepModeMutex)) { + if ((chipAwake && goToWake) || (!chipAwake && !goToWake)) + 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"); + } else { + disable_irq(gl_ts->client->irq); + i2cWriteNoReadyCheck(BUF_COMMAND, cmdGoSleep, sizeof(cmdGoSleep)); + LOGI("touch is going to sleep...\n\n"); + } + chipAwake = goToWake; + mutex_unlock(&sleepModeMutex); + return count; + } 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 struct attribute *it7260_attrstatus[] = { + &dev_attr_status.attr, + &dev_attr_version.attr, + &dev_attr_sleep.attr, + NULL +}; + +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 struct attribute *it7260_attributes[] = { + &dev_attr_calibration.attr, + &dev_attr_upgrade.attr, + &dev_attr_point.attr, + NULL +}; + +static const struct attribute_group it7260_attr_group = { + .attrs = it7260_attributes, +}; + +static void chipExternalCalibration(bool autoTuneEnabled) +{ + uint8_t resp[2]; + + LOGI("sent calibration command -> %d\n", chipSendCalibrationCmd(autoTuneEnabled)); + waitDeviceReady(true, true); + i2cReadNoReadyCheck(BUF_RESPONSE, resp, sizeof(resp)); + chipFirmwareReinitialize(CMD_FIRMWARE_REINIT_C); +} + +void sendCalibrationCmd(void) +{ + chipExternalCalibration(false); +} +EXPORT_SYMBOL(sendCalibrationCmd); + +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; + + x += ((uint16_t)(fd->hi & 0x0F)) << 8; + y += ((uint16_t)(fd->hi & 0xF0)) << 4; + + if (xP) + *xP = x; + if (yP) + *yP = y; + if (pressureP) + *pressureP = fd->pressure & FD_PRESSURE_BITS; +} + +static void readTouchDataPoint(void) +{ + struct PointData pointData; + uint8_t devStatus; + uint8_t pressure = FD_PRESSURE_NONE; + uint16_t x, y; + + /* 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); + return; + } + if (!i2cReadNoReadyCheck(BUF_POINT_INFO, (void*)&pointData, sizeof(pointData))) { + LOGE("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); + return; + } + + if ((pointData.flags & PD_FLAGS_HAVE_FINGERS) & 1) + readFingerData(&x, &y, &pressure, pointData.fd); + + if (pressure >= FD_PRESSURE_LIGHT) { + + if (!hadFingerDown) + hadFingerDown = true; + + readFingerData(&x, &y, &pressure, pointData.fd); + + input_report_abs(gl_ts->input_dev, ABS_X, x); + input_report_abs(gl_ts->input_dev, ABS_Y, y); + input_report_key(gl_ts->input_dev, BTN_TOUCH, 1); + input_sync(gl_ts->input_dev); + + } else if (hadFingerDown) { + hadFingerDown = false; + + input_report_key(gl_ts->input_dev, BTN_TOUCH, 0); + input_sync(gl_ts->input_dev); + } + +} + +static irqreturn_t IT7260_ts_threaded_handler(int irq, void *devid) +{ + smp_rmb(); + if (isDeviceSleeping) { + smp_wmb(); + } else { + 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'}; + uint8_t chipID[10] = {0,}; + + waitDeviceReady(true, false); + + if (!i2cWriteNoReadyCheck(BUF_COMMAND, cmdIdent, sizeof(cmdIdent))) { + LOGE("i2cWrite() failed\n"); + return false; + } + + waitDeviceReady(true, false); + + if (!i2cReadNoReadyCheck(BUF_RESPONSE, chipID, sizeof(chipID))) { + LOGE("i2cRead() failed\n"); + return false; + } + pr_info("chipIdentifyIT7260 read id: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", + chipID[0], chipID[1], chipID[2], chipID[3], chipID[4], + chipID[5], chipID[6], chipID[7], chipID[8], chipID[9]); + + if (memcmp(chipID, expectedID, sizeof(expectedID))) + return false; + + if (chipID[8] == '5' && chipID[9] == '6') + LOGI("rev BX3 found\n"); + 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]); + + return true; +} + +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; + uint8_t rsp[2]; + int ret = -1; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + LOGE("need I2C_FUNC_I2C\n"); + ret = -ENODEV; + goto err_out; + } + + if (!client->irq) { + LOGE("need IRQ\n"); + ret = -ENODEV; + goto err_out; + } + gl_ts = kzalloc(sizeof(*gl_ts), GFP_KERNEL); + if (!gl_ts) { + ret = -ENOMEM; + goto err_out; + } + + gl_ts->client = client; + i2c_set_clientdata(client, gl_ts); + pdata = client->dev.platform_data; + + if (sysfs_create_group(&(client->dev.kobj), &it7260_attrstatus_group)) { + dev_err(&client->dev, "failed to register sysfs #1\n"); + goto err_sysfs_grp_create_1; + } + + if (!chipIdentifyIT7260()) { + LOGI ("chipIdentifyIT7260 FAIL"); + goto err_ident_fail_or_input_alloc; + } + + input_dev = input_allocate_device(); + if (!input_dev) { + LOGE("failed to allocate input device\n"); + ret = -ENOMEM; + goto err_ident_fail_or_input_alloc; + } + gl_ts->input_dev = input_dev; + + input_dev->name = DEVICE_NAME; + input_dev->phys = "I2C"; + input_dev->id.bustype = BUS_I2C; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x7260; + set_bit(EV_SYN, input_dev->evbit); + set_bit(EV_KEY, input_dev->evbit); + set_bit(EV_ABS, input_dev->evbit); + set_bit(INPUT_PROP_DIRECT,input_dev->propbit); + set_bit(BTN_TOUCH, input_dev->keybit); + set_bit(KEY_SLEEP,input_dev->keybit); + set_bit(KEY_WAKEUP,input_dev->keybit); + set_bit(KEY_POWER,input_dev->keybit); + input_set_abs_params(input_dev, ABS_X, 0, SCREEN_X_RESOLUTION, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, SCREEN_Y_RESOLUTION, 0, 0); + + if (input_register_device(input_dev)) { + LOGE("failed to register input device\n"); + goto err_input_register; + } + + 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; + } + + if (sysfs_create_group(&(client->dev.kobj), &it7260_attr_group)) { + dev_err(&client->dev, "failed to register sysfs #2\n"); + goto err_sysfs_grp_create_2; + } + + devicePresent = true; + + i2cWriteNoReadyCheck(BUF_COMMAND, cmdStart, sizeof(cmdStart)); + mdelay(10); + i2cReadNoReadyCheck(BUF_RESPONSE, rsp, sizeof(rsp)); + mdelay(10); + + return 0; + +err_sysfs_grp_create_2: + free_irq(client->irq, gl_ts); + +err_irq_reg: + input_unregister_device(input_dev); + input_dev = NULL; + +err_input_register: + if (input_dev) + input_free_device(input_dev); + +err_ident_fail_or_input_alloc: + sysfs_remove_group(&(client->dev.kobj), &it7260_attrstatus_group); + +err_sysfs_grp_create_1: + kfree(gl_ts); + +err_out: + return ret; +} + +static int IT7260_ts_remove(struct i2c_client *client) +{ + devicePresent = false; + return 0; +} + +static const struct i2c_device_id IT7260_ts_id[] = { + { DEVICE_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, IT7260_ts_id); + +static const struct of_device_id IT7260_match_table[] = { + { .compatible = "ITE,IT7260_ts",}, + {}, +}; + +static int IT7260_ts_resume(struct i2c_client *i2cdev) +{ + isDeviceSuspend = false; + return 0; +} + +static int IT7260_ts_suspend(struct i2c_client *i2cdev, pm_message_t pmesg) +{ + isDeviceSuspend = true; + return 0; +} + +static struct i2c_driver IT7260_ts_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DEVICE_NAME, + .of_match_table = IT7260_match_table, + }, + .probe = IT7260_ts_probe, + .remove = IT7260_ts_remove, + .id_table = IT7260_ts_id, + .resume = IT7260_ts_resume, + .suspend = IT7260_ts_suspend, +}; + +module_i2c_driver(IT7260_ts_driver); + +MODULE_DESCRIPTION("IT7260 Touchscreen Driver"); +MODULE_LICENSE("GPL v2"); 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/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c index a76ccc06c9e1..d42ada769380 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c @@ -101,29 +101,21 @@ static void msm_vfe40_config_irq(struct vfe_device *vfe_dev, uint32_t irq0_mask, uint32_t irq1_mask, enum msm_isp_irq_operation oper) { - uint32_t val; - switch (oper) { case MSM_ISP_IRQ_ENABLE: - val = msm_camera_io_r(vfe_dev->vfe_base + 0x28); - val |= irq0_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x28); - val = msm_camera_io_r(vfe_dev->vfe_base + 0x2C); - val |= irq1_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x2C); + vfe_dev->irq0_mask |= irq0_mask; + vfe_dev->irq1_mask |= irq1_mask; break; case MSM_ISP_IRQ_DISABLE: - val = msm_camera_io_r(vfe_dev->vfe_base + 0x28); - val &= ~irq0_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x28); - val = msm_camera_io_r(vfe_dev->vfe_base + 0x2C); - val &= ~irq1_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x2C); + vfe_dev->irq0_mask &= ~irq0_mask; + vfe_dev->irq1_mask &= ~irq1_mask; break; case MSM_ISP_IRQ_SET: - msm_camera_io_w_mb(irq0_mask, vfe_dev->vfe_base + 0x28); - msm_camera_io_w_mb(irq1_mask, vfe_dev->vfe_base + 0x2C); + vfe_dev->irq0_mask = irq0_mask; + vfe_dev->irq1_mask = irq1_mask; } + msm_camera_io_w_mb(vfe_dev->irq0_mask, vfe_dev->vfe_base + 0x28); + msm_camera_io_w_mb(vfe_dev->irq1_mask, vfe_dev->vfe_base + 0x2C); } static int32_t msm_vfe40_init_qos_parms(struct vfe_device *vfe_dev, @@ -335,10 +327,8 @@ static void msm_vfe40_init_hardware_reg(struct vfe_device *vfe_dev) msm_vfe40_init_vbif_parms(vfe_dev, &vbif_parms); /* BUS_CFG */ msm_camera_io_w(0x10000001, vfe_dev->vfe_base + 0x50); - vfe_dev->irq0_mask = 0xE00000F1; - vfe_dev->irq1_mask = 0xFEFFFFFF; - msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe40_config_irq(vfe_dev, 0x800000E0, 0xFEFFFF7E, + MSM_ISP_IRQ_ENABLE); msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x30); msm_camera_io_w_mb(0xFEFFFFFF, vfe_dev->vfe_base + 0x34); msm_camera_io_w(1, vfe_dev->vfe_base + 0x24); @@ -346,15 +336,13 @@ static void msm_vfe40_init_hardware_reg(struct vfe_device *vfe_dev) msm_camera_io_w(0, vfe_dev->vfe_base + 0x30); msm_camera_io_w_mb(0, vfe_dev->vfe_base + 0x34); msm_camera_io_w(1, vfe_dev->vfe_base + 0x24); - msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); } static void msm_vfe40_clear_status_reg(struct vfe_device *vfe_dev) { vfe_dev->irq0_mask = (1 << 31); vfe_dev->irq1_mask = 0; - msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, + msm_vfe40_config_irq(vfe_dev, (1 << 31), 0, MSM_ISP_IRQ_SET); msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x30); msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x34); @@ -589,7 +577,6 @@ static void msm_vfe40_read_irq_status(struct vfe_device *vfe_dev, if (*irq_status1 & (1 << 0)) { vfe_dev->error_info.camif_status = msm_camera_io_r(vfe_dev->vfe_base + 0x31C); - vfe_dev->irq1_mask &= ~(1 << 0); msm_vfe40_config_irq(vfe_dev, 0, (1 << 0), MSM_ISP_IRQ_DISABLE); } @@ -812,11 +799,9 @@ static void msm_vfe40_axi_cfg_comp_mask(struct vfe_device *vfe_dev, comp_mask |= (axi_data->composite_info[comp_mask_index]. stream_composite_mask << (comp_mask_index * 8)); - vfe_dev->irq0_mask |= 1 << (comp_mask_index + 25); - msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x40); - msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe40_config_irq(vfe_dev, 1 << (comp_mask_index + 25), 0, + MSM_ISP_IRQ_ENABLE); } static void msm_vfe40_axi_clear_comp_mask(struct vfe_device *vfe_dev, @@ -828,27 +813,24 @@ static void msm_vfe40_axi_clear_comp_mask(struct vfe_device *vfe_dev, comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x40); comp_mask &= ~(0x7F << (comp_mask_index * 8)); - vfe_dev->irq0_mask &= ~(1 << (comp_mask_index + 25)); - msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x40); - msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe40_config_irq(vfe_dev, (1 << (comp_mask_index + 25)), 0, + MSM_ISP_IRQ_DISABLE); } static void msm_vfe40_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info) { - vfe_dev->irq0_mask |= 1 << (stream_info->wm[0] + 8); - msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe40_config_irq(vfe_dev, 1 << (stream_info->wm[0] + 8), 0, + MSM_ISP_IRQ_ENABLE); } static void msm_vfe40_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info) { vfe_dev->irq0_mask &= ~(1 << (stream_info->wm[0] + 8)); - msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe40_config_irq(vfe_dev, (1 << (stream_info->wm[0] + 8)), 0, + MSM_ISP_IRQ_DISABLE); } static void msm_vfe40_cfg_framedrop(void __iomem *vfe_base, @@ -1088,10 +1070,8 @@ static void msm_vfe40_cfg_fetch_engine(struct vfe_device *vfe_dev, temp |= (1 << 1); msm_camera_io_w(temp, vfe_dev->vfe_base + 0x50); - vfe_dev->irq0_mask &= 0xFEFFFFFF; - vfe_dev->irq0_mask |= (1 << 24); - msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe40_config_irq(vfe_dev, (1 << 24), 0, + MSM_ISP_IRQ_ENABLE); msm_camera_io_w((fe_cfg->fetch_height - 1), vfe_dev->vfe_base + 0x238); @@ -1382,13 +1362,11 @@ static void msm_vfe40_update_camif_state(struct vfe_device *vfe_dev, return; if (update_state == ENABLE_CAMIF) { - msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x30); - msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x34); + msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x30); + msm_camera_io_w_mb(0x81, vfe_dev->vfe_base + 0x34); msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x24); - vfe_dev->irq0_mask |= 0xF7; - msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask, - vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe40_config_irq(vfe_dev, 0xF7, 0x81, + MSM_ISP_IRQ_ENABLE); msm_camera_io_w_mb(0x140000, vfe_dev->vfe_base + 0x318); bus_en = @@ -1413,8 +1391,8 @@ static void msm_vfe40_update_camif_state(struct vfe_device *vfe_dev, if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN) update_state = DISABLE_CAMIF; - msm_vfe40_config_irq(vfe_dev, 0, 0, - MSM_ISP_IRQ_SET); + msm_vfe40_config_irq(vfe_dev, 0, 0x81, + MSM_ISP_IRQ_DISABLE); val = msm_camera_io_r(vfe_dev->vfe_base + 0x464); /* disable danger signal */ msm_camera_io_w_mb(val & ~(1 << 8), vfe_dev->vfe_base + 0x464); @@ -1897,6 +1875,9 @@ static void msm_vfe40_stats_cfg_comp_mask(struct vfe_device *vfe_dev, comp_mask_reg |= mask_bf_scale << (16 + request_comp_index * 8); atomic_set(stats_comp_mask, stats_mask | atomic_read(stats_comp_mask)); + msm_vfe40_config_irq(vfe_dev, + 1 << (request_comp_index + 29), 0, + MSM_ISP_IRQ_ENABLE); } else { if (!(atomic_read(stats_comp_mask) & stats_mask)) return; @@ -1904,6 +1885,9 @@ static void msm_vfe40_stats_cfg_comp_mask(struct vfe_device *vfe_dev, ~stats_mask & atomic_read(stats_comp_mask)); comp_mask_reg &= ~(mask_bf_scale << (16 + request_comp_index * 8)); + msm_vfe40_config_irq(vfe_dev, + 1 << (request_comp_index + 29), 0, + MSM_ISP_IRQ_DISABLE); } msm_camera_io_w(comp_mask_reg, vfe_dev->vfe_base + 0x44); @@ -1919,20 +1903,18 @@ static void msm_vfe40_stats_cfg_wm_irq_mask( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info) { - vfe_dev->irq0_mask |= - 1 << (STATS_IDX(stream_info->stream_handle) + 16); - msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe40_config_irq(vfe_dev, + 1 << (STATS_IDX(stream_info->stream_handle) + 16), 0, + MSM_ISP_IRQ_ENABLE); } static void msm_vfe40_stats_clear_wm_irq_mask( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info) { - vfe_dev->irq0_mask &= - ~(1 << (STATS_IDX(stream_info->stream_handle) + 16)); - msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe40_config_irq(vfe_dev, + (1 << (STATS_IDX(stream_info->stream_handle) + 16)), 0, + MSM_ISP_IRQ_DISABLE); } static void msm_vfe40_stats_cfg_wm_reg( diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c index 08b20395813c..388656b9ca30 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c @@ -70,30 +70,22 @@ static void msm_vfe44_config_irq(struct vfe_device *vfe_dev, uint32_t irq0_mask, uint32_t irq1_mask, enum msm_isp_irq_operation oper) { - uint32_t val; - switch (oper) { case MSM_ISP_IRQ_ENABLE: - val = msm_camera_io_r(vfe_dev->vfe_base + 0x28); - val |= irq0_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x28); - val = msm_camera_io_r(vfe_dev->vfe_base + 0x2C); - val |= irq1_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x2C); + vfe_dev->irq0_mask |= irq0_mask; + vfe_dev->irq1_mask |= irq1_mask; break; case MSM_ISP_IRQ_DISABLE: - val = msm_camera_io_r(vfe_dev->vfe_base + 0x28); - val &= ~irq0_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x28); - val = msm_camera_io_r(vfe_dev->vfe_base + 0x2C); - val &= ~irq1_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x2C); + vfe_dev->irq0_mask &= ~irq0_mask; + vfe_dev->irq1_mask &= ~irq1_mask; break; case MSM_ISP_IRQ_SET: - msm_camera_io_w_mb(irq0_mask, vfe_dev->vfe_base + 0x28); - msm_camera_io_w_mb(irq1_mask, vfe_dev->vfe_base + 0x2C); + vfe_dev->irq0_mask = irq0_mask; + vfe_dev->irq1_mask = irq1_mask; break; } + msm_camera_io_w_mb(irq0_mask, vfe_dev->vfe_base + 0x28); + msm_camera_io_w_mb(irq1_mask, vfe_dev->vfe_base + 0x2C); } static int32_t msm_vfe44_init_dt_parms(struct vfe_device *vfe_dev, @@ -181,10 +173,8 @@ static void msm_vfe44_init_hardware_reg(struct vfe_device *vfe_dev) /* BUS_CFG */ msm_camera_io_w(0x10000001, vfe_dev->vfe_base + 0x50); - vfe_dev->irq0_mask = 0xE00000F1; - vfe_dev->irq1_mask = 0xFFFFFFFF; - msm_vfe44_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe44_config_irq(vfe_dev, 0x800000E0, 0xFFFFFF7E, + MSM_ISP_IRQ_ENABLE); msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x30); msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x34); msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x24); @@ -193,9 +183,7 @@ static void msm_vfe44_init_hardware_reg(struct vfe_device *vfe_dev) static void msm_vfe44_clear_status_reg(struct vfe_device *vfe_dev) { - vfe_dev->irq0_mask = 0x80000000; - vfe_dev->irq1_mask = 0; - msm_vfe44_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, + msm_vfe44_config_irq(vfe_dev, 0x80000000, 0, MSM_ISP_IRQ_SET); msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x30); msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x34); @@ -419,7 +407,6 @@ static void msm_vfe44_read_irq_status(struct vfe_device *vfe_dev, if (*irq_status1 & (1 << 0)) { vfe_dev->error_info.camif_status = msm_camera_io_r(vfe_dev->vfe_base + 0x31C); - vfe_dev->irq1_mask &= ~(1 << 0); msm_vfe44_config_irq(vfe_dev, 0, (1 << 0), MSM_ISP_IRQ_DISABLE); } @@ -650,9 +637,8 @@ static void msm_vfe44_axi_cfg_comp_mask(struct vfe_device *vfe_dev, stream_composite_mask << (comp_mask_index * 8)); msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x40); - vfe_dev->irq0_mask |= 1 << (comp_mask_index + 25); - msm_vfe44_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe44_config_irq(vfe_dev, 1 << (comp_mask_index + 25), 0, + MSM_ISP_IRQ_ENABLE); } static void msm_vfe44_axi_clear_comp_mask(struct vfe_device *vfe_dev, @@ -664,25 +650,22 @@ static void msm_vfe44_axi_clear_comp_mask(struct vfe_device *vfe_dev, comp_mask &= ~(0x7F << (comp_mask_index * 8)); msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x40); - vfe_dev->irq0_mask &= ~(1 << (comp_mask_index + 25)); - msm_vfe44_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe44_config_irq(vfe_dev, (1 << (comp_mask_index + 25)), 0, + MSM_ISP_IRQ_DISABLE); } static void msm_vfe44_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info) { - vfe_dev->irq0_mask |= 1 << (stream_info->wm[0] + 8); - msm_vfe44_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe44_config_irq(vfe_dev, 1 << (stream_info->wm[0] + 8), 0, + MSM_ISP_IRQ_ENABLE); } static void msm_vfe44_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info) { - vfe_dev->irq0_mask &= ~(1 << (stream_info->wm[0] + 8)); - msm_vfe44_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe44_config_irq(vfe_dev, (1 << (stream_info->wm[0] + 8)), 0, + MSM_ISP_IRQ_DISABLE); } static void msm_vfe44_cfg_framedrop(void __iomem *vfe_base, @@ -918,10 +901,8 @@ static void msm_vfe44_cfg_fetch_engine(struct vfe_device *vfe_dev, temp |= (1 << 1); msm_camera_io_w(temp, vfe_dev->vfe_base + 0x50); - vfe_dev->irq0_mask &= 0xFEFFFFFF; - vfe_dev->irq0_mask |= (1 << 24); - msm_vfe44_config_irq(vfe_dev, vfe_dev->irq0_mask, - vfe_dev->irq1_mask, MSM_ISP_IRQ_SET); + msm_vfe44_config_irq(vfe_dev, (1 << 24), 0, + MSM_ISP_IRQ_SET); msm_camera_io_w((fe_cfg->fetch_height - 1) & 0xFFF, vfe_dev->vfe_base + 0x238); @@ -1045,13 +1026,12 @@ static void msm_vfe44_update_camif_state(struct vfe_device *vfe_dev, return; if (update_state == ENABLE_CAMIF) { - msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x30); - msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x34); + msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x30); + msm_camera_io_w_mb(0x81, vfe_dev->vfe_base + 0x34); msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x24); - vfe_dev->irq0_mask |= 0xF7; - msm_vfe44_config_irq(vfe_dev, vfe_dev->irq0_mask, - vfe_dev->irq1_mask, MSM_ISP_IRQ_SET); + msm_vfe44_config_irq(vfe_dev, 0xF7, 0x81, + MSM_ISP_IRQ_ENABLE); msm_camera_io_w_mb(0x140000, vfe_dev->vfe_base + 0x318); bus_en = @@ -1075,7 +1055,7 @@ static void msm_vfe44_update_camif_state(struct vfe_device *vfe_dev, if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN) update_state = DISABLE_CAMIF; msm_vfe44_config_irq(vfe_dev, 0, - 0, MSM_ISP_IRQ_SET); + 0x81, MSM_ISP_IRQ_DISABLE); val = msm_camera_io_r(vfe_dev->vfe_base + 0xC18); /* disable danger signal */ msm_camera_io_w_mb(val & ~(1 << 8), vfe_dev->vfe_base + 0xC18); @@ -1526,6 +1506,9 @@ static void msm_vfe44_stats_cfg_comp_mask( comp_mask_reg |= mask_bf_scale << (16 + request_comp_index * 8); atomic_set(stats_comp_mask, stats_mask | atomic_read(stats_comp_mask)); + msm_vfe44_config_irq(vfe_dev, + 1 << (request_comp_index + 29), 0, + MSM_ISP_IRQ_ENABLE); } else { if (!(atomic_read(stats_comp_mask) & stats_mask)) return; @@ -1540,6 +1523,9 @@ static void msm_vfe44_stats_cfg_comp_mask( ~stats_mask & atomic_read(stats_comp_mask)); comp_mask_reg &= ~(mask_bf_scale << (16 + request_comp_index * 8)); + msm_vfe44_config_irq(vfe_dev, + 1 << (request_comp_index + 29), 0, + MSM_ISP_IRQ_DISABLE); } msm_camera_io_w(comp_mask_reg, vfe_dev->vfe_base + 0x44); @@ -1555,20 +1541,18 @@ static void msm_vfe44_stats_cfg_wm_irq_mask( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info) { - vfe_dev->irq0_mask |= - 1 << (STATS_IDX(stream_info->stream_handle) + 15); - msm_vfe44_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe44_config_irq(vfe_dev, + 1 << (STATS_IDX(stream_info->stream_handle) + 15), 0, + MSM_ISP_IRQ_ENABLE); } static void msm_vfe44_stats_clear_wm_irq_mask( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info) { - vfe_dev->irq0_mask &= - ~(1 << (STATS_IDX(stream_info->stream_handle) + 15)); - msm_vfe44_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe44_config_irq(vfe_dev, + (1 << (STATS_IDX(stream_info->stream_handle) + 15)), 0, + MSM_ISP_IRQ_DISABLE); } static void msm_vfe44_stats_cfg_wm_reg( diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c index 9f815e65edc8..40bb044fde47 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c @@ -92,30 +92,24 @@ static void msm_vfe46_config_irq(struct vfe_device *vfe_dev, uint32_t irq0_mask, uint32_t irq1_mask, enum msm_isp_irq_operation oper) { - uint32_t val; - switch (oper) { case MSM_ISP_IRQ_ENABLE: - val = msm_camera_io_r(vfe_dev->vfe_base + 0x5C); - val |= irq0_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x5C); - val = msm_camera_io_r(vfe_dev->vfe_base + 0x60); - val |= irq1_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x60); + vfe_dev->irq0_mask |= irq0_mask; + vfe_dev->irq1_mask |= irq1_mask; break; case MSM_ISP_IRQ_DISABLE: - val = msm_camera_io_r(vfe_dev->vfe_base + 0x5C); - val &= ~irq0_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x5C); - val = msm_camera_io_r(vfe_dev->vfe_base + 0x60); - val &= ~irq1_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x60); + vfe_dev->irq0_mask &= ~irq0_mask; + vfe_dev->irq1_mask &= ~irq1_mask; break; case MSM_ISP_IRQ_SET: - msm_camera_io_w_mb(irq0_mask, vfe_dev->vfe_base + 0x5C); - msm_camera_io_w_mb(irq1_mask, vfe_dev->vfe_base + 0x60); + vfe_dev->irq0_mask = irq0_mask; + vfe_dev->irq1_mask = irq1_mask; break; } + msm_camera_io_w_mb(vfe_dev->irq0_mask, + vfe_dev->vfe_base + 0x5C); + msm_camera_io_w_mb(vfe_dev->irq1_mask, + vfe_dev->vfe_base + 0x60); } static int32_t msm_vfe46_init_dt_parms(struct vfe_device *vfe_dev, @@ -208,20 +202,16 @@ static void msm_vfe46_init_hardware_reg(struct vfe_device *vfe_dev) /* BUS_CFG */ msm_camera_io_w(0x00000001, vfe_dev->vfe_base + 0x84); /* IRQ_MASK/CLEAR */ - vfe_dev->irq0_mask = 0xE00000F1; - vfe_dev->irq1_mask = 0xE1FFFFFF; - msm_vfe46_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe46_config_irq(vfe_dev, 0x810000E0, 0xFFFFFF7E, + MSM_ISP_IRQ_ENABLE); msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x64); msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x68); + msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x58); } static void msm_vfe46_clear_status_reg(struct vfe_device *vfe_dev) { - vfe_dev->irq0_mask = 0x80000000; - vfe_dev->irq1_mask = 0x0; - msm_vfe46_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe46_config_irq(vfe_dev, 0x80000000, 0, MSM_ISP_IRQ_SET); msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x64); msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x68); msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x58); @@ -355,7 +345,6 @@ static void msm_vfe46_read_irq_status(struct vfe_device *vfe_dev, if (*irq_status1 & (1 << 0)) { vfe_dev->error_info.camif_status = msm_camera_io_r(vfe_dev->vfe_base + 0x3D0); - vfe_dev->irq1_mask &= ~(1 << 0); msm_vfe46_config_irq(vfe_dev, 0, (1 << 0), MSM_ISP_IRQ_DISABLE); } @@ -587,9 +576,8 @@ static void msm_vfe46_axi_cfg_comp_mask(struct vfe_device *vfe_dev, stream_composite_mask << (comp_mask_index * 8)); msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x74); - vfe_dev->irq0_mask |= 1 << (comp_mask_index + 25); - msm_vfe46_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe46_config_irq(vfe_dev, 1 << (comp_mask_index + 25), 0, + MSM_ISP_IRQ_ENABLE); } static void msm_vfe46_axi_clear_comp_mask(struct vfe_device *vfe_dev, @@ -601,25 +589,22 @@ static void msm_vfe46_axi_clear_comp_mask(struct vfe_device *vfe_dev, comp_mask &= ~(0x7F << (comp_mask_index * 8)); msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x74); - vfe_dev->irq0_mask |= 1 << (comp_mask_index + 25); - msm_vfe46_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe46_config_irq(vfe_dev, 1 << (comp_mask_index + 25), 0, + MSM_ISP_IRQ_DISABLE); } static void msm_vfe46_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info) { - vfe_dev->irq0_mask |= 1 << (stream_info->wm[0] + 8); - msm_vfe46_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe46_config_irq(vfe_dev, 1 << (stream_info->wm[0] + 8), 0, + MSM_ISP_IRQ_ENABLE); } static void msm_vfe46_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info) { - vfe_dev->irq0_mask &= ~(1 << (stream_info->wm[0] + 8)); - msm_vfe46_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe46_config_irq(vfe_dev, (1 << (stream_info->wm[0] + 8)), 0, + MSM_ISP_IRQ_DISABLE); } static void msm_vfe46_cfg_framedrop(void __iomem *vfe_base, @@ -857,10 +842,8 @@ static void msm_vfe46_cfg_fetch_engine(struct vfe_device *vfe_dev, temp |= (1 << 1); msm_camera_io_w(temp, vfe_dev->vfe_base + 0x84); - vfe_dev->irq0_mask &= 0xFEFFFFFF; - vfe_dev->irq0_mask |= (1 << 24); - msm_vfe46_config_irq(vfe_dev, vfe_dev->irq0_mask, - vfe_dev->irq1_mask, MSM_ISP_IRQ_SET); + msm_vfe46_config_irq(vfe_dev, 1 << 24, 0, + MSM_ISP_IRQ_ENABLE); temp = fe_cfg->fetch_height - 1; msm_camera_io_w(temp & 0x3FFF, vfe_dev->vfe_base + 0x278); @@ -1120,9 +1103,11 @@ static void msm_vfe46_update_camif_state(struct vfe_device *vfe_dev, return; if (update_state == ENABLE_CAMIF) { - vfe_dev->irq0_mask |= 0xF5; - msm_vfe46_config_irq(vfe_dev, vfe_dev->irq0_mask, - vfe_dev->irq1_mask, MSM_ISP_IRQ_SET); + msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x64); + msm_camera_io_w(0x81, vfe_dev->vfe_base + 0x68); + msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x58); + msm_vfe46_config_irq(vfe_dev, 0x15, 0x81, + MSM_ISP_IRQ_ENABLE); bus_en = ((vfe_dev->axi_data. @@ -1148,7 +1133,8 @@ static void msm_vfe46_update_camif_state(struct vfe_device *vfe_dev, if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN) update_state = DISABLE_CAMIF; - msm_vfe46_config_irq(vfe_dev, 0, 0, MSM_ISP_IRQ_SET); + msm_vfe46_config_irq(vfe_dev, 0, 0x81, + MSM_ISP_IRQ_DISABLE); /* disable danger signal */ val = msm_camera_io_r(vfe_dev->vfe_base + 0xC18); val &= ~(1 << 8); @@ -1611,6 +1597,9 @@ static void msm_vfe46_stats_cfg_comp_mask( comp_mask_reg |= mask_bf_scale << (16 + request_comp_index * 8); atomic_set(stats_comp_mask, stats_mask | atomic_read(stats_comp_mask)); + msm_vfe46_config_irq(vfe_dev, + 1 << (request_comp_index + 29), 0, + MSM_ISP_IRQ_ENABLE); } else { if (!(atomic_read(stats_comp_mask) & stats_mask)) return; @@ -1625,6 +1614,9 @@ static void msm_vfe46_stats_cfg_comp_mask( ~stats_mask & atomic_read(stats_comp_mask)); comp_mask_reg &= ~(mask_bf_scale << (16 + request_comp_index * 8)); + msm_vfe46_config_irq(vfe_dev, + 1 << (request_comp_index + 29), 0, + MSM_ISP_IRQ_DISABLE); } msm_camera_io_w(comp_mask_reg, vfe_dev->vfe_base + 0x78); @@ -1640,19 +1632,18 @@ static void msm_vfe46_stats_cfg_wm_irq_mask( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info) { - vfe_dev->irq0_mask |= 1 << (STATS_IDX(stream_info->stream_handle) + 15); - msm_vfe46_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe46_config_irq(vfe_dev, + 1 << (STATS_IDX(stream_info->stream_handle) + 15), 0, + MSM_ISP_IRQ_ENABLE); } static void msm_vfe46_stats_clear_wm_irq_mask( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info) { - vfe_dev->irq0_mask &= - ~(1 << (STATS_IDX(stream_info->stream_handle) + 15)); - msm_vfe46_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe46_config_irq(vfe_dev, + 1 << (STATS_IDX(stream_info->stream_handle) + 15), 0, + MSM_ISP_IRQ_DISABLE); } static void msm_vfe46_stats_cfg_wm_reg( diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c index 20aa69f322db..290f100ffeba 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c @@ -149,30 +149,24 @@ void msm_vfe47_config_irq(struct vfe_device *vfe_dev, uint32_t irq0_mask, uint32_t irq1_mask, enum msm_isp_irq_operation oper) { - uint32_t val; - switch (oper) { case MSM_ISP_IRQ_ENABLE: - val = msm_camera_io_r(vfe_dev->vfe_base + 0x5C); - val |= irq0_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x5C); - val = msm_camera_io_r(vfe_dev->vfe_base + 0x60); - val |= irq1_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x60); + vfe_dev->irq0_mask |= irq0_mask; + vfe_dev->irq1_mask |= irq1_mask; break; case MSM_ISP_IRQ_DISABLE: - val = msm_camera_io_r(vfe_dev->vfe_base + 0x5C); - val &= ~irq0_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x5C); - val = msm_camera_io_r(vfe_dev->vfe_base + 0x60); - val &= ~irq1_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x60); + vfe_dev->irq0_mask &= ~irq0_mask; + vfe_dev->irq1_mask &= ~irq1_mask; break; case MSM_ISP_IRQ_SET: - msm_camera_io_w_mb(irq0_mask, vfe_dev->vfe_base + 0x5C); - msm_camera_io_w_mb(irq1_mask, vfe_dev->vfe_base + 0x60); + vfe_dev->irq0_mask = irq0_mask; + vfe_dev->irq1_mask = irq1_mask; break; } + msm_camera_io_w_mb(vfe_dev->irq0_mask, + vfe_dev->vfe_base + 0x5C); + msm_camera_io_w_mb(vfe_dev->irq1_mask, + vfe_dev->vfe_base + 0x60); } static int32_t msm_vfe47_init_dt_parms(struct vfe_device *vfe_dev, @@ -285,13 +279,6 @@ int msm_vfe47_init_hardware(struct vfe_device *vfe_dev) else id = CAM_AHB_CLIENT_VFE1; - rc = cam_config_ahb_clk(NULL, 0, id, CAM_AHB_SVS_VOTE); - if (rc < 0) { - pr_err("%s: failed to vote for AHB\n", __func__); - goto ahb_vote_fail; - } - vfe_dev->ahb_vote = CAM_AHB_SVS_VOTE; - rc = vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators( vfe_dev, 1); if (rc) @@ -302,6 +289,13 @@ int msm_vfe47_init_hardware(struct vfe_device *vfe_dev) if (rc) goto clk_enable_failed; + rc = cam_config_ahb_clk(NULL, 0, id, CAM_AHB_SVS_VOTE); + if (rc < 0) { + pr_err("%s: failed to vote for AHB\n", __func__); + goto ahb_vote_fail; + } + vfe_dev->ahb_vote = CAM_AHB_SVS_VOTE; + vfe_dev->common_data->dual_vfe_res->vfe_base[vfe_dev->pdev->id] = vfe_dev->vfe_base; @@ -312,14 +306,14 @@ int msm_vfe47_init_hardware(struct vfe_device *vfe_dev) return rc; irq_enable_fail: vfe_dev->common_data->dual_vfe_res->vfe_base[vfe_dev->pdev->id] = NULL; - vfe_dev->hw_info->vfe_ops.platform_ops.enable_clks(vfe_dev, 0); -clk_enable_failed: - vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators(vfe_dev, 0); -enable_regulators_failed: if (cam_config_ahb_clk(NULL, 0, id, CAM_AHB_SUSPEND_VOTE) < 0) pr_err("%s: failed to remove vote for AHB\n", __func__); vfe_dev->ahb_vote = CAM_AHB_SUSPEND_VOTE; ahb_vote_fail: + vfe_dev->hw_info->vfe_ops.platform_ops.enable_clks(vfe_dev, 0); +clk_enable_failed: + vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators(vfe_dev, 0); +enable_regulators_failed: return rc; } @@ -338,9 +332,6 @@ void msm_vfe47_release_hardware(struct vfe_device *vfe_dev) msm_isp_flush_tasklet(vfe_dev); vfe_dev->common_data->dual_vfe_res->vfe_base[vfe_dev->pdev->id] = NULL; - vfe_dev->hw_info->vfe_ops.platform_ops.enable_clks( - vfe_dev, 0); - vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators(vfe_dev, 0); msm_isp_update_bandwidth(ISP_VFE0 + vfe_dev->pdev->id, 0, 0); @@ -351,7 +342,12 @@ void msm_vfe47_release_hardware(struct vfe_device *vfe_dev) if (cam_config_ahb_clk(NULL, 0, id, CAM_AHB_SUSPEND_VOTE) < 0) pr_err("%s: failed to vote for AHB\n", __func__); - vfe_dev->ahb_vote = CAM_AHB_SUSPEND_VOTE; + + vfe_dev->ahb_vote = CAM_AHB_SUSPEND_VOTE; + + vfe_dev->hw_info->vfe_ops.platform_ops.enable_clks( + vfe_dev, 0); + vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators(vfe_dev, 0); } void msm_vfe47_init_hardware_reg(struct vfe_device *vfe_dev) @@ -382,19 +378,16 @@ void msm_vfe47_init_hardware_reg(struct vfe_device *vfe_dev) /* BUS_CFG */ msm_camera_io_w(0x00000101, vfe_dev->vfe_base + 0x84); /* IRQ_MASK/CLEAR */ - vfe_dev->irq0_mask = 0xE00000F3; - vfe_dev->irq1_mask = 0xFFFFFFFF; - msm_vfe47_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe47_config_irq(vfe_dev, 0x810000E0, 0xFFFFFF7E, + MSM_ISP_IRQ_ENABLE); msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x64); msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x68); + msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x58); } void msm_vfe47_clear_status_reg(struct vfe_device *vfe_dev) { - vfe_dev->irq0_mask = 0x80000000; - vfe_dev->irq1_mask = 0x0; - msm_vfe47_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, + msm_vfe47_config_irq(vfe_dev, 0x80000000, 0x0, MSM_ISP_IRQ_SET); msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x64); msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x68); @@ -543,7 +536,6 @@ void msm_vfe47_read_irq_status(struct vfe_device *vfe_dev, vfe_dev->error_info.camif_status = msm_camera_io_r(vfe_dev->vfe_base + 0x4A4); /* mask off camif error after first occurrance */ - vfe_dev->irq1_mask &= ~(1 << 0); msm_vfe47_config_irq(vfe_dev, 0, (1 << 0), MSM_ISP_IRQ_DISABLE); } @@ -785,9 +777,8 @@ void msm_vfe47_axi_cfg_comp_mask(struct vfe_device *vfe_dev, stream_composite_mask << (comp_mask_index * 8)); msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x74); - vfe_dev->irq0_mask |= 1 << (comp_mask_index + 25); - msm_vfe47_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe47_config_irq(vfe_dev, 1 << (comp_mask_index + 25), 0, + MSM_ISP_IRQ_ENABLE); } void msm_vfe47_axi_clear_comp_mask(struct vfe_device *vfe_dev, @@ -799,25 +790,22 @@ void msm_vfe47_axi_clear_comp_mask(struct vfe_device *vfe_dev, comp_mask &= ~(0x7F << (comp_mask_index * 8)); msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x74); - vfe_dev->irq0_mask &= ~(1 << (comp_mask_index + 25)); - msm_vfe47_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe47_config_irq(vfe_dev, (1 << (comp_mask_index + 25)), 0, + MSM_ISP_IRQ_DISABLE); } void msm_vfe47_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info) { - vfe_dev->irq0_mask |= 1 << (stream_info->wm[0] + 8); - msm_vfe47_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe47_config_irq(vfe_dev, 1 << (stream_info->wm[0] + 8), 0, + MSM_ISP_IRQ_ENABLE); } void msm_vfe47_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info) { - vfe_dev->irq0_mask &= ~(1 << (stream_info->wm[0] + 8)); - msm_vfe47_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe47_config_irq(vfe_dev, (1 << (stream_info->wm[0] + 8)), 0, + MSM_ISP_IRQ_DISABLE); } void msm_vfe47_cfg_framedrop(void __iomem *vfe_base, @@ -1065,10 +1053,8 @@ void msm_vfe47_cfg_fetch_engine(struct vfe_device *vfe_dev, temp |= (1 << 1); msm_camera_io_w(temp, vfe_dev->vfe_base + 0x84); - vfe_dev->irq0_mask &= 0xFEFFFFFF; - vfe_dev->irq0_mask |= (1 << 24); - msm_vfe47_config_irq(vfe_dev, vfe_dev->irq0_mask, - vfe_dev->irq1_mask, MSM_ISP_IRQ_SET); + msm_vfe47_config_irq(vfe_dev, (1 << 24), 0, + MSM_ISP_IRQ_ENABLE); temp = fe_cfg->fetch_height - 1; msm_camera_io_w(temp & 0x3FFF, vfe_dev->vfe_base + 0x308); @@ -1394,9 +1380,11 @@ void msm_vfe47_update_camif_state(struct vfe_device *vfe_dev, val = msm_camera_io_r(vfe_dev->vfe_base + 0x47C); if (update_state == ENABLE_CAMIF) { - vfe_dev->irq0_mask |= 0xF5; - msm_vfe47_config_irq(vfe_dev, vfe_dev->irq0_mask, - vfe_dev->irq1_mask, MSM_ISP_IRQ_SET); + msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x64); + msm_camera_io_w(0x81, vfe_dev->vfe_base + 0x68); + msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x58); + msm_vfe47_config_irq(vfe_dev, 0x15, 0x81, + MSM_ISP_IRQ_ENABLE); if ((vfe_dev->hvx_cmd > HVX_DISABLE) && (vfe_dev->hvx_cmd <= HVX_ROUND_TRIP)) @@ -1427,8 +1415,9 @@ void msm_vfe47_update_camif_state(struct vfe_device *vfe_dev, /* For testgen always halt on camif boundary */ if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN) update_state = DISABLE_CAMIF; - /* turn off all irq before camif disable */ - msm_vfe47_config_irq(vfe_dev, 0, 0, MSM_ISP_IRQ_SET); + /* turn off camif violation and error irqs */ + msm_vfe47_config_irq(vfe_dev, 0, 0x81, + MSM_ISP_IRQ_DISABLE); val = msm_camera_io_r(vfe_dev->vfe_base + 0x464); /* disable danger signal */ msm_camera_io_w_mb(val & ~(1 << 8), vfe_dev->vfe_base + 0x464); @@ -1896,6 +1885,8 @@ void msm_vfe47_stats_cfg_comp_mask( comp_mask_reg |= stats_mask << (request_comp_index * 16); atomic_set(stats_comp_mask, stats_mask | atomic_read(stats_comp_mask)); + msm_vfe47_config_irq(vfe_dev, 1 << (29 + request_comp_index), + 0, MSM_ISP_IRQ_ENABLE); } else { if (!(atomic_read(stats_comp_mask) & stats_mask)) return; @@ -1903,6 +1894,8 @@ void msm_vfe47_stats_cfg_comp_mask( atomic_set(stats_comp_mask, ~stats_mask & atomic_read(stats_comp_mask)); comp_mask_reg &= ~(stats_mask << (request_comp_index * 16)); + msm_vfe47_config_irq(vfe_dev, 1 << (29 + request_comp_index), + 0, MSM_ISP_IRQ_DISABLE); } msm_camera_io_w(comp_mask_reg, vfe_dev->vfe_base + 0x78); @@ -1919,49 +1912,39 @@ void msm_vfe47_stats_cfg_wm_irq_mask( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info) { - uint32_t irq_mask; - uint32_t irq_mask_1; - - irq_mask = vfe_dev->irq0_mask; - irq_mask_1 = vfe_dev->irq1_mask; - switch (STATS_IDX(stream_info->stream_handle)) { case STATS_COMP_IDX_AEC_BG: - irq_mask |= 1 << 15; + msm_vfe47_config_irq(vfe_dev, 1 << 15, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_HDR_BE: - irq_mask |= 1 << 16; + msm_vfe47_config_irq(vfe_dev, 1 << 16, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_BG: - irq_mask |= 1 << 17; + msm_vfe47_config_irq(vfe_dev, 1 << 17, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_BF: - irq_mask |= 1 << 18; - irq_mask_1 |= 1 << 26; + msm_vfe47_config_irq(vfe_dev, 1 << 18, 1 << 26, + MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_HDR_BHIST: - irq_mask |= 1 << 19; + msm_vfe47_config_irq(vfe_dev, 1 << 19, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_RS: - irq_mask |= 1 << 20; + msm_vfe47_config_irq(vfe_dev, 1 << 20, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_CS: - irq_mask |= 1 << 21; + msm_vfe47_config_irq(vfe_dev, 1 << 21, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_IHIST: - irq_mask |= 1 << 22; + msm_vfe47_config_irq(vfe_dev, 1 << 22, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_BHIST: - irq_mask |= 1 << 23; + msm_vfe47_config_irq(vfe_dev, 1 << 23, 0, MSM_ISP_IRQ_ENABLE); break; default: pr_err("%s: Invalid stats idx %d\n", __func__, STATS_IDX(stream_info->stream_handle)); } - - msm_vfe47_config_irq(vfe_dev, irq_mask, irq_mask_1, MSM_ISP_IRQ_SET); - vfe_dev->irq0_mask = irq_mask; - vfe_dev->irq1_mask = irq_mask_1; } void msm_vfe47_stats_clear_wm_irq_mask( @@ -1975,41 +1958,37 @@ void msm_vfe47_stats_clear_wm_irq_mask( switch (STATS_IDX(stream_info->stream_handle)) { case STATS_COMP_IDX_AEC_BG: - irq_mask &= ~(1 << 15); + msm_vfe47_config_irq(vfe_dev, 1 << 15, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_HDR_BE: - irq_mask &= ~(1 << 16); + msm_vfe47_config_irq(vfe_dev, 1 << 16, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_BG: - irq_mask &= ~(1 << 17); + msm_vfe47_config_irq(vfe_dev, 1 << 17, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_BF: - irq_mask &= ~(1 << 18); - irq_mask_1 &= ~(1 << 26); + msm_vfe47_config_irq(vfe_dev, 1 << 18, 1 << 26, + MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_HDR_BHIST: - irq_mask &= ~(1 << 19); + msm_vfe47_config_irq(vfe_dev, 1 << 19, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_RS: - irq_mask &= ~(1 << 20); + msm_vfe47_config_irq(vfe_dev, 1 << 20, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_CS: - irq_mask &= ~(1 << 21); + msm_vfe47_config_irq(vfe_dev, 1 << 21, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_IHIST: - irq_mask &= ~(1 << 22); + msm_vfe47_config_irq(vfe_dev, 1 << 22, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_BHIST: - irq_mask &= ~(1 << 23); + msm_vfe47_config_irq(vfe_dev, 1 << 23, 0, MSM_ISP_IRQ_DISABLE); break; default: pr_err("%s: Invalid stats idx %d\n", __func__, STATS_IDX(stream_info->stream_handle)); } - - msm_vfe47_config_irq(vfe_dev, irq_mask, irq_mask_1, MSM_ISP_IRQ_SET); - vfe_dev->irq0_mask = irq_mask; - vfe_dev->irq1_mask = irq_mask_1; } void msm_vfe47_stats_cfg_wm_reg( diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c index 3dd55e02826d..8721fc18eaa8 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c @@ -1188,15 +1188,9 @@ int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg) vfe_dev->vt_enable = stream_cfg_cmd->vt_enable; msm_isp_start_avtimer(); } - if (stream_info->num_planes > 1) { + if (stream_info->num_planes > 1) msm_isp_axi_reserve_comp_mask( &vfe_dev->axi_data, stream_info); - vfe_dev->hw_info->vfe_ops.axi_ops. - cfg_comp_mask(vfe_dev, stream_info); - } else { - vfe_dev->hw_info->vfe_ops.axi_ops. - cfg_wm_irq_mask(vfe_dev, stream_info); - } for (i = 0; i < stream_info->num_planes; i++) { vfe_dev->hw_info->vfe_ops.axi_ops. @@ -1252,14 +1246,8 @@ int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg) clear_wm_xbar_reg(vfe_dev, stream_info, i); } - if (stream_info->num_planes > 1) { - vfe_dev->hw_info->vfe_ops.axi_ops. - clear_comp_mask(vfe_dev, stream_info); + if (stream_info->num_planes > 1) msm_isp_axi_free_comp_mask(&vfe_dev->axi_data, stream_info); - } else { - vfe_dev->hw_info->vfe_ops.axi_ops. - clear_wm_irq_mask(vfe_dev, stream_info); - } vfe_dev->hw_info->vfe_ops.axi_ops.clear_framedrop(vfe_dev, stream_info); msm_isp_axi_free_wm(axi_data, stream_info); @@ -2617,6 +2605,13 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev, return rc; } spin_unlock_irqrestore(&stream_info->lock, flags); + if (stream_info->num_planes > 1) { + vfe_dev->hw_info->vfe_ops.axi_ops. + cfg_comp_mask(vfe_dev, stream_info); + } else { + vfe_dev->hw_info->vfe_ops.axi_ops. + cfg_wm_irq_mask(vfe_dev, stream_info); + } stream_info->state = START_PENDING; @@ -2733,6 +2728,13 @@ static int msm_isp_stop_axi_stream(struct vfe_device *vfe_dev, spin_unlock_irqrestore(&stream_info->lock, flags); wait_for_complete_for_this_stream = 0; + if (stream_info->num_planes > 1) + vfe_dev->hw_info->vfe_ops.axi_ops. + clear_comp_mask(vfe_dev, stream_info); + else + vfe_dev->hw_info->vfe_ops.axi_ops. + clear_wm_irq_mask(vfe_dev, stream_info); + stream_info->state = STOP_PENDING; if (!halt && !ext_read && diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c index e98c99fcb62d..4aef6b5c7f38 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c @@ -444,10 +444,6 @@ int msm_isp_request_stats_stream(struct vfe_device *vfe_dev, void *arg) stream_info->framedrop_pattern = 0x1; stream_info->framedrop_period = framedrop_period - 1; - if (!stream_info->composite_flag) - vfe_dev->hw_info->vfe_ops.stats_ops. - cfg_wm_irq_mask(vfe_dev, stream_info); - if (stream_info->init_stats_frame_drop == 0) vfe_dev->hw_info->vfe_ops.stats_ops.cfg_wm_reg(vfe_dev, stream_info); @@ -485,10 +481,6 @@ int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg) rc = msm_isp_cfg_stats_stream(vfe_dev, &stream_cfg_cmd); } - if (!stream_info->composite_flag) - vfe_dev->hw_info->vfe_ops.stats_ops. - clear_wm_irq_mask(vfe_dev, stream_info); - vfe_dev->hw_info->vfe_ops.stats_ops.clear_wm_reg(vfe_dev, stream_info); memset(stream_info, 0, sizeof(struct msm_vfe_stats_stream)); return 0; @@ -711,6 +703,9 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev, pr_err("%s: No buffer for stream%d\n", __func__, idx); return rc; } + if (!stream_info->composite_flag) + vfe_dev->hw_info->vfe_ops.stats_ops. + cfg_wm_irq_mask(vfe_dev, stream_info); if (vfe_dev->axi_data.src_info[VFE_PIX_0].active) stream_info->state = STATS_START_PENDING; @@ -784,6 +779,10 @@ static int msm_isp_stop_stats_stream(struct vfe_device *vfe_dev, return -EINVAL; } + if (!stream_info->composite_flag) + vfe_dev->hw_info->vfe_ops.stats_ops. + clear_wm_irq_mask(vfe_dev, stream_info); + if (vfe_dev->axi_data.src_info[VFE_PIX_0].active) stream_info->state = STATS_STOP_PENDING; else 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/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c index 54cb04fcc4f0..d8c6e30204d1 100644 --- a/drivers/media/platform/msm/vidc/msm_vdec.c +++ b/drivers/media/platform/msm/vidc/msm_vdec.c @@ -615,6 +615,12 @@ static u32 get_frame_size_nv12_ubwc(int plane, u32 height, u32 width) return VENUS_BUFFER_SIZE(COLOR_FMT_NV12_UBWC, width, height); } +static u32 get_frame_size_compressed_full_yuv(int plane, + u32 max_mbs_per_frame, u32 size_per_mb) +{ + return (max_mbs_per_frame * size_per_mb * 3 / 2); +} + static u32 get_frame_size_compressed(int plane, u32 max_mbs_per_frame, u32 size_per_mb) { @@ -791,7 +797,7 @@ struct msm_vidc_format vdec_formats[] = { .description = "VP9 compressed format", .fourcc = V4L2_PIX_FMT_VP9, .num_planes = 1, - .get_frame_size = get_frame_size_compressed, + .get_frame_size = get_frame_size_compressed_full_yuv, .type = OUTPUT_PORT, }, { @@ -2835,4 +2841,3 @@ int msm_vdec_ctrl_init(struct msm_vidc_inst *inst) return msm_comm_ctrl_init(inst, msm_vdec_ctrls, ARRAY_SIZE(msm_vdec_ctrls), &msm_vdec_ctrl_ops); } - diff --git a/drivers/mfd/wcd9xxx-utils.c b/drivers/mfd/wcd9xxx-utils.c index 2160dfd063b1..22d61d96a11d 100644 --- a/drivers/mfd/wcd9xxx-utils.c +++ b/drivers/mfd/wcd9xxx-utils.c @@ -39,6 +39,10 @@ static enum wcd9xxx_intf_status wcd9xxx_intf = -1; static struct mfd_cell tavil_devs[] = { { + .name = "qcom-wcd-pinctrl", + .of_compatible = "qcom,wcd-pinctrl", + }, + { .name = "tavil_codec", }, }; 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/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c index 86f3a4800d1d..3f9eeabc5464 100644 --- a/drivers/net/wireless/wcnss/wcnss_wlan.c +++ b/drivers/net/wireless/wcnss/wcnss_wlan.c @@ -3339,7 +3339,7 @@ static ssize_t wcnss_wlan_write(struct file *fp, const char __user return -EFAULT; if ((UINT32_MAX - count < penv->user_cal_rcvd) || - MAX_CALIBRATED_DATA_SIZE < count + penv->user_cal_rcvd) { + (penv->user_cal_exp_size < count + penv->user_cal_rcvd)) { pr_err(DEVICE " invalid size to write %zu\n", count + penv->user_cal_rcvd); rc = -ENOMEM; diff --git a/drivers/of/of_batterydata.c b/drivers/of/of_batterydata.c index 5f140cd0c2a6..4410f270f557 100644 --- a/drivers/of/of_batterydata.c +++ b/drivers/of/of_batterydata.c @@ -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 @@ -312,32 +312,15 @@ static int64_t of_batterydata_convert_battery_id_kohm(int batt_id_uv, struct device_node *of_batterydata_get_best_profile( const struct device_node *batterydata_container_node, - const char *psy_name, const char *batt_type) + int batt_id_kohm, const char *batt_type) { struct batt_ids batt_ids; struct device_node *node, *best_node = NULL; - struct power_supply *psy; const char *battery_type = NULL; - union power_supply_propval ret = {0, }; int delta = 0, best_delta = 0, best_id_kohm = 0, id_range_pct, - batt_id_kohm = 0, i = 0, rc = 0, limit = 0; + i = 0, rc = 0, limit = 0; bool in_range = false; - psy = power_supply_get_by_name(psy_name); - if (!psy) { - pr_err("%s supply not found. defer\n", psy_name); - return ERR_PTR(-EPROBE_DEFER); - } - - rc = power_supply_get_property(psy, POWER_SUPPLY_PROP_RESISTANCE_ID, - &ret); - if (rc) { - pr_err("failed to retrieve resistance value rc=%d\n", rc); - return ERR_PTR(-ENOSYS); - } - - batt_id_kohm = ret.intval / 1000; - /* read battery id range percentage for best profile */ rc = of_property_read_u32(batterydata_container_node, "qcom,batt-id-range-pct", &id_range_pct); diff --git a/drivers/power/qcom-charger/qpnp-fg.c b/drivers/power/qcom-charger/qpnp-fg.c index 8660c1f8c3f5..0658f0d3b1eb 100644 --- a/drivers/power/qcom-charger/qpnp-fg.c +++ b/drivers/power/qcom-charger/qpnp-fg.c @@ -4772,8 +4772,7 @@ fail: #define BATTERY_PSY_WAIT_MS 2000 static int fg_batt_profile_init(struct fg_chip *chip) { - int rc = 0, ret; - int len; + int rc = 0, ret, len, batt_id; struct device_node *node = chip->pdev->dev.of_node; struct device_node *batt_node, *profile_node; const char *data, *batt_type_str; @@ -4802,14 +4801,16 @@ wait: goto no_profile; } + batt_id = get_sram_prop_now(chip, FG_DATA_BATT_ID); + batt_id /= 1000; if (fg_debug_mask & FG_STATUS) - pr_info("battery id = %d\n", - get_sram_prop_now(chip, FG_DATA_BATT_ID)); - profile_node = of_batterydata_get_best_profile(batt_node, "bms", + pr_info("battery id = %dKOhms\n", batt_id); + + profile_node = of_batterydata_get_best_profile(batt_node, batt_id, fg_batt_type); - if (!profile_node) { - pr_err("couldn't find profile handle\n"); - rc = -ENODATA; + if (IS_ERR_OR_NULL(profile_node)) { + rc = PTR_ERR(profile_node); + pr_err("couldn't find profile handle %d\n", rc); goto no_profile; } diff --git a/drivers/power/qcom-charger/qpnp-smb2.c b/drivers/power/qcom-charger/qpnp-smb2.c index 7810ecb9b15b..ad00a987ae41 100644 --- a/drivers/power/qcom-charger/qpnp-smb2.c +++ b/drivers/power/qcom-charger/qpnp-smb2.c @@ -25,54 +25,98 @@ #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, }, }; 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 +169,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 +535,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 +682,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; } diff --git a/drivers/power/qcom-charger/qpnp-smbcharger.c b/drivers/power/qcom-charger/qpnp-smbcharger.c index 2536f4ec5c15..6c1e58d046e8 100644 --- a/drivers/power/qcom-charger/qpnp-smbcharger.c +++ b/drivers/power/qcom-charger/qpnp-smbcharger.c @@ -3507,19 +3507,27 @@ static int smbchg_config_chg_battery_type(struct smbchg_chip *chip) if (chip->battery_type && !strcmp(prop.strval, chip->battery_type)) return 0; + chip->battery_type = prop.strval; batt_node = of_parse_phandle(node, "qcom,battery-data", 0); if (!batt_node) { pr_smb(PR_MISC, "No batterydata available\n"); return 0; } + rc = power_supply_get_property(chip->bms_psy, + POWER_SUPPLY_PROP_RESISTANCE_ID, &prop); + if (rc < 0) { + pr_smb(PR_STATUS, "Unable to read battery-id rc=%d\n", rc); + return 0; + } + profile_node = of_batterydata_get_best_profile(batt_node, - "bms", NULL); - if (!profile_node) { - pr_err("couldn't find profile handle\n"); - return -EINVAL; + prop.intval / 1000, NULL); + if (IS_ERR_OR_NULL(profile_node)) { + rc = PTR_ERR(profile_node); + pr_err("couldn't find profile handle %d\n", rc); + return rc; } - chip->battery_type = prop.strval; /* change vfloat */ rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv", diff --git a/drivers/power/qcom-charger/smb-lib.h b/drivers/power/qcom-charger/smb-lib.h index 8b3d00b6a5c1..1ee4945b625f 100644 --- a/drivers/power/qcom-charger/smb-lib.h +++ b/drivers/power/qcom-charger/smb-lib.h @@ -62,6 +62,12 @@ 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 parallel_params { 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/power/qcom-charger/smb138x-charger.c b/drivers/power/qcom-charger/smb138x-charger.c index f092f5a580b5..9a6baff27dac 100644 --- a/drivers/power/qcom-charger/smb138x-charger.c +++ b/drivers/power/qcom-charger/smb138x-charger.c @@ -423,7 +423,7 @@ static int smb138x_parallel_prop_is_writeable(struct power_supply *psy, static const struct power_supply_desc parallel_psy_desc = { .name = "parallel", - .type = POWER_SUPPLY_TYPE_BATTERY, + .type = POWER_SUPPLY_TYPE_USB_PARALLEL, .properties = smb138x_parallel_props, .num_properties = ARRAY_SIZE(smb138x_parallel_props), .get_property = smb138x_parallel_get_prop, diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c index c13e811a5d71..78c5c47e4e8b 100644 --- a/drivers/regulator/cpr3-regulator.c +++ b/drivers/regulator/cpr3-regulator.c @@ -1109,10 +1109,18 @@ static int cpr3_regulator_init_cprh_corners(struct cpr3_regulator *vreg) } if (ro_sel == INT_MAX) { - cpr3_err(vreg, "corner=%d has invalid RO select value\n", - i); - rc = -EINVAL; - goto free_base_quots; + if (!corner->proc_freq) { + /* + * Corner is not used as active DCVS set point + * select RO 0 arbitrarily. + */ + ro_sel = 0; + } else { + cpr3_err(vreg, "corner=%d has invalid RO select value\n", + i); + rc = -EINVAL; + goto free_base_quots; + } } open_loop_volt_steps = DIV_ROUND_UP(corner->open_loop_volt - @@ -1121,9 +1129,11 @@ static int cpr3_regulator_init_cprh_corners(struct cpr3_regulator *vreg) floor_volt_steps = DIV_ROUND_UP(corner->floor_volt - ctrl->base_volt, ctrl->step_volt); - delta_quot_steps = DIV_ROUND_UP(corner->target_quot[ro_sel] - - base_quots[ro_sel], - CPRH_DELTA_QUOT_STEP_FACTOR); + delta_quot_steps = corner->proc_freq ? + DIV_ROUND_UP(corner->target_quot[ro_sel] - + base_quots[ro_sel], + CPRH_DELTA_QUOT_STEP_FACTOR) : + 0; if (open_loop_volt_steps > CPRH_CORNER_INIT_VOLTAGE_MAX_VALUE || floor_volt_steps > CPRH_CORNER_FLOOR_VOLTAGE_MAX_VALUE || diff --git a/drivers/regulator/cpr3-regulator.h b/drivers/regulator/cpr3-regulator.h index d750b70519d1..3ddc1dc3c982 100644 --- a/drivers/regulator/cpr3-regulator.h +++ b/drivers/regulator/cpr3-regulator.h @@ -572,7 +572,13 @@ struct cpr3_panic_regs_info { * when hardware closed-loop attempts to exceed the ceiling * voltage * @apm: Handle to the array power mux (APM) - * @apm_threshold_volt: APM threshold voltage in microvolts + * @apm_threshold_volt: Voltage in microvolts which defines the threshold + * voltage to determine the APM supply selection for + * each corner + * @apm_crossover_volt: Voltage in microvolts corresponding to the voltage that + * the VDD supply must be set to while an APM switch is in + * progress. This element must be initialized for CPRh + * controllers when an APM threshold voltage is defined * @apm_adj_volt: Minimum difference between APM threshold voltage and * open-loop voltage which allows the APM threshold voltage * to be used as a ceiling @@ -736,6 +742,7 @@ struct cpr3_controller { int ceiling_irq; struct msm_apm_ctrl_dev *apm; int apm_threshold_volt; + int apm_crossover_volt; int apm_adj_volt; enum msm_apm_supply apm_high_supply; enum msm_apm_supply apm_low_supply; diff --git a/drivers/regulator/cprh-kbss-regulator.c b/drivers/regulator/cprh-kbss-regulator.c index ffd3db1a6dff..dfdd6921fed5 100644 --- a/drivers/regulator/cprh-kbss-regulator.c +++ b/drivers/regulator/cprh-kbss-regulator.c @@ -697,61 +697,38 @@ free_temp: } /** - * cprh_kbss_apm_threshold_as_corner() - introduce a corner whose floor, open-loop, - * and ceiling voltages correspond to the APM threshold voltage. + * cprh_kbss_apm_crossover_as_corner() - introduce a corner whose floor, + * open-loop, and ceiling voltages correspond to the APM + * crossover voltage. * @vreg: Pointer to the CPR3 regulator * * The APM corner is utilized as a crossover corner by OSM and CPRh - * hardware to determine the correct APM supply selection for the - * rest of the corners. This function must be called after all other - * functions which load per-corner values. + * hardware to set the VDD supply voltage during the APM switch + * routine. * * Return: 0 on success, errno on failure */ -static int cprh_kbss_apm_threshold_as_corner(struct cpr3_regulator *vreg) +static int cprh_kbss_apm_crossover_as_corner(struct cpr3_regulator *vreg) { struct cpr3_controller *ctrl = vreg->thread->ctrl; struct cpr3_corner *corner; - struct cprh_corner_band *corner_band; - int i, threshold, apm_corner = 0; - if (!ctrl->apm_threshold_volt) { - /* APM voltage threshold corner not required. */ + if (!ctrl->apm_crossover_volt) { + /* APM voltage crossover corner not required. */ return 0; } - threshold = ctrl->apm_threshold_volt; - vreg->corner_count++; - - for (i = vreg->corner_count - 1; i >= 1; i--) { - corner = &vreg->corner[i]; - - if (threshold >= vreg->corner[i - 1].open_loop_volt) { - apm_corner = i; - break; - } - - memcpy(corner, &vreg->corner[i - 1], sizeof(*corner)); - } - - corner = &vreg->corner[apm_corner]; - corner->proc_freq = 0; - corner->floor_volt = threshold; - corner->ceiling_volt = threshold; - corner->open_loop_volt = threshold; - corner->use_open_loop = true; - cpr3_info(vreg, "APM threshold corner=%d, open-loop=%d\n", - apm_corner, threshold); - + corner = &vreg->corner[vreg->corner_count]; /* - * Update corner band mappings to account for the inserted - * APM crossover corner. + * 0 MHz indicates this corner is not to be + * used as active DCVS set point. */ - for (i = 0; i < vreg->corner_band_count; i++) { - corner_band = &vreg->corner_band[i]; - if (corner_band->corner >= apm_corner) - corner_band->corner++; - } + corner->proc_freq = 0; + corner->floor_volt = ctrl->apm_crossover_volt; + corner->ceiling_volt = ctrl->apm_crossover_volt; + corner->open_loop_volt = ctrl->apm_crossover_volt; + corner->use_open_loop = true; + vreg->corner_count++; return 0; } @@ -1203,9 +1180,9 @@ static int cprh_kbss_init_regulator(struct cpr3_regulator *vreg) return -EINVAL; } - rc = cprh_kbss_apm_threshold_as_corner(vreg); + rc = cprh_kbss_apm_crossover_as_corner(vreg); if (rc) { - cpr3_err(vreg, "unable to introduce APM voltage threshold corner\n, rc=%d\n", + cpr3_err(vreg, "unable to introduce APM voltage crossover corner, rc=%d\n", rc); return rc; } @@ -1288,8 +1265,18 @@ static int cprh_kbss_init_controller(struct cpr3_controller *ctrl) rc = of_property_read_u32(ctrl->dev->of_node, "qcom,apm-threshold-voltage", &ctrl->apm_threshold_volt); - if (rc) + if (rc) { cpr3_debug(ctrl, "qcom,apm-threshold-voltage not specified\n"); + } else { + rc = of_property_read_u32(ctrl->dev->of_node, + "qcom,apm-crossover-voltage", + &ctrl->apm_crossover_volt); + if (rc) { + cpr3_err(ctrl, "error reading property qcom,apm-crossover-voltage, rc=%d\n", + rc); + return rc; + } + } of_property_read_u32(ctrl->dev->of_node, "qcom,apm-hysteresis-voltage", &ctrl->apm_adj_volt); 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/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index bb3f092f6de9..36b9a9435910 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 diff --git a/drivers/soc/qcom/glink_private.h b/drivers/soc/qcom/glink_private.h index 2f064e546f48..cdd6988418f7 100644 --- a/drivers/soc/qcom/glink_private.h +++ b/drivers/soc/qcom/glink_private.h @@ -19,6 +19,7 @@ #include <linux/kernel.h> #include <linux/kref.h> #include <linux/ratelimit.h> +#include <linux/sched.h> #include <linux/seq_file.h> #include <linux/spinlock.h> #include <linux/types.h> diff --git a/drivers/soc/qcom/glink_smem_native_xprt.c b/drivers/soc/qcom/glink_smem_native_xprt.c index d7d08dc588e5..84f346385f18 100644 --- a/drivers/soc/qcom/glink_smem_native_xprt.c +++ b/drivers/soc/qcom/glink_smem_native_xprt.c @@ -2191,6 +2191,8 @@ static int subsys_name_to_id(const char *name) return SMEM_WCNSS; if (!strcmp(name, "spss")) return SMEM_SPSS; + if (!strcmp(name, "cdsp")) + return SMEM_CDSP; return -ENODEV; } diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c index 37c73401a1c6..e4d235957981 100644 --- a/drivers/soc/qcom/service-locator.c +++ b/drivers/soc/qcom/service-locator.c @@ -39,15 +39,11 @@ #define LOCATOR_NOT_PRESENT 0 #define LOCATOR_PRESENT 1 -#define LOCATOR_UNKNOWN -1 -static u32 locator_status = LOCATOR_UNKNOWN; +static u32 locator_status = LOCATOR_NOT_PRESENT; static bool service_inited; -int enable; -module_param(enable, int, 0); - -DECLARE_COMPLETION(locator_status_known); +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); @@ -70,47 +66,6 @@ struct pd_qmi_data service_locator; /* Please refer soc/qcom/service-locator.h for use about APIs defined here */ -static ssize_t show_service_locator_status(struct class *cl, - struct class_attribute *attr, - char *buf) -{ - return scnprintf(buf, PAGE_SIZE, "%x\n", locator_status); -} - -static ssize_t store_service_locator_status(struct class *cl, - struct class_attribute *attr, - const char *buf, size_t size) -{ - u32 val; - - if (kstrtos32(buf, 10, &val) < 0) - goto err; - if (val != LOCATOR_NOT_PRESENT && val != LOCATOR_PRESENT) - goto err; - - mutex_lock(&service_init_mutex); - locator_status = val; - complete_all(&locator_status_known); - mutex_unlock(&service_init_mutex); - return size; -err: - pr_err("Invalid input parameters\n"); - return -EINVAL; -} - -static struct class_attribute service_locator_class_attr[] = { - __ATTR(service_locator_status, S_IRUGO | S_IWUSR, - show_service_locator_status, - store_service_locator_status), - __ATTR_NULL, -}; - -static struct class service_locator_class = { - .name = "service_locator", - .owner = THIS_MODULE, - .class_attrs = service_locator_class_attr, -}; - static int service_locator_svc_event_notify(struct notifier_block *this, unsigned long code, void *_cmd) @@ -338,30 +293,13 @@ static int init_service_locator(void) mutex_lock(&service_init_mutex); if (locator_status == LOCATOR_NOT_PRESENT) { - pr_err("Service Locator not present\n"); + pr_err("Service Locator not enabled\n"); rc = -ENODEV; goto inited; } if (service_inited) goto inited; - if (locator_status != LOCATOR_PRESENT) { - mutex_unlock(&service_init_mutex); - rc = wait_for_completion_timeout(&locator_status_known, - msecs_to_jiffies(INITIAL_TIMEOUT)); - if (!rc) { - locator_status = LOCATOR_NOT_PRESENT; - pr_err("Timed out waiting for Service Locator\n"); - return -ENODEV; - } - mutex_lock(&service_init_mutex); - if (locator_status == LOCATOR_NOT_PRESENT) { - pr_err("Service Locator not present\n"); - rc = -ENODEV; - goto inited; - } - } - service_locator.notifier.notifier_call = service_locator_svc_event_notify; init_completion(&service_locator.service_available); @@ -509,10 +447,7 @@ static struct dentry *test_servloc_file; static int __init service_locator_init(void) { - if (!enable) - locator_status = LOCATOR_NOT_PRESENT; - - class_register(&service_locator_class); + 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); @@ -523,7 +458,6 @@ static int __init service_locator_init(void) static void __exit service_locator_exit(void) { - class_unregister(&service_locator_class); debugfs_remove(test_servloc_file); } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 7087b5744eef..35994b827549 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1787,6 +1787,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (is_on) { + dbg_event(0xFF, "Pullup_enable", is_on); if (dwc->revision <= DWC3_REVISION_187A) { reg &= ~DWC3_DCTL_TRGTULST_MASK; reg |= DWC3_DCTL_TRGTULST_RX_DET; @@ -1824,6 +1825,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) dwc->pullups_connected = true; } else { + dbg_event(0xFF, "Pullup_disable", is_on); dwc3_gadget_disable_irq(dwc); __dwc3_gadget_ep_disable(dwc->eps[0]); __dwc3_gadget_ep_disable(dwc->eps[1]); @@ -1849,8 +1851,15 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) break; } timeout--; - if (!timeout) + if (!timeout) { + dev_err(dwc->dev, "failed to %s controller\n", + is_on ? "start" : "stop"); + if (is_on) + dbg_event(0xFF, "STARTTOUT", reg); + else + dbg_event(0xFF, "STOPTOUT", reg); return -ETIMEDOUT; + } udelay(1); } while (1); 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/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.c b/drivers/video/fbdev/msm/mdss_mdp.c index 164bf0273597..5a355f226179 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -1638,9 +1638,16 @@ static int mdss_mdp_gdsc_notifier_call(struct notifier_block *self, if (event & REGULATOR_EVENT_ENABLE) { __mdss_restore_sec_cfg(mdata); } else if (event & REGULATOR_EVENT_PRE_DISABLE) { - pr_debug("mdss gdsc is getting disabled\n"); - /* halt the vbif transactions */ - mdss_mdp_vbif_axi_halt(mdata); + int active_cnt = atomic_read(&mdata->active_intf_cnt); + + pr_debug("mdss gdsc is getting disabled, active_cnt=%d\n", + active_cnt); + /* + * halt the vbif transactions only if we have any active + * overlay session + */ + if (active_cnt) + mdss_mdp_vbif_axi_halt(mdata); } return NOTIFY_OK; diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index f97a9f9a9adc..cd8df78bc8c0 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -561,7 +561,8 @@ static u32 __calc_qseed3_mdp_clk_rate(struct mdss_mdp_pipe *pipe, u64 active_line; u64 backfill_line; - ver_dwnscale = ((u64)src_h << PHASE_STEP_SHIFT) / dst.h; + ver_dwnscale = (u64)src_h << PHASE_STEP_SHIFT; + do_div(ver_dwnscale, dst.h); if (ver_dwnscale > (MDSS_MDP_QSEED3_VER_DOWNSCALE_LIM << PHASE_STEP_SHIFT)) { 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/linux/of_batterydata.h b/include/linux/of_batterydata.h index fe2c996de264..5505371488d0 100644 --- a/include/linux/of_batterydata.h +++ b/include/linux/of_batterydata.h @@ -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 @@ -39,10 +39,7 @@ int of_batterydata_read_data(struct device_node *container_node, * of_batterydata_get_best_profile() - Find matching battery data device node * @batterydata_container_node: pointer to the battery-data container device * node containing the profile nodes. - * @psy_name: Name of the power supply which holds the - * POWER_SUPPLY_RESISTANCE_ID value to be used to match - * against the id resistances specified in the corresponding - * battery data profiles. + * @batt_id_kohm: Battery ID in KOhms for which we want to find the profile. * @batt_type: Battery type which we want to force load the profile. * * This routine returns a device_node pointer to the closest match battery data @@ -50,7 +47,7 @@ int of_batterydata_read_data(struct device_node *container_node, */ struct device_node *of_batterydata_get_best_profile( struct device_node *batterydata_container_node, - const char *psy_name, const char *batt_type); + int batt_id_kohm, const char *batt_type); #else static inline int of_batterydata_read_data(struct device_node *container_node, struct bms_battery_data *batt_data, @@ -60,7 +57,7 @@ static inline int of_batterydata_read_data(struct device_node *container_node, } static inline struct device_node *of_batterydata_get_best_profile( struct device_node *batterydata_container_node, - struct device_node *best_node, const char *psy_name) + int batt_id_kohm, const char *batt_type) { return -ENXIO; } 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/smem.h b/include/soc/qcom/smem.h index 9295532dec8a..79bcc1b31cf8 100644 --- a/include/soc/qcom/smem.h +++ b/include/soc/qcom/smem.h @@ -26,6 +26,7 @@ enum { SMEM_TZ, SMEM_SPSS, SMEM_HYP, + SMEM_CDSP, NUM_SMEM_SUBSYSTEMS, }; diff --git a/include/uapi/linux/msm_kgsl.h b/include/uapi/linux/msm_kgsl.h index 34503420c882..dbba773cd49d 100644 --- a/include/uapi/linux/msm_kgsl.h +++ b/include/uapi/linux/msm_kgsl.h @@ -43,13 +43,13 @@ /* This is a cmdbatch exclusive flag - use the CMDBATCH equivalent instead */ #define KGSL_CONTEXT_SYNC 0x00000400 #define KGSL_CONTEXT_PWR_CONSTRAINT 0x00000800 - #define KGSL_CONTEXT_PRIORITY_MASK 0x0000F000 #define KGSL_CONTEXT_PRIORITY_SHIFT 12 #define KGSL_CONTEXT_PRIORITY_UNDEF 0 #define KGSL_CONTEXT_IFH_NOP 0x00010000 #define KGSL_CONTEXT_SECURE 0x00020000 +#define KGSL_CONTEXT_NO_SNAPSHOT 0x00040000 #define KGSL_CONTEXT_PREEMPT_STYLE_MASK 0x0E000000 #define KGSL_CONTEXT_PREEMPT_STYLE_SHIFT 25 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/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/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 6d75693e5c4d..f46057d027e0 100755 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -129,6 +129,7 @@ #define WCD9335_DEC_PWR_LVL_LP 0x02 #define WCD9335_DEC_PWR_LVL_HP 0x04 #define WCD9335_DEC_PWR_LVL_DF 0x00 +#define WCD9335_STRING_LEN 100 #define CALCULATE_VOUT_D(req_mv) (((req_mv - 650) * 10) / 25) @@ -786,7 +787,7 @@ struct tasha_priv { /* to track the status */ unsigned long status_mask; - struct work_struct swr_add_devices_work; + struct work_struct tasha_add_child_devices_work; struct wcd_swr_ctrl_platform_data swr_plat_data; /* Port values for Rx and Tx codec_dai */ @@ -13464,7 +13465,7 @@ static int tasha_swrm_handle_irq(void *handle, return ret; } -static void wcd_swr_ctrl_add_devices(struct work_struct *work) +static void tasha_add_child_devices(struct work_struct *work) { struct tasha_priv *tasha; struct platform_device *pdev; @@ -13473,9 +13474,10 @@ static void wcd_swr_ctrl_add_devices(struct work_struct *work) struct tasha_swr_ctrl_data *swr_ctrl_data = NULL, *temp; int ret, ctrl_num = 0; struct wcd_swr_ctrl_platform_data *platdata; + char plat_dev_name[WCD9335_STRING_LEN]; tasha = container_of(work, struct tasha_priv, - swr_add_devices_work); + tasha_add_child_devices_work); if (!tasha) { pr_err("%s: Memory for WCD9335 does not exist\n", __func__); @@ -13496,17 +13498,17 @@ static void wcd_swr_ctrl_add_devices(struct work_struct *work) platdata = &tasha->swr_plat_data; for_each_child_of_node(wcd9xxx->dev->of_node, node) { - temp = krealloc(swr_ctrl_data, - (ctrl_num + 1) * sizeof(struct tasha_swr_ctrl_data), - GFP_KERNEL); - if (!temp) { - dev_err(wcd9xxx->dev, "out of memory\n"); - ret = -ENOMEM; - goto err; - } - swr_ctrl_data = temp; - swr_ctrl_data[ctrl_num].swr_pdev = NULL; - pdev = platform_device_alloc("tasha_swr_ctrl", -1); + if (!strcmp(node->name, "swr_master")) + strlcpy(plat_dev_name, "tasha_swr_ctrl", + (WCD9335_STRING_LEN - 1)); + else if (strnstr(node->name, "msm_cdc_pinctrl", + strlen("msm_cdc_pinctrl")) != NULL) + strlcpy(plat_dev_name, node->name, + (WCD9335_STRING_LEN - 1)); + else + continue; + + pdev = platform_device_alloc(plat_dev_name, -1); if (!pdev) { dev_err(wcd9xxx->dev, "%s: pdev memory alloc failed\n", __func__); @@ -13516,28 +13518,45 @@ static void wcd_swr_ctrl_add_devices(struct work_struct *work) pdev->dev.parent = tasha->dev; pdev->dev.of_node = node; - ret = platform_device_add_data(pdev, platdata, - sizeof(*platdata)); - if (ret) { - dev_err(&pdev->dev, "%s: cannot add plat data for ctrl:%d\n", - __func__, ctrl_num); - goto fail_pdev_add; + if (!strcmp(node->name, "swr_master")) { + ret = platform_device_add_data(pdev, platdata, + sizeof(*platdata)); + if (ret) { + dev_err(&pdev->dev, + "%s: cannot add plat data ctrl:%d\n", + __func__, ctrl_num); + goto fail_pdev_add; + } } ret = platform_device_add(pdev); if (ret) { - dev_err(&pdev->dev, "%s: Cannot add swr platform device\n", + dev_err(&pdev->dev, + "%s: Cannot add platform device\n", __func__); goto fail_pdev_add; } - swr_ctrl_data[ctrl_num].swr_pdev = pdev; - ctrl_num++; - dev_dbg(&pdev->dev, "%s: Added soundwire ctrl device(s)\n", - __func__); + if (!strcmp(node->name, "swr_master")) { + temp = krealloc(swr_ctrl_data, + (ctrl_num + 1) * sizeof( + struct tasha_swr_ctrl_data), + GFP_KERNEL); + if (!temp) { + dev_err(wcd9xxx->dev, "out of memory\n"); + ret = -ENOMEM; + goto err; + } + swr_ctrl_data = temp; + swr_ctrl_data[ctrl_num].swr_pdev = pdev; + ctrl_num++; + dev_dbg(&pdev->dev, + "%s: Added soundwire ctrl device(s)\n", + __func__); + tasha->nr = ctrl_num; + tasha->swr_ctrl_data = swr_ctrl_data; + } } - tasha->nr = ctrl_num; - tasha->swr_ctrl_data = swr_ctrl_data; return; fail_pdev_add: @@ -13637,7 +13656,8 @@ static int tasha_probe(struct platform_device *pdev) INIT_DELAYED_WORK(&tasha->power_gate_work, tasha_codec_power_gate_work); mutex_init(&tasha->power_lock); mutex_init(&tasha->sido_lock); - INIT_WORK(&tasha->swr_add_devices_work, wcd_swr_ctrl_add_devices); + INIT_WORK(&tasha->tasha_add_child_devices_work, + tasha_add_child_devices); BLOCKING_INIT_NOTIFIER_HEAD(&tasha->notifier); mutex_init(&tasha->micb_lock); mutex_init(&tasha->swr_read_lock); @@ -13713,7 +13733,7 @@ static int tasha_probe(struct platform_device *pdev) } /* Update codec register default values */ tasha_update_reg_defaults(tasha); - schedule_work(&tasha->swr_add_devices_work); + schedule_work(&tasha->tasha_add_child_devices_work); tasha_get_codec_ver(tasha); dev_info(&pdev->dev, "%s: Tasha driver probe done\n", __func__); diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c index 6b2ef88c7163..75387b7c2069 100644 --- a/sound/soc/codecs/wcd934x/wcd934x.c +++ b/sound/soc/codecs/wcd934x/wcd934x.c @@ -108,6 +108,7 @@ static const struct snd_kcontrol_new name##_mux = \ #define WCD934X_DEC_PWR_LVL_LP 0x02 #define WCD934X_DEC_PWR_LVL_HP 0x04 #define WCD934X_DEC_PWR_LVL_DF 0x00 +#define WCD934X_STRING_LEN 100 #define WCD934X_MAX_MICBIAS 4 #define DAPM_MICBIAS1_STANDALONE "MIC BIAS1 Standalone" @@ -470,7 +471,7 @@ struct tavil_priv { struct clk *wcd_ext_clk; struct mutex codec_mutex; - struct work_struct wcd_add_child_devices_work; + struct work_struct tavil_add_child_devices_work; struct hpf_work tx_hpf_work[WCD934X_NUM_DECIMATORS]; struct tx_mute_work tx_mute_dwork[WCD934X_NUM_DECIMATORS]; }; @@ -5551,7 +5552,7 @@ static int tavil_swrm_handle_irq(void *handle, return ret; } -static void wcd_add_child_devices(struct work_struct *work) +static void tavil_add_child_devices(struct work_struct *work) { struct tavil_priv *tavil; struct platform_device *pdev; @@ -5560,9 +5561,10 @@ static void wcd_add_child_devices(struct work_struct *work) struct tavil_swr_ctrl_data *swr_ctrl_data = NULL, *temp; int ret, ctrl_num = 0; struct wcd_swr_ctrl_platform_data *platdata; + char plat_dev_name[WCD934X_STRING_LEN]; tavil = container_of(work, struct tavil_priv, - wcd_add_child_devices_work); + tavil_add_child_devices_work); if (!tavil) { pr_err("%s: Memory for WCD934X does not exist\n", __func__); @@ -5583,17 +5585,17 @@ static void wcd_add_child_devices(struct work_struct *work) platdata = &tavil->swr.plat_data; for_each_child_of_node(wcd9xxx->dev->of_node, node) { - temp = krealloc(swr_ctrl_data, - (ctrl_num + 1) * sizeof(struct tavil_swr_ctrl_data), - GFP_KERNEL); - if (!temp) { - dev_err(wcd9xxx->dev, "out of memory\n"); - ret = -ENOMEM; - goto err_mem; - } - swr_ctrl_data = temp; - swr_ctrl_data[ctrl_num].swr_pdev = NULL; - pdev = platform_device_alloc("tavil_swr_ctrl", -1); + if (!strcmp(node->name, "swr_master")) + strlcpy(plat_dev_name, "tavil_swr_ctrl", + (WCD934X_STRING_LEN - 1)); + else if (strnstr(node->name, "msm_cdc_pinctrl", + strlen("msm_cdc_pinctrl")) != NULL) + strlcpy(plat_dev_name, node->name, + (WCD934X_STRING_LEN - 1)); + else + continue; + + pdev = platform_device_alloc(plat_dev_name, -1); if (!pdev) { dev_err(wcd9xxx->dev, "%s: pdev memory alloc failed\n", __func__); @@ -5603,34 +5605,51 @@ static void wcd_add_child_devices(struct work_struct *work) pdev->dev.parent = tavil->dev; pdev->dev.of_node = node; - ret = platform_device_add_data(pdev, platdata, - sizeof(*platdata)); - if (ret) { - dev_err(&pdev->dev, "%s: cannot add plat data for ctrl:%d\n", - __func__, ctrl_num); - goto err_pdev_add; + if (strcmp(node->name, "swr_master") == 0) { + ret = platform_device_add_data(pdev, platdata, + sizeof(*platdata)); + if (ret) { + dev_err(&pdev->dev, + "%s: cannot add plat data ctrl:%d\n", + __func__, ctrl_num); + goto err_pdev_add; + } } ret = platform_device_add(pdev); if (ret) { - dev_err(&pdev->dev, "%s: Cannot add swr platform device\n", + dev_err(&pdev->dev, + "%s: Cannot add platform device\n", __func__); goto err_pdev_add; } - swr_ctrl_data[ctrl_num].swr_pdev = pdev; - ctrl_num++; - dev_dbg(&pdev->dev, "%s: Added soundwire ctrl device(s)\n", - __func__); + if (strcmp(node->name, "swr_master") == 0) { + temp = krealloc(swr_ctrl_data, + (ctrl_num + 1) * sizeof( + struct tavil_swr_ctrl_data), + GFP_KERNEL); + if (!temp) { + dev_err(wcd9xxx->dev, "out of memory\n"); + ret = -ENOMEM; + goto err_pdev_add; + } + swr_ctrl_data = temp; + swr_ctrl_data[ctrl_num].swr_pdev = pdev; + ctrl_num++; + dev_dbg(&pdev->dev, + "%s: Added soundwire ctrl device(s)\n", + __func__); + tavil->swr.ctrl_data = swr_ctrl_data; + } } - tavil->swr.ctrl_data = swr_ctrl_data; return; err_pdev_add: platform_device_put(pdev); err_mem: - kfree(swr_ctrl_data); + return; } static int __tavil_enable_efuse_sensing(struct tavil_priv *tavil) @@ -5676,7 +5695,8 @@ static int tavil_probe(struct platform_device *pdev) tavil->wcd9xxx = dev_get_drvdata(pdev->dev.parent); tavil->dev = &pdev->dev; - INIT_WORK(&tavil->wcd_add_child_devices_work, wcd_add_child_devices); + INIT_WORK(&tavil->tavil_add_child_devices_work, + tavil_add_child_devices); mutex_init(&tavil->swr.read_mutex); mutex_init(&tavil->swr.write_mutex); mutex_init(&tavil->swr.clk_mutex); @@ -5733,7 +5753,7 @@ static int tavil_probe(struct platform_device *pdev) __func__); goto err_cdc_reg; } - schedule_work(&tavil->wcd_add_child_devices_work); + schedule_work(&tavil->tavil_add_child_devices_work); return ret; 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/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 |
