diff options
156 files changed, 15414 insertions, 854 deletions
diff --git a/Documentation/devicetree/bindings/fb/mdss-dp.txt b/Documentation/devicetree/bindings/fb/mdss-dp.txt index 7bf7b9bacb60..aa227c2628da 100644 --- a/Documentation/devicetree/bindings/fb/mdss-dp.txt +++ b/Documentation/devicetree/bindings/fb/mdss-dp.txt @@ -54,6 +54,7 @@ Optional properties: device node. Refer to pinctrl-bindings.txt - qcom,logical2physical-lane-map: An array that specifies the DP logical to physical lane map setting. - qcom,phy-register-offset: An integer specifying the offset value of DP PHY register space. +- qcom,max-pclk-frequency-khz: An integer specifying the max. pixel clock in KHz supported by Display Port. Example: mdss_dp_ctrl: qcom,dp_ctrl@c990000 { @@ -89,6 +90,7 @@ Example: qcom,aux-cfg-settings = [00 13 00 10 0a 26 0a 03 8b 03]; qcom,logical2physical-lane-map = [02 03 01 00]; qcom,phy-register-offset = <0x4>; + qcom,max-pclk-frequency-khz = <593470>; qcom,core-supply-entries { #address-cells = <1>; diff --git a/Documentation/devicetree/bindings/fb/mdss-mdp.txt b/Documentation/devicetree/bindings/fb/mdss-mdp.txt index 7a98e4e8fdd2..0ab4d53f2663 100644 --- a/Documentation/devicetree/bindings/fb/mdss-mdp.txt +++ b/Documentation/devicetree/bindings/fb/mdss-mdp.txt @@ -92,6 +92,8 @@ Required properties active in MDP for this configuration. Meant for hardware that has hw cursors support as a source pipe. +- qcom,mdss-rot-xin-id: Array of VBIF clients ids (xins) corresponding + to the respective rotator pipes. - qcom,mdss-pipe-cursor-xin-id: Array of VBIF clients ids (xins) corresponding to the respective cursor pipes. Number of xin ids defined should match the number of offsets @@ -754,6 +756,7 @@ Example: qcom,mdss-pipe-rgb-xin-id = <1 5 9>; qcom,mdss-pipe-dma-xin-id = <2 10>; qcom,mdss-pipe-cursor-xin-id = <7 7>; + qcom,mdss-rot-xin-id = <14 15>; qcom,mdss-pipe-vig-clk-ctrl-offsets = <0x3AC 0 0>, <0x3B4 0 0>, diff --git a/Documentation/devicetree/bindings/input/touchscreen/STMicroelectronics.txt b/Documentation/devicetree/bindings/input/touchscreen/STMicroelectronics.txt new file mode 100644 index 000000000000..7799392700a7 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/STMicroelectronics.txt @@ -0,0 +1,54 @@ +STMicroelectronics touch controller + +The STMicroelectronics controller is connected to host processor +via i2c. The controller generates interrupts when the +user touches the panel. The host controller is expected +to read the touch coordinates over i2c and pass the coordinates +to the rest of the system. + +Required properties: + + - compatible : should be "st,fts". + - reg : i2c slave address of the device. + - interrupt-parent : parent of interrupt. + - interrupts : touch sample interrupt to indicate presense or release + of fingers on the panel. + - vdd-supply : Power supply needed to power up the device. + - vcc-supply : Power source required to power up i2c bus. + - st,irq-gpio : irq gpio which is to provide interrupts to host, + same as "interrupts" node. It will also + contain active low or active high information. + - st,reset-gpio : reset gpio to control the reset of chip. + - pinctrl-names : This should be defined if a target uses pinctrl framework. + See "pinctrl" in Documentation/devicetree/bindings/pinctrl/msm-pinctrl.txt. + Specify the names of the configs that pinctrl can install in driver. + Following are the pinctrl configs that can be installed: + "pmx_ts_active" : Active configuration of pins, this should specify active + config defined in pin groups of interrupt and reset gpio. + "pmx_ts_suspend" : Disabled configuration of pins, this should specify sleep + config defined in pin groups of interrupt and reset gpio. + "pmx_ts_release" : Release configuration of pins, this should specify + release config defined in pin groups of interrupt and reset gpio. + - st,regulator_avdd : name of Power supply needed to power up the device. + - st,regulator_dvdd : name of Power source required to power up i2c bus. +Optional properties: + + +Example: + i2c@78b9000 { /* BLSP1 QUP5 */ + st_fts@49 { + compatible = "st,fts"; + reg = <0x49>; + interrupt-parent = <&msm_gpio>; + interrupts = <13 0x2008>; + vdd-supply = <&pm8916_l17>; + vcc-supply = <&pm8916_l6>; + pinctrl-names = "pmx_ts_active","pmx_ts_suspend"; + pinctrl-0 = <&ts_int_active &ts_reset_active>; + pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>; + st,irq-gpio = <&msm_gpio 13 0x00000001>; + st,reset-gpio = <&msm_gpio 12 0x0>; + st,regulator_dvdd = "vdd"; + st,regulator_avdd = "avdd"; + }; + }; diff --git a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt index be5633024986..47a6fdd300ca 100644 --- a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt +++ b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt @@ -89,6 +89,13 @@ Optional properties: - qcom,cx-ipeak-vote: Boolean- Present if we need to set bit 5 of cxip_lm_vote_clear during modem shutdown +One child node to represent the MBA image may be specified, when the MBA image +needs to be loaded in a specifically carved out memory region. + +Required properties: +- compatible: Must be "qcom,pil-mba-mem" +- memory-region: A phandle that points to a reserved memory where the MBA image will be loaded. + Example: qcom,mss@fc880000 { compatible = "qcom,pil-q6v5-mss"; @@ -128,4 +135,9 @@ Example: qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_1_out 0 0>; qcom,ssctl-instance-id = <12>; qcom,sysmon-id = <0>; + + qcom,mba-mem@0 { + compatible = "qcom,pil-mba-mem"; + memory-region = <&peripheral_mem>; + }; }; diff --git a/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt b/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt index af53e59cd87f..37e8391259c6 100644 --- a/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt @@ -209,6 +209,15 @@ Platform independent properties: as the corresponding addresses are specified in the qcom,cpr-panic-reg-addr-list property. +- qcom,cpr-reset-step-quot-loop-en + Usage: optional; only meaningful for CPR4 and CPRh controllers + Value type: <empty> + Definition: Boolean value which indicates that the CPR controller should + be configured to reset step_quot on each loop_en = 0 + transition. This configuration allows the CPR controller to + first use the default step_quot and then later switch to the + run-time calibrated step_quot. + ================================================= Second Level Nodes - CPR Threads for a Controller ================================================= diff --git a/arch/arm/boot/dts/qcom/apq8998-v2.1-mediabox.dts b/arch/arm/boot/dts/qcom/apq8998-v2.1-mediabox.dts index 6288031fdd50..daf43d49e079 100644 --- a/arch/arm/boot/dts/qcom/apq8998-v2.1-mediabox.dts +++ b/arch/arm/boot/dts/qcom/apq8998-v2.1-mediabox.dts @@ -74,3 +74,7 @@ status = "ok"; }; + +&tspp { + qcom,lpass-timer-tts = <1>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi b/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi index 7dfcd34c2743..24186aca22be 100644 --- a/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi @@ -45,6 +45,7 @@ /* VBIF QoS remapper settings*/ qcom,mdss-vbif-qos-rt-setting = <1 2 2 2>; + qcom,mdss-vbif-qos-nrt-setting = <1 1 1 1>; qcom,vbif-settings = <0x00ac 0x00000040>, <0x00d0 0x00001010>; /* v1 only */ diff --git a/arch/arm/boot/dts/qcom/msm8998.dtsi b/arch/arm/boot/dts/qcom/msm8998.dtsi index 697b049235bd..81bf1777c7e2 100644 --- a/arch/arm/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998.dtsi @@ -1750,6 +1750,7 @@ tx-fifo-resize; snps,nominal-elastic-buffer; snps,disable-clk-gating; + snps,has-lpm-erratum; snps,hird-threshold = /bits/ 8 <0x10>; snps,num-gsi-evt-buffs = <0x3>; }; diff --git a/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi b/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi index 59afb993fb83..b1fed582aa1e 100644 --- a/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi @@ -10,8 +10,19 @@ * GNU General Public License for more details. */ +#include "msm-pm660a.dtsi" #include "sdm660-pinctrl.dtsi" / { + qrd_batterydata: qcom,battery-data { + qcom,batt-id-range-pct = <15>; + + #include "fg-gen3-batterydata-qrd-skuk-4v4-3000mah.dtsi" + }; +}; + +&pm660_fg { + qcom,battery-data = <&qrd_batterydata>; + qcom,fg-jeita-thresholds = <0 5 55 55>; }; &uartblsp1dm1 { @@ -100,5 +111,31 @@ status = "ok"; }; +&pm660l_gpios { + /* GPIO 7 for VOL_UP */ + gpio@c600 { + status = "ok"; + qcom,mode = <0>; + qcom,pull = <0>; + qcom,vin-sel = <0>; + qcom,src-sel = <0>; + qcom,out-strength = <1>; + }; +}; + &soc { + gpio_keys { + compatible = "gpio-keys"; + input-name = "gpio-keys"; + status = "ok"; + + vol_up { + label = "volume_up"; + gpios = <&pm660l_gpios 7 0x1>; + linux,input-type = <1>; + linux,code = <115>; + gpio-key,wakeup; + debounce-interval = <15>; + }; + }; }; diff --git a/arch/arm/boot/dts/qcom/sdm630.dtsi b/arch/arm/boot/dts/qcom/sdm630.dtsi index 302a9a197128..206628149f7d 100644 --- a/arch/arm/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630.dtsi @@ -1018,6 +1018,158 @@ #clock-cells = <1>; }; + cpubw: qcom,cpubw { + compatible = "qcom,devbw"; + governor = "performance"; + qcom,src-dst-ports = <1 512>; + qcom,active-only; + qcom,bw-tbl = + < 381 /* 100 MHz */ >, + < 572 /* 150 MHz */ >, + < 762 /* 200 MHz */ >, + < 1144 /* 300 MHz */ >, + < 1571 /* 412 MHz */ >, + < 2086 /* 547 MHz */ >, + < 2597 /* 681 MHz */ >, + < 2929 /* 768 MHz */ >, + < 3879 /* 1017 MHz */ >, + < 4943 /* 1296 MHz */ >, + < 5163 /* 1353 MHz */ >; + }; + + bwmon: qcom,cpu-bwmon { + compatible = "qcom,bimc-bwmon4"; + reg = <0x01008000 0x300>, <0x01001000 0x200>; + reg-names = "base", "global_base"; + interrupts = <0 183 4>; + qcom,mport = <0>; + qcom,hw-timer-hz = <19200000>; + qcom,target-dev = <&cpubw>; + }; + + mincpubw: qcom,mincpubw { + compatible = "qcom,devbw"; + governor = "powersave"; + qcom,src-dst-ports = <1 512>; + qcom,active-only; + qcom,bw-tbl = + < 381 /* 100 MHz */ >, + < 572 /* 150 MHz */ >, + < 762 /* 200 MHz */ >, + < 1144 /* 300 MHz */ >, + < 1571 /* 412 MHz */ >, + < 2086 /* 547 MHz */ >, + < 2597 /* 681 MHz */ >, + < 2929 /* 768 MHz */ >, + < 3879 /* 1017 MHz */ >, + < 4943 /* 1296 MHz */ >, + < 5163 /* 1353 MHz */ >; + }; + + memlat_cpu0: qcom,memlat-cpu0 { + compatible = "qcom,devbw"; + governor = "powersave"; + qcom,src-dst-ports = <1 512>; + qcom,active-only; + qcom,bw-tbl = + < 381 /* 100 MHz */ >, + < 572 /* 150 MHz */ >, + < 762 /* 200 MHz */ >, + < 1144 /* 300 MHz */ >, + < 1571 /* 412 MHz */ >, + < 2086 /* 547 MHz */ >, + < 2597 /* 681 MHz */ >, + < 2929 /* 768 MHz */ >, + < 3879 /* 1017 MHz */ >, + < 4943 /* 1296 MHz */ >, + < 5163 /* 1353 MHz */ >; + }; + + memlat_cpu4: qcom,memlat-cpu4 { + compatible = "qcom,devbw"; + governor = "powersave"; + qcom,src-dst-ports = <1 512>; + qcom,active-only; + qcom,bw-tbl = + < 381 /* 100 MHz */ >, + < 572 /* 150 MHz */ >, + < 762 /* 200 MHz */ >, + < 1144 /* 300 MHz */ >, + < 1571 /* 412 MHz */ >, + < 2086 /* 547 MHz */ >, + < 2597 /* 681 MHz */ >, + < 2929 /* 768 MHz */ >, + < 3879 /* 1017 MHz */ >, + < 4943 /* 1296 MHz */ >, + < 5163 /* 1353 MHz */ >; + }; + + devfreq_memlat_0: qcom,arm-memlat-mon-0 { + compatible = "qcom,arm-memlat-mon"; + qcom,cpulist = <&CPU0 &CPU1 &CPU2 &CPU3>; + qcom,target-dev = <&memlat_cpu0>; + qcom,core-dev-table = + < 787200 762 >, + < 1344000 2086 >, + < 1670400 2929 >, + < 2150400 3879 >, + < 2380800 4943 >; + }; + + devfreq_memlat_4: qcom,arm-memlat-mon-4 { + compatible = "qcom,arm-memlat-mon"; + qcom,cpulist = <&CPU4 &CPU5 &CPU6 &CPU7>; + qcom,target-dev = <&memlat_cpu4>; + qcom,core-dev-table = + < 614400 762 >, + < 1536000 2929 >, + < 1728000 3879 >; + }; + + devfreq_cpufreq: devfreq-cpufreq { + mincpubw-cpufreq { + target-dev = <&mincpubw>; + cpu-to-dev-map-0 = + < 787200 762 >, + < 1344000 1571 >, + < 1881600 2929 >, + < 2380800 3879 >; + cpu-to-dev-map-4 = + < 614400 762 >, + < 1536000 1571 >, + < 1728000 3879 >; + }; + }; + + msm_cpufreq: qcom,msm-cpufreq { + compatible = "qcom,msm-cpufreq"; + clock-names = "cpu0_clk", "cpu4_clk"; + clocks = <&clock_cpu PWRCL_CLK>, + <&clock_cpu PERFCL_CLK>; + + qcom,governor-per-policy; + + qcom,cpufreq-table-0 = + < 787200 >, + < 1113600 >, + < 1344000 >, + < 1670400 >, + < 1881600 >, + < 2150400 >, + < 2380800 >, + < 2515000 >; + + qcom,cpufreq-table-4 = + < 614400 >, + < 883200 >, + < 1094400 >, + < 1382400 >, + < 1536000 >, + < 1728000 >, + < 1843200 >, + < 2000000 >; + }; + ipa_hw: qcom,ipa@14780000 { compatible = "qcom,ipa"; reg = <0x14780000 0x4effc>, <0x14784000 0x26934>; diff --git a/arch/arm/boot/dts/qcom/sdm660-common.dtsi b/arch/arm/boot/dts/qcom/sdm660-common.dtsi index 5a0997faf133..05b7973f2457 100644 --- a/arch/arm/boot/dts/qcom/sdm660-common.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-common.dtsi @@ -25,12 +25,58 @@ status = "disabled"; }; + ufs_ice: ufsice@1db0000 { + compatible = "qcom,ice"; + reg = <0x1db0000 0x8000>; + qcom,enable-ice-clk; + clock-names = "ufs_core_clk", "bus_clk", + "iface_clk", "ice_core_clk"; + clocks = <&clock_gcc GCC_UFS_AXI_CLK>, + <&clock_gcc GCC_UFS_CLKREF_CLK>, + <&clock_gcc GCC_UFS_AHB_CLK>, + <&clock_gcc GCC_UFS_ICE_CORE_CLK>; + qcom,op-freq-hz = <0>, <0>, <0>, <300000000>; + vdd-hba-supply = <&gdsc_ufs>; + qcom,msm-bus,name = "ufs_ice_noc"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <1 650 0 0>, /* No vote */ + <1 650 1000 0>; /* Max. bandwidth */ + qcom,bus-vector-names = "MIN", + "MAX"; + qcom,instance-type = "ufs"; + }; + + sdcc1_ice: sdcc1ice@c0c8000 { + compatible = "qcom,ice"; + reg = <0xc0c8000 0x8000>; + qcom,enable-ice-clk; + clock-names = "ice_core_clk_src", "ice_core_clk", + "bus_clk", "iface_clk"; + clocks = <&clock_gcc SDCC1_ICE_CORE_CLK_SRC>, + <&clock_gcc GCC_SDCC1_ICE_CORE_CLK>, + <&clock_gcc GCC_SDCC1_APPS_CLK>, + <&clock_gcc GCC_SDCC1_AHB_CLK>; + qcom,op-freq-hz = <300000000>, <0>, <0>, <0>; + qcom,msm-bus,name = "sdcc_ice_noc"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <78 512 0 0>, /* No vote */ + <78 512 1000 0>; /* Max. bandwidth */ + qcom,bus-vector-names = "MIN", + "MAX"; + qcom,instance-type = "sdcc"; + }; + ufs1: ufshc@1da4000 { compatible = "qcom,ufshc"; reg = <0x1da4000 0x3000>; interrupts = <0 265 0>; phys = <&ufsphy1>; phy-names = "ufsphy"; + ufs-qcom-crypto = <&ufs_ice>; clock-names = "core_clk", @@ -415,6 +461,7 @@ qcom,bus-width = <8>; qcom,large-address-bus; + sdhc-msm-crypto = <&sdcc1_ice>; qcom,devfreq,freq-table = <50000000 200000000>; diff --git a/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi b/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi index d2a6b3e16d83..8116f56642f3 100644 --- a/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi @@ -575,6 +575,7 @@ /* VBIF QoS remapper settings*/ qcom,mdss-rot-vbif-qos-setting = <1 1 1 1>; + qcom,mdss-rot-xin-id = <14 15>; qcom,mdss-default-ot-rd-limit = <40>; qcom,mdss-default-ot-wr-limit = <32>; diff --git a/arch/arm/boot/dts/qcom/sdm660-qrd.dtsi b/arch/arm/boot/dts/qcom/sdm660-qrd.dtsi index f0d13b3455ab..49b58c8be8d5 100644 --- a/arch/arm/boot/dts/qcom/sdm660-qrd.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-qrd.dtsi @@ -110,16 +110,6 @@ }; &pm660_gpios { - /* GPIO 11 for home key */ - gpio@ca00 { - status = "ok"; - qcom,mode = <0>; - qcom,pull = <0>; - qcom,vin-sel = <0>; - qcom,src-sel = <0>; - qcom,out-strength = <1>; - }; - /* GPIO 4 (NFC_CLK_REQ) */ gpio@c300 { qcom,mode = <0>; @@ -210,16 +200,6 @@ gpio-key,wakeup; debounce-interval = <15>; }; - - home { - label = "home"; - gpios = <&pm660_gpios 11 0x1>; - linux,input-type = <1>; - linux,code = <102>; - gpio-key,wakeup; - debounce-interval = <15>; - }; - }; hbtp { diff --git a/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi b/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi index 0826f94a2296..44796eeb7059 100644 --- a/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi @@ -547,6 +547,7 @@ qcom,cpr-step-quot-init-max = <14>; qcom,cpr-count-mode = <0>; /* All at once */ qcom,cpr-count-repeat = <14>; + qcom,cpr-reset-step-quot-loop-en; vdd-supply = <&gfx_stub_vreg>; mem-acc-supply = <&gfx_mem_acc_vreg>; @@ -664,6 +665,7 @@ qcom,voltage-step = <4000>; qcom,voltage-base = <400000>; qcom,cpr-saw-use-unit-mV; + qcom,cpr-reset-step-quot-loop-en; qcom,cpr-panic-reg-addr-list = <0x179cbaa4 0x17912c18>; @@ -738,6 +740,7 @@ qcom,voltage-step = <4000>; qcom,voltage-base = <400000>; qcom,cpr-saw-use-unit-mV; + qcom,cpr-reset-step-quot-loop-en; qcom,cpr-panic-reg-addr-list = <0x179c7aa4 0x17812c18>; diff --git a/arch/arm/boot/dts/qcom/sdm660.dtsi b/arch/arm/boot/dts/qcom/sdm660.dtsi index 4d8a72fea6a8..e9f6e4b1b727 100644 --- a/arch/arm/boot/dts/qcom/sdm660.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660.dtsi @@ -152,7 +152,7 @@ qcom,limits-info = <&mitigation_profile1>; qcom,lmh-dcvs = <&lmh_dcvs1>; qcom,ea = <&ea4>; - efficiency = <1536>; + efficiency = <1638>; next-level-cache = <&L2_1>; L2_1: l2-cache { compatible = "arm,arch-cache"; @@ -179,7 +179,7 @@ qcom,limits-info = <&mitigation_profile2>; qcom,lmh-dcvs = <&lmh_dcvs1>; qcom,ea = <&ea5>; - efficiency = <1536>; + efficiency = <1638>; next-level-cache = <&L2_1>; L1_I_101: l1-icache { compatible = "arm,arch-cache"; @@ -202,7 +202,7 @@ qcom,limits-info = <&mitigation_profile3>; qcom,lmh-dcvs = <&lmh_dcvs1>; qcom,ea = <&ea6>; - efficiency = <1536>; + efficiency = <1638>; next-level-cache = <&L2_1>; L1_I_102: l1-icache { compatible = "arm,arch-cache"; @@ -225,7 +225,7 @@ qcom,limits-info = <&mitigation_profile4>; qcom,lmh-dcvs = <&lmh_dcvs1>; qcom,ea = <&ea7>; - efficiency = <1536>; + efficiency = <1638>; next-level-cache = <&L2_1>; L1_I_103: l1-icache { compatible = "arm,arch-cache"; @@ -1307,51 +1307,6 @@ < 2457600 >; }; - ufs_ice: ufsice@1db0000 { - compatible = "qcom,ice"; - reg = <0x1db0000 0x8000>; - qcom,enable-ice-clk; - clock-names = "ufs_core_clk", "bus_clk", - "iface_clk", "ice_core_clk"; - clocks = <&clock_gcc GCC_UFS_AXI_CLK>, - <&clock_gcc GCC_UFS_CLKREF_CLK>, - <&clock_gcc GCC_UFS_AHB_CLK>, - <&clock_gcc GCC_UFS_ICE_CORE_CLK>; - qcom,op-freq-hz = <0>, <0>, <0>, <300000000>; - vdd-hba-supply = <&gdsc_ufs>; - qcom,msm-bus,name = "ufs_ice_noc"; - qcom,msm-bus,num-cases = <2>; - qcom,msm-bus,num-paths = <1>; - qcom,msm-bus,vectors-KBps = - <1 650 0 0>, /* No vote */ - <1 650 1000 0>; /* Max. bandwidth */ - qcom,bus-vector-names = "MIN", - "MAX"; - qcom,instance-type = "ufs"; - }; - - sdcc1_ice: sdcc1ice@c0c8000 { - compatible = "qcom,ice"; - reg = <0xc0c8000 0x8000>; - qcom,enable-ice-clk; - clock-names = "ice_core_clk_src", "ice_core_clk", - "bus_clk", "iface_clk"; - clocks = <&clock_gcc SDCC1_ICE_CORE_CLK_SRC>, - <&clock_gcc GCC_SDCC1_ICE_CORE_CLK>, - <&clock_gcc GCC_SDCC1_APPS_CLK>, - <&clock_gcc GCC_SDCC1_AHB_CLK>; - qcom,op-freq-hz = <300000000>, <0>, <0>, <0>; - qcom,msm-bus,name = "sdcc_ice_noc"; - qcom,msm-bus,num-cases = <2>; - qcom,msm-bus,num-paths = <1>; - qcom,msm-bus,vectors-KBps = - <78 512 0 0>, /* No vote */ - <78 512 1000 0>; /* Max. bandwidth */ - qcom,bus-vector-names = "MIN", - "MAX"; - qcom,instance-type = "sdcc"; - }; - sdhc_1: sdhci@c0c4000 { compatible = "qcom,sdhci-msm-v5"; reg = <0xc0c4000 0x1000>, <0xc0c5000 0x1000>; diff --git a/arch/arm/configs/msmcortex_defconfig b/arch/arm/configs/msmcortex_defconfig index 2cfe647855c3..1658cc992ee6 100644 --- a/arch/arm/configs/msmcortex_defconfig +++ b/arch/arm/configs/msmcortex_defconfig @@ -217,6 +217,7 @@ CONFIG_NFC_NQ=y CONFIG_IPC_ROUTER=y CONFIG_IPC_ROUTER_SECURITY=y CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS=y CONFIG_DMA_CMA=y CONFIG_CMA_SIZE_MBYTES=40 CONFIG_ZRAM=y diff --git a/arch/arm/configs/sdm660-perf_defconfig b/arch/arm/configs/sdm660-perf_defconfig index 73c12f90016c..7df369de029a 100644 --- a/arch/arm/configs/sdm660-perf_defconfig +++ b/arch/arm/configs/sdm660-perf_defconfig @@ -378,6 +378,9 @@ CONFIG_USB_VIDEO_CLASS=y CONFIG_V4L_PLATFORM_DRIVERS=y CONFIG_MSM_CAMERA=y CONFIG_MSM_CAMERA_DEBUG=y +CONFIG_MSM_VIDC_V4L2=y +CONFIG_MSM_VIDC_VMEM=y +CONFIG_MSM_VIDC_GOVERNORS=y CONFIG_MSM_SDE_ROTATOR=y CONFIG_DVB_MPQ=m CONFIG_DVB_MPQ_DEMUX=m diff --git a/arch/arm/configs/sdm660_defconfig b/arch/arm/configs/sdm660_defconfig index 5c8dbe8e8302..3a757a7dc7b5 100644 --- a/arch/arm/configs/sdm660_defconfig +++ b/arch/arm/configs/sdm660_defconfig @@ -376,8 +376,9 @@ CONFIG_USB_VIDEO_CLASS=y CONFIG_V4L_PLATFORM_DRIVERS=y CONFIG_MSM_CAMERA=y CONFIG_MSM_CAMERA_DEBUG=y -CONFIG_MSM_VIDC_V4L2=m -CONFIG_MSM_VIDC_GOVERNORS=m +CONFIG_MSM_VIDC_V4L2=y +CONFIG_MSM_VIDC_VMEM=y +CONFIG_MSM_VIDC_GOVERNORS=y CONFIG_MSM_SDE_ROTATOR=y CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y CONFIG_DVB_MPQ=m diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index dd1c4ffe3dc5..f08cd3b27c81 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -548,7 +548,6 @@ CONFIG_QCOM_WATCHDOG_V2=y CONFIG_QCOM_IRQ_HELPER=y CONFIG_QCOM_MEMORY_DUMP_V2=y CONFIG_ICNSS=y -CONFIG_ICNSS_DEBUG=y CONFIG_MSM_GLADIATOR_ERP_V2=y CONFIG_PANIC_ON_GLADIATOR_ERROR_V2=y CONFIG_MSM_GLADIATOR_HANG_DETECT=y diff --git a/arch/arm64/configs/msmcortex_mediabox_defconfig b/arch/arm64/configs/msmcortex_mediabox_defconfig index 06b4554ec362..967edf184f1b 100644 --- a/arch/arm64/configs/msmcortex_mediabox_defconfig +++ b/arch/arm64/configs/msmcortex_mediabox_defconfig @@ -405,6 +405,7 @@ CONFIG_MSM_SDE_ROTATOR=y CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y CONFIG_DVB_MPQ=m CONFIG_DVB_MPQ_DEMUX=m +CONFIG_DVB_MPQ_MEDIA_BOX_DEMUX=y CONFIG_TSPP=m CONFIG_QCOM_KGSL=y CONFIG_FB=y diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c index 2d3f1ab33f70..45c365290553 100644 --- a/arch/arm64/mm/dma-mapping.c +++ b/arch/arm64/mm/dma-mapping.c @@ -1927,7 +1927,11 @@ arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, size_t size) if (!mapping) goto err; - mapping->bitmap = kzalloc(bitmap_size, GFP_KERNEL); + mapping->bitmap = kzalloc(bitmap_size, GFP_KERNEL | __GFP_NOWARN | + __GFP_NORETRY); + if (!mapping->bitmap) + mapping->bitmap = vzalloc(bitmap_size); + if (!mapping->bitmap) goto err2; @@ -1942,7 +1946,7 @@ arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, size_t size) kref_init(&mapping->kref); return mapping; err3: - kfree(mapping->bitmap); + kvfree(mapping->bitmap); err2: kfree(mapping); err: @@ -1956,7 +1960,7 @@ static void release_iommu_mapping(struct kref *kref) container_of(kref, struct dma_iommu_mapping, kref); iommu_domain_free(mapping->domain); - kfree(mapping->bitmap); + kvfree(mapping->bitmap); kfree(mapping); } diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig index 5b83a06ee1a9..45fc564bf949 100644 --- a/drivers/base/regmap/Kconfig +++ b/drivers/base/regmap/Kconfig @@ -36,3 +36,8 @@ config REGMAP_SWR config REGMAP_ALLOW_WRITE_DEBUGFS depends on REGMAP && DEBUG_FS bool "Allow REGMAP debugfs write" + default n + help + Say 'y' here to allow the regmap debugfs write. Regmap debugfs write + could be risky when accessing some essential hardwares, so it is not + recommended to enable this option on any production device. diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c index 2c7662a24cd1..4748012a37cd 100644 --- a/drivers/char/diag/diag_dci.c +++ b/drivers/char/diag/diag_dci.c @@ -133,6 +133,35 @@ void diag_dci_record_traffic(int read_bytes, uint8_t ch_type, void diag_dci_record_traffic(int read_bytes, uint8_t ch_type, uint8_t peripheral, uint8_t proc) { } #endif + +static int check_peripheral_dci_support(int peripheral_id, int dci_proc_id) +{ + int dci_peripheral_list = 0; + + if (dci_proc_id < 0 || dci_proc_id >= NUM_DCI_PROC) { + pr_err("diag:In %s,not a supported DCI proc id\n", __func__); + return 0; + } + if (peripheral_id < 0 || peripheral_id >= NUM_PERIPHERALS) { + pr_err("diag:In %s,not a valid peripheral id\n", __func__); + return 0; + } + dci_peripheral_list = dci_ops_tbl[dci_proc_id].peripheral_status; + + if (dci_peripheral_list <= 0 || dci_peripheral_list > DIAG_CON_ALL) { + pr_err("diag:In %s,not a valid dci peripheral mask\n", + __func__); + return 0; + } + /* Remove APSS bit mask information */ + dci_peripheral_list = dci_peripheral_list >> 1; + + if ((1 << peripheral_id) & (dci_peripheral_list)) + return 1; + else + return 0; +} + static void create_dci_log_mask_tbl(unsigned char *mask, uint8_t dirty) { unsigned char *temp = mask; @@ -1433,7 +1462,7 @@ void diag_dci_channel_open_work(struct work_struct *work) void diag_dci_notify_client(int peripheral_mask, int data, int proc) { - int stat; + int stat = 0; struct siginfo info; struct list_head *start, *temp; struct diag_dci_client_tbl *entry = NULL; @@ -2372,10 +2401,12 @@ int diag_send_dci_event_mask(int token) * is down. It may also mean that the peripheral doesn't * support DCI. */ - err = diag_dci_write_proc(i, DIAG_CNTL_TYPE, buf, - header_size + DCI_EVENT_MASK_SIZE); - if (err != DIAG_DCI_NO_ERROR) - ret = DIAG_DCI_SEND_DATA_FAIL; + if (check_peripheral_dci_support(i, DCI_LOCAL_PROC)) { + err = diag_dci_write_proc(i, DIAG_CNTL_TYPE, buf, + header_size + DCI_EVENT_MASK_SIZE); + if (err != DIAG_DCI_NO_ERROR) + ret = DIAG_DCI_SEND_DATA_FAIL; + } } mutex_unlock(&event_mask.lock); @@ -2557,11 +2588,13 @@ int diag_send_dci_log_mask(int token) } write_len = dci_fill_log_mask(buf, log_mask_ptr); for (j = 0; j < NUM_PERIPHERALS && write_len; j++) { - err = diag_dci_write_proc(j, DIAG_CNTL_TYPE, buf, - write_len); - if (err != DIAG_DCI_NO_ERROR) { - updated = 0; - ret = DIAG_DCI_SEND_DATA_FAIL; + if (check_peripheral_dci_support(j, DCI_LOCAL_PROC)) { + err = diag_dci_write_proc(j, DIAG_CNTL_TYPE, + buf, write_len); + if (err != DIAG_DCI_NO_ERROR) { + updated = 0; + ret = DIAG_DCI_SEND_DATA_FAIL; + } } } if (updated) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 4f1ba98a2b2c..e8e48015d3af 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2649,7 +2649,7 @@ static const struct file_operations clk_enabled_list_fops = { .release = seq_release, }; -static void clk_debug_print_hw(struct clk_core *clk, struct seq_file *f) +void clk_debug_print_hw(struct clk_core *clk, struct seq_file *f) { if (IS_ERR_OR_NULL(clk)) return; @@ -2663,6 +2663,7 @@ static void clk_debug_print_hw(struct clk_core *clk, struct seq_file *f) clk->ops->list_registers(f, clk->hw); } +EXPORT_SYMBOL(clk_debug_print_hw); static int print_hw_show(struct seq_file *m, void *unused) { diff --git a/drivers/clk/clk.h b/drivers/clk/clk.h index c95a327a9301..25bf4bc85f5c 100644 --- a/drivers/clk/clk.h +++ b/drivers/clk/clk.h @@ -25,6 +25,7 @@ void __clk_free_clk(struct clk *clk); void clock_debug_print_enabled(void); int clk_register_debug(struct clk_hw *hw, struct dentry *dentry); void clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry); +void clk_debug_print_hw(struct clk_core *clk, struct seq_file *f); #else /* All these casts to avoid ifdefs in clkdev... */ diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index 375f1420f3bb..e6054444599c 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -16,8 +16,10 @@ #include <linux/clk-provider.h> #include <linux/regmap.h> #include <linux/delay.h> +#include <linux/sched.h> #include "clk-alpha-pll.h" +#include "common.h" #define PLL_MODE 0x00 #define PLL_OUTCTRL BIT(0) @@ -74,13 +76,17 @@ static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse, u32 val, off; int count; int ret; - const char *name = clk_hw_get_name(&pll->clkr.hw); + u64 time; + struct clk_hw *hw = &pll->clkr.hw; + const char *name = clk_hw_get_name(hw); off = pll->offset; ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val); if (ret) return ret; + time = sched_clock(); + for (count = 100; count > 0; count--) { ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val); if (ret) @@ -93,7 +99,13 @@ static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse, udelay(1); } - WARN(1, "%s failed to %s!\n", name, action); + time = sched_clock() - time; + + pr_err("PLL lock bit detection total wait time: %lld ns", time); + + WARN_CLK(hw->core, name, 1, "failed to %s!\n", action); + + return -ETIMEDOUT; } @@ -589,7 +601,11 @@ static void clk_alpha_pll_list_registers(struct seq_file *f, struct clk_hw *hw) {"PLL_ALPHA_VAL", 0x8}, {"PLL_ALPHA_VAL_U", 0xC}, {"PLL_USER_CTL", 0x10}, + {"PLL_USER_CTL_U", 0x14}, {"PLL_CONFIG_CTL", 0x18}, + {"PLL_TEST_CTL", 0x1c}, + {"PLL_TEST_CTL_U", 0x20}, + {"PLL_STATUS", 0x24}, }; static struct clk_register_data data1[] = { @@ -601,7 +617,8 @@ static void clk_alpha_pll_list_registers(struct seq_file *f, struct clk_hw *hw) for (i = 0; i < size; i++) { regmap_read(pll->clkr.regmap, pll->offset + data[i].offset, &val); - seq_printf(f, "%20s: 0x%.8x\n", data[i].name, val); + clock_debug_output(f, false, "%20s: 0x%.8x\n", + data[i].name, val); } regmap_read(pll->clkr.regmap, pll->offset + data[0].offset, &val); @@ -609,7 +626,8 @@ static void clk_alpha_pll_list_registers(struct seq_file *f, struct clk_hw *hw) if (val & PLL_FSM_ENA) { regmap_read(pll->clkr.regmap, pll->clkr.enable_reg + data1[0].offset, &val); - seq_printf(f, "%20s: 0x%.8x\n", data1[0].name, val); + clock_debug_output(f, false, "%20s: 0x%.8x\n", + data1[0].name, val); } } diff --git a/drivers/clk/qcom/clk-branch.c b/drivers/clk/qcom/clk-branch.c index bfaf4482d668..ca6010db8d78 100644 --- a/drivers/clk/qcom/clk-branch.c +++ b/drivers/clk/qcom/clk-branch.c @@ -22,6 +22,7 @@ #include "clk-branch.h" #include "clk-regmap.h" +#include "common.h" static bool clk_branch_in_hwcg_mode(const struct clk_branch *br) { @@ -77,7 +78,8 @@ static int clk_branch_wait(const struct clk_branch *br, bool enabling, bool (check_halt)(const struct clk_branch *, bool)) { bool voted = br->halt_check & BRANCH_VOTED; - const char *name = clk_hw_get_name(&br->clkr.hw); + const struct clk_hw *hw = &br->clkr.hw; + const char *name = clk_hw_get_name(hw); /* Skip checking halt bit if the clock is in hardware gated mode */ if (clk_branch_in_hwcg_mode(br)) @@ -104,8 +106,10 @@ static int clk_branch_wait(const struct clk_branch *br, bool enabling, return 0; udelay(1); } - WARN(1, "%s status stuck at 'o%s'", name, - enabling ? "ff" : "n"); + + WARN_CLK(hw->core, name, 1, "status stuck at 'o%s'", + enabling ? "ff" : "n"); + return -EBUSY; } return 0; @@ -212,7 +216,8 @@ static void clk_branch2_list_registers(struct seq_file *f, struct clk_hw *hw) for (i = 0; i < size; i++) { regmap_read(br->clkr.regmap, br->halt_reg + data[i].offset, &val); - seq_printf(f, "%20s: 0x%.8x\n", data[i].name, val); + clock_debug_output(f, false, "%20s: 0x%.8x\n", + data[i].name, val); } if ((br->halt_check & BRANCH_HALT_VOTED) && @@ -222,7 +227,7 @@ static void clk_branch2_list_registers(struct seq_file *f, struct clk_hw *hw) for (i = 0; i < size; i++) { regmap_read(br->clkr.regmap, rclk->enable_reg + data1[i].offset, &val); - seq_printf(f, "%20s: 0x%.8x\n", + clock_debug_output(f, false, "%20s: 0x%.8x\n", data1[i].name, val); } } @@ -360,7 +365,8 @@ static void clk_gate2_list_registers(struct seq_file *f, struct clk_hw *hw) for (i = 0; i < size; i++) { regmap_read(gt->clkr.regmap, gt->clkr.enable_reg + data[i].offset, &val); - seq_printf(f, "%20s: 0x%.8x\n", data[i].name, val); + clock_debug_output(f, false, "%20s: 0x%.8x\n", + data[i].name, val); } } diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index 6e6adbff4676..a07deb902577 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -122,7 +122,7 @@ err: return 0; } -static int update_config(struct clk_rcg2 *rcg) +static int update_config(struct clk_rcg2 *rcg, u32 cfg) { int count, ret; u32 cmd; @@ -144,7 +144,11 @@ static int update_config(struct clk_rcg2 *rcg) udelay(1); } - WARN(1, "%s: rcg didn't update its configuration.", name); + pr_err("CFG_RCGR old frequency configuration 0x%x !\n", cfg); + + WARN_CLK(hw->core, name, count == 0, + "rcg didn't update its configuration."); + return 0; } @@ -153,13 +157,17 @@ static int clk_rcg2_set_parent(struct clk_hw *hw, u8 index) struct clk_rcg2 *rcg = to_clk_rcg2(hw); int ret; u32 cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; + u32 old_cfg; + + /* Read back the old configuration */ + regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &old_cfg); ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, CFG_SRC_SEL_MASK, cfg); if (ret) return ret; - return update_config(rcg); + return update_config(rcg, old_cfg); } static void clk_rcg_clear_force_enable(struct clk_hw *hw) @@ -297,13 +305,16 @@ static int clk_rcg2_determine_rate(struct clk_hw *hw, static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f) { - u32 cfg, mask; + u32 cfg, mask, old_cfg; struct clk_hw *hw = &rcg->clkr.hw; int ret, index = qcom_find_src_index(hw, rcg->parent_map, f->src); if (index < 0) return index; + /* Read back the old configuration */ + regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &old_cfg); + if (rcg->mnd_width && f->n) { mask = BIT(rcg->mnd_width) - 1; ret = regmap_update_bits(rcg->clkr.regmap, @@ -333,7 +344,7 @@ static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f) if (ret) return ret; - return update_config(rcg); + return update_config(rcg, old_cfg); } static void clk_rcg2_list_registers(struct seq_file *f, struct clk_hw *hw) @@ -359,14 +370,16 @@ static void clk_rcg2_list_registers(struct seq_file *f, struct clk_hw *hw) for (i = 0; i < size; i++) { regmap_read(rcg->clkr.regmap, (rcg->cmd_rcgr + data1[i].offset), &val); - seq_printf(f, "%20s: 0x%.8x\n", data1[i].name, val); + clock_debug_output(f, false, "%20s: 0x%.8x\n", + data1[i].name, val); } } else { size = ARRAY_SIZE(data); for (i = 0; i < size; i++) { regmap_read(rcg->clkr.regmap, (rcg->cmd_rcgr + data[i].offset), &val); - seq_printf(f, "%20s: 0x%.8x\n", data[i].name, val); + clock_debug_output(f, false, "%20s: 0x%.8x\n", + data[i].name, val); } } } @@ -1186,16 +1199,19 @@ static int clk_gfx3d_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate, u8 index) { struct clk_rcg2 *rcg = to_clk_rcg2(hw); - u32 cfg; + u32 cfg, old_cfg; int ret; + /* Read back the old configuration */ + regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &old_cfg); + /* Just mux it, we don't use the division or m/n hardware */ cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; ret = regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, cfg); if (ret) return ret; - return update_config(rcg); + return update_config(rcg, old_cfg); } static int clk_gfx3d_set_rate(struct clk_hw *hw, unsigned long rate, @@ -1271,9 +1287,12 @@ static int clk_gfx3d_src_set_rate_and_parent(struct clk_hw *hw, { struct clk_rcg2 *rcg = to_clk_rcg2(hw); const struct freq_tbl *f; - u32 cfg; + u32 cfg, old_cfg; int ret; + /* Read back the old configuration */ + regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &old_cfg); + cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; f = qcom_find_freq(rcg->freq_tbl, rate); @@ -1287,7 +1306,7 @@ static int clk_gfx3d_src_set_rate_and_parent(struct clk_hw *hw, if (ret) return ret; - return update_config(rcg); + return update_config(rcg, old_cfg); } const struct clk_ops clk_gfx3d_src_ops = { diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h index 06ec0bb0bc29..95408a47fef0 100644 --- a/drivers/clk/qcom/common.h +++ b/drivers/clk/qcom/common.h @@ -13,6 +13,8 @@ #ifndef __QCOM_CLK_COMMON_H__ #define __QCOM_CLK_COMMON_H__ +#include "../clk.h" + struct platform_device; struct regmap_config; struct clk_regmap; @@ -153,4 +155,19 @@ struct clk_debug_mux { extern const struct clk_ops clk_debug_mux_ops; +#define WARN_CLK(core, name, cond, fmt, ...) do { \ + clk_debug_print_hw(core, NULL); \ + WARN(cond, "%s: " fmt, name, ##__VA_ARGS__); \ +} while (0) + +#define clock_debug_output(m, c, fmt, ...) \ +do { \ + if (m) \ + seq_printf(m, fmt, ##__VA_ARGS__); \ + else if (c) \ + pr_cont(fmt, ##__VA_ARGS__); \ + else \ + pr_info(fmt, ##__VA_ARGS__); \ +} while (0) + #endif diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c index 05e6da14cec0..82da4c1bc86c 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.c +++ b/drivers/gpu/drm/msm/sde/sde_crtc.c @@ -358,7 +358,17 @@ bool sde_crtc_is_rt(struct drm_crtc *crtc) return to_sde_crtc_state(crtc->state)->is_rt; } -/* if file!=NULL, this is preclose potential cancel-flip path */ +/** + * _sde_crtc_complete_flip - signal pending page_flip events + * Any pending vblank events are added to the vblank_event_list + * so that the next vblank interrupt shall signal them. + * However PAGE_FLIP events are not handled through the vblank_event_list. + * This API signals any pending PAGE_FLIP events requested through + * DRM_IOCTL_MODE_PAGE_FLIP and are cached in the sde_crtc->event. + * if file!=NULL, this is preclose potential cancel-flip path + * @crtc: Pointer to drm crtc structure + * @file: Pointer to drm file + */ static void _sde_crtc_complete_flip(struct drm_crtc *crtc, struct drm_file *file) { @@ -395,7 +405,7 @@ static void sde_crtc_vblank_cb(void *data) sde_crtc->vblank_cb_time = ktime_get(); else sde_crtc->vblank_cb_count++; - + _sde_crtc_complete_flip(crtc, NULL); drm_crtc_handle_vblank(crtc); DRM_DEBUG_VBL("crtc%d\n", crtc->base.id); SDE_EVT32_IRQ(DRMID(crtc)); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 49df5e0afbfb..f13017cd9d46 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1228,4 +1228,15 @@ config TOUCHSCREEN_GT9XX If unsure, say N. source "drivers/input/touchscreen/gt9xx/Kconfig" + +config TOUCHSCREEN_ST + bool "STMicroelectronics Touchscreen Driver" + depends on I2C + help + Say Y here if you have a STMicroelectronics Touchscreen. + + If unsure, say N. + +source "drivers/input/touchscreen/st/Kconfig" + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 06953a69123f..7606fe53fcff 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -100,3 +100,4 @@ obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o obj-$(CONFIG_TOUCHSCREEN_MSTAR21XX) += msg21xx_ts.o obj-$(CONFIG_TOUCHSCREEN_GT9XX) += gt9xx/ +obj-$(CONFIG_TOUCHSCREEN_ST) += st/ diff --git a/drivers/input/touchscreen/gt9xx/goodix_tool.c b/drivers/input/touchscreen/gt9xx/goodix_tool.c index 1657f56558ce..ded8c88fdac9 100644 --- a/drivers/input/touchscreen/gt9xx/goodix_tool.c +++ b/drivers/input/touchscreen/gt9xx/goodix_tool.c @@ -1,7 +1,7 @@ /* drivers/input/touchscreen/goodix_tool.c * * 2010 - 2012 Goodix Technology. - * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2017, 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 as published by @@ -308,6 +308,7 @@ static ssize_t goodix_tool_write(struct file *filp, const char __user *userbuf, size_t count, loff_t *ppos) { s32 ret = 0; + u8 *dataptr = NULL; mutex_lock(&lock); ret = copy_from_user(&cmd_head, userbuf, CMD_HEAD_LENGTH); @@ -463,6 +464,11 @@ static ssize_t goodix_tool_write(struct file *filp, const char __user *userbuf, ret = CMD_HEAD_LENGTH; exit: + dataptr = cmd_head.data; + memset(&cmd_head, 0, sizeof(cmd_head)); + cmd_head.wr = 0xFF; + cmd_head.data = dataptr; + mutex_unlock(&lock); return ret; } diff --git a/drivers/input/touchscreen/st/Kconfig b/drivers/input/touchscreen/st/Kconfig new file mode 100644 index 000000000000..817faea01742 --- /dev/null +++ b/drivers/input/touchscreen/st/Kconfig @@ -0,0 +1,9 @@ +# +# STMicroelectronics touchscreen driver configuration +# + +config TOUCHSCREEN_ST_I2C + tristate "STMicroelectronics i2c touchscreen" + depends on TOUCHSCREEN_ST + help + This enables support for ST touch panel over I2C based touchscreens. diff --git a/drivers/input/touchscreen/st/Makefile b/drivers/input/touchscreen/st/Makefile new file mode 100644 index 000000000000..0aa7b4a364da --- /dev/null +++ b/drivers/input/touchscreen/st/Makefile @@ -0,0 +1,5 @@ +# +## Makefile for the STMicroelectronics touchscreen driver. +# + +obj-$(CONFIG_TOUCHSCREEN_ST_I2C) += fts.o fts_gui.o fts_driver_test.o fts_lib/ diff --git a/drivers/input/touchscreen/st/fts.c b/drivers/input/touchscreen/st/fts.c new file mode 100644 index 000000000000..bbe872001407 --- /dev/null +++ b/drivers/input/touchscreen/st/fts.c @@ -0,0 +1,2354 @@ +/* + * fts.c + * + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * marco.cali@st.com + * + * 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/device.h> + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/interrupt.h> +#include <linux/hrtimer.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/completion.h> + +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/regulator/consumer.h> + +#include <linux/notifier.h> +#include <linux/fb.h> + +#ifdef KERNEL_ABOVE_2_6_38 +#include <linux/input/mt.h> +#endif + +#include "fts.h" +#include "fts_lib/ftsCompensation.h" +#include "fts_lib/ftsIO.h" +#include "fts_lib/ftsError.h" +#include "fts_lib/ftsFlash.h" +#include "fts_lib/ftsFrame.h" +#include "fts_lib/ftsGesture.h" +#include "fts_lib/ftsTest.h" +#include "fts_lib/ftsTime.h" +#include "fts_lib/ftsTool.h" + +#define LINK_KOBJ_NAME "tp" + +/* + * Uncomment to use polling mode instead of interrupt mode. + * + */ +/* #define FTS_USE_POLLING_MODE */ + +/* + * Event installer helpers + */ +#define event_id(_e) EVENTID_##_e +#define handler_name(_h) fts_##_h##_event_handler + +#define install_handler(_i, _evt, _hnd) \ +do { \ + _i->event_dispatch_table[event_id(_evt)] = handler_name(_hnd); \ +} while (0) + +/* + * Asyncronouns command helper + */ +#define WAIT_WITH_TIMEOUT(_info, _timeout, _command) \ +do { \ + if (wait_for_completion_timeout(&_info->cmd_done, _timeout) == 0) { \ + dev_warn(_info->dev, "Waiting for %s command: timeout\n", \ + #_command); \ + } \ +} while (0) + +#ifdef KERNEL_ABOVE_2_6_38 +#define TYPE_B_PROTOCOL +#endif + +#if defined(SCRIPTLESS) || defined(DRIVER_TEST) +static struct class *fts_cmd_class; +#endif + +extern chipInfo ftsInfo; + +unsigned char tune_version_same; + +char tag[8] = "[ FTS ]\0"; + +static u32 *typeOfComand; +static int numberParameters; +static int feature_feasibility = ERROR_OP_NOT_ALLOW; +#ifdef PHONE_GESTURE +static u8 mask[GESTURE_MASK_SIZE+2]; +#endif +static void fts_interrupt_enable(struct fts_ts_info *info); +static int fts_init_hw(struct fts_ts_info *info); +static int fts_mode_handler(struct fts_ts_info *info, int force); +static int fts_command(struct fts_ts_info *info, unsigned char cmd); + +static int fts_chip_initialization(struct fts_ts_info *info); + +void touch_callback(unsigned int status) +{ + /* Empty */ +} + +unsigned int le_to_uint(const unsigned char *ptr) +{ + return (unsigned int) ptr[0] + (unsigned int) ptr[1] * 0x100; +} + +unsigned int be_to_uint(const unsigned char *ptr) +{ + return (unsigned int) ptr[1] + (unsigned int) ptr[0] * 0x100; +} + +/* force update firmware*/ +static ssize_t fts_fw_control_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count){ + int ret, mode; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + /* reading out firmware upgrade mode */ + sscanf(buf, "%d", &mode); +#ifdef FTM3_CHIP + ret = flashProcedure(PATH_FILE_FW, mode, !mode); +#else + ret = flashProcedure(PATH_FILE_FW, mode, 1); +#endif + info->fwupdate_stat = ret; + + if (ret < OK) + logError(1, "%s %s :Unable to upgrade firmware\n", tag, __func__); + return count; +} + +static ssize_t fts_sysfs_config_id_show(struct device *dev, struct device_attribute *attr, + char *buf) { + int error; + + error = snprintf(buf, TSP_BUF_SIZE, "%x.%x\n", ftsInfo.u16_fwVer, ftsInfo.u16_cfgId); + return error; +} + +static ssize_t fts_sysfs_fwupdate_show(struct device *dev, struct device_attribute *attr, + char *buf) { + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + /* fwupdate_stat: ERROR code Returned by flashProcedure. */ + return snprintf(buf, TSP_BUF_SIZE, "%08X\n", info->fwupdate_stat); +} + +static ssize_t fts_fw_test_show(struct device *dev, struct device_attribute *attr, + char *buf) { + + Firmware fw; + int ret; + + fw.data = NULL; + ret = readFwFile(PATH_FILE_FW, &fw, 0); + + if (ret < OK) { + logError(1, "%s: Error during reading FW file! ERROR %08X\n", tag, ret); + } else + logError(1, "%s: fw_version = %04X, config_version = %04X, size = %d bytes\n", + tag, fw.fw_ver, fw.config_id, fw.data_size); + + kfree(fw.data); + return 0; +} + +/* TODO: edit this function according to the features policy to allow during the screen on/off */ +int check_feature_feasibility(struct fts_ts_info *info, unsigned int feature) +{ + int res = ERROR_OP_NOT_ALLOW; + + if (info->resume_bit == 0) { + switch (feature) { +#ifdef PHONE_GESTURE + case FEAT_GESTURE: + res = OK; + break; +#endif + default: + logError(1, "%s %s: Feature not allowed in this operating mode! ERROR %08X\n", tag, __func__, res); + break; + + } + } else{ + switch (feature) { +#ifdef PHONE_GESTURE + case FEAT_GESTURE: +#endif + case FEAT_GLOVE: + /* glove mode can only activate during sense on */ + res = OK; + break; + + default: + logError(1, "%s %s: Feature not allowed in this operating mode! ERROR %08X\n", tag, __func__, res); + break; + + } + } + + return res; + +} + +static ssize_t fts_feature_enable_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + char *p = (char *)buf; + unsigned int temp; + int res = OK; + + if ((count + 1) / 3 != 2) { + logError(1, "%s fts_feature_enable: Number of parameter wrong! %d > %d\n", + tag, (count + 1) / 3, 2); + } else{ + sscanf(p, "%02X ", &temp); + p += 3; + res = check_feature_feasibility(info, temp); + if (res >= OK) { + switch (temp) { +#ifdef PHONE_GESTURE + case FEAT_GESTURE: + sscanf(p, "%02X ", &info->gesture_enabled); + logError(1, "%s fts_feature_enable: Gesture Enabled = %d\n", tag, + info->gesture_enabled); + break; +#endif + case FEAT_GLOVE: + sscanf(p, "%02X ", &info->glove_enabled); + logError(1, "%s fts_feature_enable: Glove Enabled = %d\n", + tag, info->glove_enabled); + + break; + + default: + logError(1, "%s fts_feature_enable: Feature %02X not valid! ERROR %08X\n", tag, temp, ERROR_OP_NOT_ALLOW); + + } + feature_feasibility = res; + } + + } + return count; +} + +static ssize_t fts_feature_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0, res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + if (feature_feasibility >= OK) + res = fts_mode_handler(info, 1); + else{ + res = feature_feasibility; + logError(1, "%s %s: Call before echo xx xx > feature_enable with a correct feature! ERROR %08X\n", tag, __func__, res); + } + + all_strbuff = (u8 *) kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", res); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else{ + logError(1, "%s fts_feature_enable_show: Unable to allocate all_strbuff! ERROR %08X\n", tag, ERROR_ALLOC); + } + + feature_feasibility = ERROR_OP_NOT_ALLOW; + return count; +} + +#ifdef PHONE_GESTURE +static ssize_t fts_gesture_mask_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0, res; + + if (mask[0] == 0) { + res = ERROR_OP_NOT_ALLOW; + logError(1, "%s %s: Call before echo enable/disable xx xx .... > gesture_mask with a correct number of parameters! ERROR %08X\n", tag, __func__, res); + + } else{ + res = fts_disableInterrupt(); + if (res >= OK) { + if (mask[1] == FEAT_ENABLE) + res = enableGesture(&mask[2], mask[0]); + else{ + if (mask[1] == FEAT_DISABLE) + res = disableGesture(&mask[2], mask[0]); + else + res = ERROR_OP_NOT_ALLOW; + } + if (res < OK) { + logError(1, "%s fts_gesture_mask_store: ERROR %08X\n", tag, res); + } + } + res |= fts_enableInterrupt(); + } + + all_strbuff = (u8 *) kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", res); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else{ + logError(1, "%s fts_gesture_mask_show: Unable to allocate all_strbuff! ERROR %08X\n", tag, ERROR_ALLOC); + } + + mask[0] = 0; + return count; +} + +static ssize_t fts_gesture_mask_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + char *p = (char *)buf; + int n; + unsigned int temp; + + if ((count + 1) / 3 > GESTURE_MASK_SIZE+1) { + logError(1, "%s fts_gesture_mask_store: Number of bytes of parameter wrong! %d > (enable/disable + %d )\n", tag, (count + 1) / 3, GESTURE_MASK_SIZE); + mask[0] = 0; + } else { + mask[0] = ((count + 1) / 3) - 1; + for (n = 1; n <= (count + 1) / 3; n++) { + sscanf(p, "%02X ", &temp); + p += 3; + mask[n] = (u8)temp; + logError(1, "%s mask[%d] = %02X\n", tag, n, mask[n]); + + } + + } + + return count; +} +#endif + +/************************ PRODUCTION TEST **********************************/ +static ssize_t stm_fts_cmd_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { + int n; + char *p = (char *) buf; + + typeOfComand = (u32 *) kmalloc(8 * sizeof (u32), GFP_KERNEL); + if (typeOfComand == NULL) { + logError(1, "%s impossible to allocate typeOfComand!\n", tag); + return count; + } + memset(typeOfComand, 0, 8 * sizeof (u32)); + + logError(1, "%s\n", tag); + for (n = 0; n < (count + 1) / 3; n++) { + sscanf(p, "%02X ", &typeOfComand[n]); + p += 3; + logError(1, "%s typeOfComand[%d] = %02X\n", tag, n, typeOfComand[n]); + + } + + numberParameters = n; + logError(1, "%s Number of Parameters = %d\n", tag, numberParameters); + return count; +} + +static ssize_t stm_fts_cmd_show(struct device *dev, struct device_attribute *attr, + char *buf) { + char buff[CMD_STR_LEN] = {0}; + int res, j, doClean = 0, count; + + int size = 6 * 2; + u8 *all_strbuff = NULL; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + MutualSenseData compData; + SelfSenseData comData; + MutualSenseFrame frameMS; + SelfSenseFrame frameSS; + + /* struct used for defining which test + *perform during the production test + */ + TestToDo todoDefault; + + todoDefault.MutualRaw = 1; + todoDefault.MutualRawGap = 1; + todoDefault.MutualCx1 = 0; + todoDefault.MutualCx2 = 1; + todoDefault.MutualCx2Adj = 1; + todoDefault.MutualCxTotal = 0; + todoDefault.MutualCxTotalAdj = 0; + + todoDefault.MutualKeyRaw = 0; + todoDefault.MutualKeyCx1 = 0; + todoDefault.MutualKeyCx2 = 0; + todoDefault.MutualKeyCxTotal = 0; + + todoDefault.SelfForceRaw = 1; + todoDefault.SelfForceRawGap = 0; + todoDefault.SelfForceIx1 = 0; + todoDefault.SelfForceIx2 = 0; + todoDefault.SelfForceIx2Adj = 0; + todoDefault.SelfForceIxTotal = 1; + todoDefault.SelfForceIxTotalAdj = 0; + todoDefault.SelfForceCx1 = 0; + todoDefault.SelfForceCx2 = 0; + todoDefault.SelfForceCx2Adj = 0; + todoDefault.SelfForceCxTotal = 0; + todoDefault.SelfForceCxTotalAdj = 0; + + todoDefault.SelfSenseRaw = 1; + todoDefault.SelfSenseRawGap = 0; + todoDefault.SelfSenseIx1 = 0; + todoDefault.SelfSenseIx2 = 0; + todoDefault.SelfSenseIx2Adj = 0; + todoDefault.SelfSenseIxTotal = 1; + todoDefault.SelfSenseIxTotalAdj = 0; + todoDefault.SelfSenseCx1 = 0; + todoDefault.SelfSenseCx2 = 0; + todoDefault.SelfSenseCx2Adj = 0; + todoDefault.SelfSenseCxTotal = 0; + todoDefault.SelfSenseCxTotalAdj = 0; + + if (numberParameters >= 1 && typeOfComand != NULL) { + res = fts_disableInterrupt(); + if (res < 0) { + logError(0, "%s fts_disableInterrupt: ERROR %08X\n", tag, res); + res = (res | ERROR_DISABLE_INTER); + goto END; + } + + res = fb_unregister_client(&info->notifier); + if (res < 0) { + logError(1, "%s ERROR: unregister notifier failed!\n", tag); + goto END; + } + + switch (typeOfComand[0]) { + /*ITO TEST*/ + case 0x01: + res = production_test_ito(); + break; + /*PRODUCTION TEST*/ + case 0x00: + if (ftsInfo.u32_mpPassFlag != INIT_MP) { + logError(0, "%s MP Flag not set!\n", tag, res); + res = production_test_main(LIMITS_FILE, 1, 1, &todoDefault, INIT_MP); + } else{ + logError(0, "%s MP Flag set!\n", tag, res); + res = production_test_main(LIMITS_FILE, 1, 0, &todoDefault, INIT_MP); + } + break; + /*read mutual raw*/ + case 0x13: + logError(0, "%s Get 1 MS Frame\n", tag); + /* res = getMSFrame(ADDR_RAW_TOUCH, &frame, 0); */ + res = getMSFrame2(MS_TOUCH_ACTIVE, &frameMS); + if (res < 0) { + logError(0, "%s Error while taking the MS frame... ERROR %02X\n", tag, res); + + } else { + logError(0, "%s The frame size is %d words\n", tag, res); + size = (res * sizeof (short) + 8)*2; + /* set res to OK because if getMSFrame is + * successful res = number of words read + */ + res = OK; + } + break; + /*read self raw*/ + case 0x15: + logError(0, "%s Get 1 SS Frame\n", tag); + res = getSSFrame2(SS_TOUCH, &frameSS); + + if (res < OK) { + logError(0, "%s Error while taking the SS frame... ERROR %02X\n", tag, res); + + } else { + logError(0, "%s The frame size is %d words\n", tag, res); + size = (res * sizeof (short) + 8)*2+1; + /* set res to OK because if getMSFrame is + * successful res = number of words read + */ + res = OK; + } + + break; + + case 0x14: /*read mutual comp data */ + logError(0, "%s Get MS Compensation Data\n", tag); + res = readMutualSenseCompensationData(MS_TOUCH_ACTIVE, &compData); + + if (res < 0) { + logError(0, "%s Error reading MS compensation data ERROR %02X\n", tag, res); + } else { + logError(0, "%s MS Compensation Data Reading Finished!\n", tag); + size = ((compData.node_data_size + 9) * sizeof (u8))*2; + } + break; + + case 0x16: /* read self comp data */ + logError(0, "%s Get SS Compensation Data...\n", tag); + res = readSelfSenseCompensationData(SS_TOUCH, &comData); + if (res < 0) { + logError(0, "%s Error reading SS compensation data ERROR %02X\n", tag, res); + } else { + logError(0, "%s SS Compensation Data Reading Finished!\n", tag); + size = ((comData.header.force_node + comData.header.sense_node)*2 + 12) * sizeof (u8)*2; + } + break; + + case 0x03: /* MS Raw DATA TEST */ + res = fts_system_reset(); + if (res >= OK) + res = production_test_ms_raw(LIMITS_FILE, 1, &todoDefault); + break; + + case 0x04: /* MS CX DATA TEST */ + res = fts_system_reset(); + if (res >= OK) + res = production_test_ms_cx(LIMITS_FILE, 1, &todoDefault); + break; + + case 0x05: /* SS RAW DATA TEST */ + res = fts_system_reset(); + if (res >= OK) + res = production_test_ss_raw(LIMITS_FILE, 1, &todoDefault); + break; + + case 0x06: /* SS IX CX DATA TEST */ + res = fts_system_reset(); + if (res >= OK) + res = production_test_ss_ix_cx(LIMITS_FILE, 1, &todoDefault); + break; + + case 0xF0: + case 0xF1: /* TOUCH ENABLE/DISABLE */ + doClean = (int) (typeOfComand[0]&0x01); + res = cleanUp(doClean); + + break; + + default: + logError(1, "%s COMMAND NOT VALID!! Insert a proper value ...\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + + doClean = fts_enableInterrupt(); + if (doClean < 0) { + logError(0, "%s fts_enableInterrupt: ERROR %08X\n", tag, (doClean|ERROR_ENABLE_INTER)); + } + } else { + logError(1, "%s NO COMMAND SPECIFIED!!! do: 'echo [cmd_code] [args] > stm_fts_cmd' before looking for result!\n", tag); + res = ERROR_OP_NOT_ALLOW; + typeOfComand = NULL; + + } + + if (fb_register_client(&info->notifier) < 0) { + logError(1, "%s ERROR: register notifier failed!\n", tag); + } + +END: /* here start the reporting phase, assembling the data to send in the file node */ + all_strbuff = (u8 *) kmalloc(size, GFP_KERNEL); + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", res); + strlcat(all_strbuff, buff, size); + + if (res >= OK) { + /*all the other cases are already fine printing only the res.*/ + switch (typeOfComand[0]) { + case 0x13: + snprintf(buff, sizeof (buff), "%02X", (u8) frameMS.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", (u8) frameMS.header.sense_node); + strlcat(all_strbuff, buff, size); + + for (j = 0; j < frameMS.node_data_size; j++) { + snprintf(buff, sizeof(buff), "%04X", frameMS.node_data[j]); + strlcat(all_strbuff, buff, size); + } + + kfree(frameMS.node_data); + break; + + case 0x15: + snprintf(buff, sizeof(buff), "%02X", (u8) frameSS.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", (u8) frameSS.header.sense_node); + strlcat(all_strbuff, buff, size); + + /* Copying self raw data Force */ + for (j = 0; j < frameSS.header.force_node; j++) { + snprintf(buff, sizeof(buff), "%04X", frameSS.force_data[j]); + strlcat(all_strbuff, buff, size); + } + + /* Copying self raw data Sense */ + for (j = 0; j < frameSS.header.sense_node; j++) { + snprintf(buff, sizeof(buff), "%04X", frameSS.sense_data[j]); + strlcat(all_strbuff, buff, size); + } + + kfree(frameSS.force_data); + kfree(frameSS.sense_data); + break; + + case 0x14: + snprintf(buff, sizeof(buff), "%02X", (u8) compData.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", (u8) compData.header.sense_node); + strlcat(all_strbuff, buff, size); + + /* Cpying CX1 value */ + snprintf(buff, sizeof(buff), "%02X", compData.cx1); + strlcat(all_strbuff, buff, size); + + /* Copying CX2 values */ + for (j = 0; j < compData.node_data_size; j++) { + snprintf(buff, sizeof(buff), "%02X", *(compData.node_data + j)); + strlcat(all_strbuff, buff, size); + } + + kfree(compData.node_data); + break; + + case 0x16: + snprintf(buff, sizeof(buff), "%02X", comData.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", comData.header.sense_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", comData.f_ix1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", comData.s_ix1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", comData.f_cx1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", comData.s_cx1); + strlcat(all_strbuff, buff, size); + + /* Copying IX2 Force */ + for (j = 0; j < comData.header.force_node; j++) { + snprintf(buff, sizeof(buff), "%02X", comData.ix2_fm[j]); + strlcat(all_strbuff, buff, size); + } + + /* Copying IX2 Sense */ + for (j = 0; j < comData.header.sense_node; j++) { + snprintf(buff, sizeof(buff), "%02X", comData.ix2_sn[j]); + strlcat(all_strbuff, buff, size); + } + + /* Copying CX2 Force */ + for (j = 0; j < comData.header.force_node; j++) { + snprintf(buff, sizeof(buff), "%02X", comData.cx2_fm[j]); + strlcat(all_strbuff, buff, size); + } + + /* Copying CX2 Sense */ + for (j = 0; j < comData.header.sense_node; j++) { + snprintf(buff, sizeof(buff), "%02X", comData.cx2_sn[j]); + strlcat(all_strbuff, buff, size); + } + + kfree(comData.ix2_fm); + kfree(comData.ix2_sn); + kfree(comData.cx2_fm); + kfree(comData.cx2_sn); + break; + + default: + break; + + } + } + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + numberParameters = 0; /* need to reset the number of parameters + * in order to wait the next command, comment + *if you want to repeat the last command sent + *just doing a cat + */ + /* logError(0,"%s numberParameters = %d\n", tag, numberParameters); */ + kfree(all_strbuff); + + kfree(typeOfComand); + return count; + +} + +static DEVICE_ATTR(fwupdate, (S_IRUGO | S_IWUSR | S_IWGRP), fts_sysfs_fwupdate_show, fts_fw_control_store); +static DEVICE_ATTR(appid, (S_IRUGO), fts_sysfs_config_id_show, NULL); +static DEVICE_ATTR(fw_file_test, (S_IRUGO), fts_fw_test_show, NULL); +static DEVICE_ATTR(stm_fts_cmd, (S_IRUGO | S_IWUSR | S_IWGRP), stm_fts_cmd_show, stm_fts_cmd_store); +static DEVICE_ATTR(feature_enable, (S_IRUGO | S_IWUSR | S_IWGRP), fts_feature_enable_show, fts_feature_enable_store); +#ifdef PHONE_GESTURE +static DEVICE_ATTR(gesture_mask, (S_IRUGO | S_IWUSR | S_IWGRP), fts_gesture_mask_show, fts_gesture_mask_store); +#endif +/* /sys/devices/soc.0/f9928000.i2c/i2c-6/6-0049 */ +static struct attribute *fts_attr_group[] = { + &dev_attr_fwupdate.attr, + &dev_attr_appid.attr, + &dev_attr_fw_file_test.attr, + /* &dev_attr_touch_debug.attr, */ + &dev_attr_stm_fts_cmd.attr, + &dev_attr_feature_enable.attr, +#ifdef PHONE_GESTURE + &dev_attr_gesture_mask.attr, +#endif + NULL, +}; + +static int fts_command(struct fts_ts_info *info, unsigned char cmd) +{ + unsigned char regAdd; + int ret; + + regAdd = cmd; + + ret = fts_writeCmd(®Add, sizeof (regAdd)); /* 0 = ok */ + + logError(0, "%s Issued command 0x%02x, return value %08X\n", cmd, ret); + + return ret; +} + +void fts_input_report_key(struct fts_ts_info *info, int key_code) +{ + mutex_lock(&info->input_report_mutex); + input_report_key(info->input_dev, key_code, 1); + input_sync(info->input_dev); + input_report_key(info->input_dev, key_code, 0); + input_sync(info->input_dev); + mutex_unlock(&info->input_report_mutex); +} + +/* + * New Interrupt handle implementation + */ + +static inline unsigned char *fts_next_event(unsigned char *evt) +{ + /* Nothing to do with this event, moving to the next one */ + evt += FIFO_EVENT_SIZE; + + /* the previous one was the last event ? */ + return (evt[-1] & 0x1F) ? evt : NULL; +} + +/* EventId : 0x00 */ +static unsigned char *fts_nop_event_handler(struct fts_ts_info *info, + unsigned char *event) { + /* logError(1, "%s %s Doing nothing for event = %02X %02X %02X %02X %02X %02X %02X %02X\n", + * tag, __func__, event[0], event[1], event[2], event[3], event[4], event[5], event[6], event[7]); + */ + return fts_next_event(event); +} + +/* EventId : 0x03 */ +static unsigned char *fts_enter_pointer_event_handler(struct fts_ts_info *info, + unsigned char *event) { + unsigned char touchId, touchcount; + int x, y, z; + + if (!info->resume_bit) + goto no_report; + + touchId = event[1] & 0x0F; + touchcount = (event[1] & 0xF0) >> 4; + + __set_bit(touchId, &info->touch_id); + + x = (event[2] << 4) | (event[4] & 0xF0) >> 4; + y = (event[3] << 4) | (event[4] & 0x0F); + z = (event[5] & 0x3F); + + if (x == X_AXIS_MAX) + x--; + + if (y == Y_AXIS_MAX) + y--; + + input_mt_slot(info->input_dev, touchId); + input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, 1); + logError(0, "%s %s : TouchID = %d,Touchcount = %d\n", tag, __func__, touchId, touchcount); + if (touchcount == 1) { + input_report_key(info->input_dev, BTN_TOUCH, 1); + input_report_key(info->input_dev, BTN_TOOL_FINGER, 1); + } + /* input_report_abs(info->input_dev, ABS_MT_TRACKING_ID, touchId); */ + input_report_abs(info->input_dev, ABS_MT_POSITION_X, x); + input_report_abs(info->input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(info->input_dev, ABS_MT_TOUCH_MAJOR, z); + input_report_abs(info->input_dev, ABS_MT_TOUCH_MINOR, z); + input_report_abs(info->input_dev, ABS_MT_PRESSURE, z); + logError(0, "%s %s : Event 0x%02x - ID[%d], (x, y, z) = (%3d, %3d, %3d)\n", tag, __func__, *event, touchId, x, y, z); + +no_report: + return fts_next_event(event); +} + +/* EventId : 0x04 */ +static unsigned char *fts_leave_pointer_event_handler(struct fts_ts_info *info, + unsigned char *event) { + unsigned char touchId, touchcount; + + touchId = event[1] & 0x0F; + touchcount = (event[1] & 0xF0) >> 4; + + __clear_bit(touchId, &info->touch_id); + + input_mt_slot(info->input_dev, touchId); + input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, 0); + logError(0, "%s %s : TouchID = %d, Touchcount = %d\n", tag, __func__, touchId, touchcount); + if (touchcount == 0) { + input_report_key(info->input_dev, BTN_TOUCH, 0); + input_report_key(info->input_dev, BTN_TOOL_FINGER, 0); + } + + input_report_abs(info->input_dev, ABS_MT_TRACKING_ID, -1); + logError(0, "%s %s : Event 0x%02x - release ID[%d]\n", tag, __func__, event[0], touchId); + + return fts_next_event(event); +} + +/* EventId : 0x05 */ +#define fts_motion_pointer_event_handler fts_enter_pointer_event_handler + +#ifdef PHONE_KEY +/* EventId : 0x0E */ +static unsigned char *fts_key_status_event_handler(struct fts_ts_info *info, unsigned char *event) +{ + int value; + logError(0, "%s %s Received event %02X %02X %02X %02X %02X %02X %02X %02X\n", tag, __func__, event[0], event[1], event[2], event[3], event[4], event[5], event[6], event[7]); + /* TODO: the customer should handle the events coming from the keys according his needs (this is an example that report only the single pressure of one key at time) */ + if (event[2] != 0) { /* event[2] contain the bitmask of the keys that are actually pressed */ + switch (event[2]) { + case KEY1: + value = KEY_HOMEPAGE; + logError(0, "%s %s: Button HOME !\n", tag, __func__); + break; + + case KEY2: + value = KEY_BACK; + logError(0, "%s %s: Button Back !\n", tag, __func__); + break; + + case KEY3: + value = KEY_MENU; + logError(0, "%s %s: Button Menu !\n", tag, __func__); + break; + + default: + logError(0, "%s %s: No valid Button ID or more than one key pressed!\n", tag, __func__); + goto done; + } + + fts_input_report_key(info, value); + } else{ + logError(0, "%s %s: All buttons released!\n", tag, __func__); + } +done: + return fts_next_event(event); +} +#endif + +/* EventId : 0x0F */ +static unsigned char *fts_error_event_handler(struct fts_ts_info *info, + unsigned char *event) { + int error = 0, i = 0; + logError(0, "%s %s Received event 0x%02x 0x%02x\n", tag, __func__, event[0], event[1]); + + switch (event[1]) { + case EVENT_TYPE_ESD_ERROR: + { + for (i = 0; i < TOUCH_ID_MAX; i++) { + input_mt_slot(info->input_dev, i); + input_mt_report_slot_state(info->input_dev, (i < FINGER_MAX) ? MT_TOOL_FINGER : MT_TOOL_PEN, 0); + } + input_sync(info->input_dev); + + fts_chip_powercycle(info); + + error = fts_system_reset(); + error |= fts_mode_handler(info, 0); + error |= fts_enableInterrupt(); + if (error < OK) { + logError(1, "%s %s Cannot restore the device ERROR %08X\n", tag, __func__, error); + } + } + break; + case EVENT_TYPE_WATCHDOG_ERROR: /* watch dog timer */ + { + if (event[2] == 0) { + /* before reset clear all slot */ + for (i = 0; i < TOUCH_ID_MAX; i++) { + input_mt_slot(info->input_dev, i); + input_mt_report_slot_state(info->input_dev, + (i < FINGER_MAX) ? MT_TOOL_FINGER : MT_TOOL_PEN, 0); + } + input_sync(info->input_dev); + error = fts_system_reset(); + error |= fts_mode_handler(info, 0); + error |= fts_enableInterrupt(); + if (error < OK) { + logError(1, "%s %s Cannot reset the device ERROR %08X\n", tag, __func__, error); + } + } + } + break; + + } + return fts_next_event(event); +} + +/* EventId : 0x10 */ +static unsigned char *fts_controller_ready_event_handler( + struct fts_ts_info *info, unsigned char *event) { + int error; + logError(0, "%s %s Received event 0x%02x\n", tag, __func__, event[0]); + info->touch_id = 0; + input_sync(info->input_dev); + setSystemResettedUp(1); + setSystemResettedDown(1); + error = fts_mode_handler(info, 0); + if (error < OK) { + logError(1, "%s %s Cannot restore the device status ERROR %08X\n", tag, __func__, error); + } + return fts_next_event(event); +} + +/* EventId : 0x16 */ +static unsigned char *fts_status_event_handler( + struct fts_ts_info *info, unsigned char *event) { + /* logError(1, "%s Received event 0x%02x\n", tag, event[0]); */ + + switch (event[1]) { + case EVENT_TYPE_MS_TUNING_CMPL: + case EVENT_TYPE_SS_TUNING_CMPL: + case FTS_FORCE_CAL_SELF_MUTUAL: + case FTS_FLASH_WRITE_CONFIG: + case FTS_FLASH_WRITE_COMP_MEMORY: + case FTS_FORCE_CAL_SELF: + case FTS_WATER_MODE_ON: + case FTS_WATER_MODE_OFF: + default: + logError(1, "%s %s Received unhandled status event = %02X %02X %02X %02X %02X %02X %02X %02X\n", tag, __func__, event[0], event[1], event[2], event[3], event[4], event[5], event[6], event[7]); + break; + } + + return fts_next_event(event); +} + +#ifdef PHONE_GESTURE +static unsigned char *fts_gesture_event_handler(struct fts_ts_info *info, unsigned char *event) +{ + unsigned char touchId; + int value; + + logError(0, "%s gesture event data: %02X %02X %02X %02X %02X %02X %02X %02X\n", tag, event[0], event[1], event[2], event[3], event[4], event[5], event[6], event[7]); + + if (event[1] == 0x03) { + + logError(1, "%s %s: Gesture ID %02X enable_status = %02X\n", tag, __func__, event[2], event[3]); + + } + if (event[1] == EVENT_TYPE_ENB && event[2] == 0x00) { + switch (event[3]) { + case GESTURE_ENABLE: + logError(1, "%s %s: Gesture Enabled! res = %02X\n", tag, __func__, event[4]); + break; + + case GESTURE_DISABLE: + logError(1, "%s %s: Gesture Disabled! res = %02X\n", tag, __func__, event[4]); + break; + + default: + logError(1, "%s %s: Event not Valid!\n", tag, __func__); + + } + + } + + /* always use touchId zero */ + touchId = 0; + __set_bit(touchId, &info->touch_id); + + if (event[0] == EVENTID_GESTURE && (event[1] == EVENT_TYPE_GESTURE_DTC1 || event[1] == EVENT_TYPE_GESTURE_DTC2)) { + + switch (event[2]) { + case GES_ID_DBLTAP: + value = KEY_WAKEUP; + logError(0, "%s %s: double tap !\n", tag, __func__); + break; + + case GES_ID_AT: + value = KEY_WWW; + logError(0, "%s %s: @ !\n", tag, __func__); + break; + + case GES_ID_C: + value = KEY_C; + logError(0, "%s %s: C !\n", tag, __func__); + break; + + case GES_ID_E: + value = KEY_E; + logError(0, "%s %s: e !\n", tag, __func__); + break; + + case GES_ID_F: + value = KEY_F; + logError(0, "%s %s: F !\n", tag, __func__); + break; + + case GES_ID_L: + value = KEY_L; + logError(0, "%s %s: L !\n", tag, __func__); + break; + + case GES_ID_M: + value = KEY_M; + logError(0, "%s %s: M !\n", tag, __func__); + break; + + case GES_ID_O: + value = KEY_O; + logError(0, "%s %s: O !\n", tag, __func__); + break; + + case GES_ID_S: + value = KEY_S; + logError(0, "%s %s: S !\n", tag, __func__); + break; + + case GES_ID_V: + value = KEY_V; + logError(0, "%s %s: V !\n", tag, __func__); + break; + + case GES_ID_W: + value = KEY_W; + logError(0, "%s %s: W !\n", tag, __func__); + break; + + case GES_ID_Z: + value = KEY_Z; + logError(0, "%s %s: Z !\n", tag, __func__); + break; + + case GES_ID_HFLIP_L2R: + value = KEY_RIGHT; + logError(0, "%s %s: -> !\n", tag, __func__); + break; + + case GES_ID_HFLIP_R2L: + value = KEY_LEFT; + logError(0, "%s %s: <- !\n", tag, __func__); + break; + + case GES_ID_VFLIP_D2T: + value = KEY_UP; + logError(0, "%s %s: UP !\n", tag, __func__); + break; + + case GES_ID_VFLIP_T2D: + value = KEY_DOWN; + logError(0, "%s %s: DOWN !\n", tag, __func__); + break; + + case GES_ID_CUST1: + value = KEY_F1; + logError(0, "%s %s: F1 !\n", tag, __func__); + break; + + case GES_ID_CUST2: + value = KEY_F1; + logError(0, "%s %s: F2 !\n", tag, __func__); + break; + + case GES_ID_CUST3: + value = KEY_F3; + logError(0, "%s %s: F3 !\n", tag, __func__); + break; + + case GES_ID_CUST4: + value = KEY_F1; + logError(0, "%s %s: F4 !\n", tag, __func__); + break; + + case GES_ID_CUST5: + value = KEY_F1; + logError(0, "%s %s: F5 !\n", tag, __func__); + break; + + case GES_ID_LEFTBRACE: + value = KEY_LEFTBRACE; + logError(0, "%s %s: < !\n", tag, __func__); + break; + + case GES_ID_RIGHTBRACE: + value = KEY_RIGHTBRACE; + logError(0, "%s %s: > !\n", tag, __func__); + break; + default: + logError(0, "%s %s: No valid GestureID!\n", tag, __func__); + goto gesture_done; + + } + + fts_input_report_key(info, value); + + gesture_done: + /* Done with gesture event, clear bit. */ + __clear_bit(touchId, &info->touch_id); + } + + return fts_next_event(event); +} +#endif + +/* EventId : 0x05 */ +#define fts_motion_pointer_event_handler fts_enter_pointer_event_handler + +/* + * This handler is called each time there is at least + * one new event in the FIFO + */ +static void fts_event_handler(struct work_struct *work) +{ + struct fts_ts_info *info; + int error, error1; + int left_events; + unsigned char regAdd; + unsigned char data[FIFO_EVENT_SIZE * (FIFO_DEPTH)] = {0}; + unsigned char *event = NULL; + unsigned char eventId; + event_dispatch_handler_t event_handler; + + info = container_of(work, struct fts_ts_info, work); + /* + * to avoid reading all FIFO, we read the first event and + * then check how many events left in the FIFO + */ + + + regAdd = FIFO_CMD_READONE; + error = fts_readCmd(®Add, + sizeof (regAdd), data, FIFO_EVENT_SIZE); + + if (!error) { + + left_events = data[7] & 0x1F; + if ((left_events > 0) && (left_events < FIFO_DEPTH)) { + /* + * Read remaining events. + */ + regAdd = FIFO_CMD_READALL; + + error1 = fts_readCmd(®Add, sizeof (regAdd), + &data[FIFO_EVENT_SIZE], + left_events * FIFO_EVENT_SIZE); + /* + * Got an error reading remaining events, + * process at least * the first one that was + * reading fine. + */ + if (error1) + data[7] &= 0xE0; + } + + /* At least one event is available */ + event = data; + do { + eventId = *event; + event_handler = info->event_dispatch_table[eventId]; + + if (eventId < EVENTID_LAST) { + event = event_handler(info, (event)); + } else { + event = fts_next_event(event); + } + input_sync(info->input_dev); + } while (event); + } + + /* + * re-enable interrupts + */ + fts_interrupt_enable(info); +} + +static int cx_crc_check(void) +{ + unsigned char regAdd1[3] = {FTS_CMD_HW_REG_R, ADDR_CRC_BYTE0, ADDR_CRC_BYTE1}; + unsigned char val; + unsigned char crc_status; + unsigned int error; + + error = fts_readCmd(regAdd1, sizeof (regAdd1), &val, 1); + if (error < OK) { + logError(1, "%s %s Cannot read crc status ERROR %08X\n", tag, __func__, error); + return error; + } + + crc_status = val & CRC_MASK; + if (crc_status != OK) { /* CRC error if crc_status!= 0 */ + logError(1, "%s %s CRC ERROR = %X\n", tag, __func__, crc_status); + } + + return crc_status; /* return OK if no CRC error, or a number >OK if crc error */ +} + +static void fts_fw_update_auto(struct work_struct *work) +{ + int retval = 0; + int retval1 = 0; + int ret; + struct fts_ts_info *info; + struct delayed_work *fwu_work = container_of(work, struct delayed_work, work); + int crc_status = 0; + int error = 0; + info = container_of(fwu_work, struct fts_ts_info, fwu_work); + + logError(1, "%s Fw Auto Update is starting...\n", tag); + + /* check CRC status */ + ret = cx_crc_check(); + if (ret > OK && ftsInfo.u16_fwVer == 0x0000) { + logError(1, "%s %s: CRC Error or NO FW!\n", tag, __func__); + crc_status = 1; + } else { + crc_status = 0; + logError(1, "%s %s: NO CRC Error or Impossible to read CRC register!\n", tag, __func__); + } +#ifdef FTM3_CHIP + retval = flashProcedure(PATH_FILE_FW, crc_status, !crc_status); +#else + retval = flashProcedure(PATH_FILE_FW, crc_status, 1); +#endif + if ((retval & 0xFF000000) == ERROR_FLASH_PROCEDURE) { + logError(1, "%s %s: firmware update failed and retry! ERROR %08X\n", tag, __func__, retval); + fts_chip_powercycle(info); /* power reset */ +#ifdef FTM3_CHIP + retval1 = flashProcedure(PATH_FILE_FW, crc_status, !crc_status); +#else + retval1 = flashProcedure(PATH_FILE_FW, crc_status, 1); +#endif + if ((retval1 & 0xFF000000) == ERROR_FLASH_PROCEDURE) { + logError(1, "%s %s: firmware update failed again! ERROR %08X\n", tag, __func__, retval1); + logError(1, "%s Fw Auto Update Failed!\n", tag); + /* return; */ + } + } + + if ((ftsInfo.u32_mpPassFlag != INIT_MP) && (ftsInfo.u32_mpPassFlag != INIT_FIELD)) + ret = ERROR_GET_INIT_STATUS; + else + ret = OK; + + if (ret == ERROR_GET_INIT_STATUS) { /* initialization status not correct or after FW complete update, do initialization. */ + error = fts_chip_initialization(info); + if (error < OK) { + logError(1, "%s %s Cannot initialize the chip ERROR %08X\n", tag, __func__, error); + } + } + error = fts_init_hw(info); + if (error < OK) { + logError(1, "%s Cannot initialize the hardware device ERROR %08X\n", tag, error); + } + + logError(1, "%s Fw Auto Update Finished!\n", tag); +} + +static int fts_chip_initialization(struct fts_ts_info *info) +{ + int ret2 = 0; + int retry; + int initretrycnt = 0; + + /* initialization error, retry initialization */ + for (retry = 0; retry <= INIT_FLAG_CNT; retry++) { + ret2 = production_test_initialization(); + if (ret2 == OK) { + ret2 = save_mp_flag(INIT_FIELD); + if (ret2 == OK) + break; + } + initretrycnt++; + logError(1, "%s initialization cycle count = %04d - ERROR %08X\n", tag, initretrycnt, ret2); + fts_chip_powercycle(info); + } + if (ret2 < OK) { /* initialization error */ + logError(1, "%s fts initialization failed 3 times\n", tag); + } + + return ret2; +} + +#ifdef FTS_USE_POLLING_MODE + +static enum hrtimer_restart fts_timer_func(struct hrtimer *timer) +{ + struct fts_ts_info *info = + container_of(timer, struct fts_ts_info, timer); + + queue_work(info->event_wq, &info->work); + return HRTIMER_NORESTART; +} +#else + +static irqreturn_t fts_interrupt_handler(int irq, void *handle) +{ + struct fts_ts_info *info = handle; + disable_irq_nosync(info->client->irq); + queue_work(info->event_wq, &info->work); + return IRQ_HANDLED; +} +#endif + +static int fts_interrupt_install(struct fts_ts_info *info) +{ + int i, error = 0; + + info->event_dispatch_table = kzalloc( + sizeof (event_dispatch_handler_t) * EVENTID_LAST, GFP_KERNEL); + + if (!info->event_dispatch_table) { + logError(1, "%s OOM allocating event dispatch table\n", tag); + return -ENOMEM; + } + + for (i = 0; i < EVENTID_LAST; i++) + info->event_dispatch_table[i] = fts_nop_event_handler; + + install_handler(info, ENTER_POINTER, enter_pointer); + install_handler(info, LEAVE_POINTER, leave_pointer); + install_handler(info, MOTION_POINTER, motion_pointer); + install_handler(info, ERROR_EVENT, error); + install_handler(info, CONTROL_READY, controller_ready); + install_handler(info, STATUS_UPDATE, status); +#ifdef PHONE_GESTURE + install_handler(info, GESTURE, gesture); +#endif +#ifdef PHONE_KEY + install_handler(info, KEY_STATUS, key_status); +#endif + + /* disable interrupts in any case */ + error = fts_disableInterrupt(); + +#ifdef FTS_USE_POLLING_MODE + logError(1, "%s Polling Mode\n"); + hrtimer_init(&info->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + info->timer.function = fts_timer_func; + hrtimer_start(&info->timer, ktime_set(1, 0), HRTIMER_MODE_REL); +#else + logError(1, "%s Interrupt Mode\n", tag); + if (request_irq(info->client->irq, fts_interrupt_handler, + IRQF_TRIGGER_LOW, info->client->name, + info)) { + logError(1, "%s Request irq failed\n", tag); + kfree(info->event_dispatch_table); + error = -EBUSY; + } /*else { + error = fts_enableInterrupt(); + }*/ +#endif + + return error; +} + +static void fts_interrupt_uninstall(struct fts_ts_info *info) +{ + + fts_disableInterrupt(); + + kfree(info->event_dispatch_table); +#ifdef FTS_USE_POLLING_MODE + hrtimer_cancel(&info->timer); +#else + free_irq(info->client->irq, info); +#endif +} + +static void fts_interrupt_enable(struct fts_ts_info *info) +{ +#ifdef FTS_USE_POLLING_MODE + hrtimer_start(&info->timer, + ktime_set(0, 10000000), HRTIMER_MODE_REL); +#else + enable_irq(info->client->irq); +#endif +} + +/* +static void fts_interrupt_disable(struct fts_ts_info *info) +{ +#ifdef FTS_USE_POLLING_MODE + hrtimer_cancel(&info->timer); +#else + disable_irq(info->client->irq); +#endif +} +*/ + +static int fts_init(struct fts_ts_info *info) +{ + int error; + + error = fts_system_reset(); + if (error < OK && error != (ERROR_TIMEOUT | ERROR_SYSTEM_RESET_FAIL)) { + logError(1, "%s Cannot reset the device! ERROR %08X\n", tag, error); + return error; + } + if (error == (ERROR_TIMEOUT | ERROR_SYSTEM_RESET_FAIL)) { + logError(1, "%s Setting default Chip INFO!\n", tag); + defaultChipInfo(0); + } else { + error = readChipInfo(0); /* system reset OK */ + if (error < OK) { + logError(1, "%s Cannot read Chip Info! ERROR %08X\n", tag, error); + } + } + + error = fts_interrupt_install(info); + + if (error != OK) + logError(1, "%s Init (1) error (ERROR = %08X)\n", error); + + return error; +} + +int fts_chip_powercycle(struct fts_ts_info *info) +{ + int error, i; + + logError(1, "%s %s: Power Cycle Starting...\n", tag, __func__); + if (info->pwr_reg) { + error = regulator_disable(info->pwr_reg); + if (error < 0) { + logError(1, "%s %s: Failed to disable DVDD regulator\n", tag, __func__); + } + } + + if (info->bus_reg) { + error = regulator_disable(info->bus_reg); + if (error < 0) { + logError(1, "%s %s: Failed to disable AVDD regulator\n", tag, __func__); + } + } + + if (info->bdata->reset_gpio != GPIO_NOT_DEFINED) + gpio_set_value(info->bdata->reset_gpio, 0); + + msleep(300); + if (info->pwr_reg) { + error = regulator_enable(info->bus_reg); + if (error < 0) { + logError(1, "%s %s: Failed to enable AVDD regulator\n", tag, __func__); + } + } + + if (info->bus_reg) { + error = regulator_enable(info->pwr_reg); + if (error < 0) { + logError(1, "%s %s: Failed to enable DVDD regulator\n", tag, __func__); + } + } + msleep(300); /* time needed by the regulators for reaching the regime values */ + + if (info->bdata->reset_gpio != GPIO_NOT_DEFINED) { + msleep(10); /* time to wait before bring up the reset gpio after the power up of the regulators */ + gpio_set_value(info->bdata->reset_gpio, 1); + /* msleep(300); */ + } + + /* before reset clear all slot */ + for (i = 0; i < TOUCH_ID_MAX; i++) { + input_mt_slot(info->input_dev, i); + input_mt_report_slot_state(info->input_dev, + (i < FINGER_MAX) ? MT_TOOL_FINGER : MT_TOOL_PEN, 0); + } + input_sync(info->input_dev); + + logError(1, "%s %s: Power Cycle Finished! ERROR CODE = %08x\n", tag, __func__, error); + setSystemResettedUp(1); + setSystemResettedDown(1); + return error; +} + +int fts_chip_powercycle2(struct fts_ts_info *info, unsigned long sleep) +{ + int error, i; + + logError(1, "%s %s: Power Cycle Starting...\n", tag, __func__); + if (info->pwr_reg) { + error = regulator_disable(info->pwr_reg); + if (error < 0) { + logError(1, "%s %s: Failed to disable DVDD regulator\n", tag, __func__); + } + } + + if (info->bus_reg) { + error = regulator_disable(info->bus_reg); + if (error < 0) { + logError(1, "%s %s: Failed to disable AVDD regulator\n", tag, __func__); + } + } + + if (info->bdata->reset_gpio != GPIO_NOT_DEFINED) + gpio_set_value(info->bdata->reset_gpio, 0); + + msleep(sleep); + if (info->pwr_reg) { + error = regulator_enable(info->bus_reg); + if (error < 0) { + logError(1, "%s %s: Failed to enable AVDD regulator\n", tag, __func__); + } + } + + if (info->bus_reg) { + error = regulator_enable(info->pwr_reg); + if (error < 0) { + logError(1, "%s %s: Failed to enable DVDD regulator\n", tag, __func__); + } + } + msleep(500); /*time needed by the regulators for reaching the regime values */ + + if (info->bdata->reset_gpio != GPIO_NOT_DEFINED) { + msleep(10); /*time to wait before bring up the reset gpio after the power up of the regulators */ + gpio_set_value(info->bdata->reset_gpio, 1); + /* msleep(300); */ + } + + /* before reset clear all slot */ + for (i = 0; i < TOUCH_ID_MAX; i++) { + input_mt_slot(info->input_dev, i); + input_mt_report_slot_state(info->input_dev, + (i < FINGER_MAX) ? MT_TOOL_FINGER : MT_TOOL_PEN, 0); + } + input_sync(info->input_dev); + + logError(1, "%s %s: Power Cycle Finished! ERROR CODE = %08x\n", tag, __func__, error); + setSystemResettedUp(1); + setSystemResettedDown(1); + return error; +} + +static int fts_init_hw(struct fts_ts_info *info) +{ + int error = 0; + + error = cleanUp(1); + if (error < OK) + logError(1, "%s Init (2) error (ERROR = %08X)\n", tag, error); + + info->mode = MODE_NORMAL; + + return error; +} + + /* + * TODO: change this function according with the needs of + *customer in temrs of feature to enable/disable + */ +static int fts_mode_handler(struct fts_ts_info *info, int force) +{ + int res = OK; + int ret = OK; + + logError(0, "%s %s: Mode Handler starting...\n", tag, __func__); + switch (info->resume_bit) { + case 0:/* screen down */ + logError(0, "%s %s: Screen OFF...\n", tag, __func__); +#ifdef PHONE_GESTURE + if (info->gesture_enabled == 1) { + logError(0, "%s %s: enter in gesture mode !\n", tag, __func__); + res = enterGestureMode(isSystemResettedDown()); + if (res >= OK) { + + info->mode = MODE_GESTURE; + /* return OK; */ + } else { + logError(1, "%s %s: enterGestureMode failed! ERROR %08X recovery in senseOff...\n", tag, __func__, res); + } + } +#endif + if (info->mode != MODE_GESTURE || info->gesture_enabled == 0) { + logError(0, "%s %s: Sense OFF!\n", tag, __func__); + res |= fts_command(info, FTS_CMD_MS_MT_SENSE_OFF); /* we need to use fts_command for speed reason (no need to check echo in this case and interrupt can be enabled) */ +#ifdef PHONE_KEY + logError(0, "%s %s: Key OFF!\n", tag, __func__); + res |= fts_command(info, FTS_CMD_MS_KEY_OFF); +#endif + + info->mode = MODE_SENSEOFF; + + } + setSystemResettedDown(0); + break; + + case 1: /* screen up */ + logError(0, "%s %s: Screen ON...\n", tag, __func__); + logError(0, "%s %s: Sense ON!\n", tag, __func__); + res |= fts_command(info, FTS_CMD_MS_MT_SENSE_ON); +#ifdef PHONE_KEY + logError(0, "%s %s: Key ON!\n", tag, __func__); + res |= fts_command(info, FTS_CMD_MS_KEY_ON); +#endif + info->mode = MODE_NORMAL; + + if (info->glove_enabled == FEAT_ENABLE || force == 1) { + if (isSystemResettedUp() || force == 1) { + logError(0, "%s %s: Glove Mode setting...\n", tag, __func__); + ret = featureEnableDisable(info->glove_enabled, FEAT_GLOVE); + if (ret < OK) { + logError(1, "%s %s: error during setting GLOVE_MODE! ERROR %08X\n", tag, __func__, ret); + } + res |= ret; + } + if (ret >= OK && info->glove_enabled == FEAT_ENABLE) { + info->mode = MODE_GLOVE; + logError(1, "%s %s: GLOVE_MODE Enabled!\n", tag, __func__); + } else{ + logError(1, "%s %s: GLOVE_MODE Disabled!\n", tag, __func__); + } + } + + setSystemResettedUp(0); + break; + + default: + logError(1, "%s %s: invalid resume_bit value = %d! ERROR %08X\n", tag, __func__, info->resume_bit, ERROR_OP_NOT_ALLOW); + res = ERROR_OP_NOT_ALLOW; + } + + logError(0, "%s %s: Mode Handler finished! res = %08X\n", tag, __func__, res); + return res; + +} + +static int fts_fb_state_chg_callback(struct notifier_block *nb, unsigned long val, void *data) +{ + struct fts_ts_info *info = container_of(nb, struct fts_ts_info, notifier); + struct fb_event *evdata = data; + int i; + unsigned int blank; + + if (val != FB_EVENT_BLANK) + return 0; + + logError(0, "%s %s: fts notifier begin!\n", tag, __func__); + + if (evdata && evdata->data && val == FB_EVENT_BLANK && info) { + + blank = *(int *) (evdata->data); + + switch (blank) { + case FB_BLANK_POWERDOWN: + if (info->sensor_sleep) + break; + + logError(0, "%s %s: FB_BLANK_POWERDOWN\n", tag, __func__); + + /* Release all slots */ + for (i = 0; i < TOUCH_ID_MAX; i++) { + input_mt_slot(info->input_dev, i); + input_mt_report_slot_state(info->input_dev, + (i < FINGER_MAX) ? MT_TOOL_FINGER : MT_TOOL_PEN, 0); + } + input_sync(info->input_dev); + + info->resume_bit = 0; + + fts_mode_handler(info, 0); + + info->sensor_sleep = true; + + fts_disableInterrupt(); + + break; + + case FB_BLANK_UNBLANK: + if (!info->sensor_sleep) + break; + + logError(0, "%s %s: FB_BLANK_UNBLANK\n", tag, __func__); + + for (i = 0; i < TOUCH_ID_MAX; i++) { + input_mt_slot(info->input_dev, i); + input_mt_report_slot_state(info->input_dev, + (i < FINGER_MAX) ? MT_TOOL_FINGER : MT_TOOL_PEN, 0); + } + input_sync(info->input_dev); + + info->resume_bit = 1; + + fts_system_reset(); + + fts_mode_handler(info, 0); + + info->sensor_sleep = false; + + fts_enableInterrupt(); + break; + default: + break; + + } + } + return NOTIFY_OK; + +} + +static struct notifier_block fts_noti_block = { + .notifier_call = fts_fb_state_chg_callback, +}; + +static int fts_get_reg(struct fts_ts_info *rmi4_data, + bool get) { + int retval; + const struct fts_i2c_platform_data *bdata = + rmi4_data->bdata; + + if (!get) { + retval = 0; + goto regulator_put; + } + + if ((bdata->pwr_reg_name != NULL) && (*bdata->pwr_reg_name != 0)) { + rmi4_data->pwr_reg = regulator_get(rmi4_data->dev, + bdata->pwr_reg_name); + if (IS_ERR(rmi4_data->pwr_reg)) { + logError(1, "%s %s: Failed to get power regulator\n", tag, + __func__); + retval = PTR_ERR(rmi4_data->pwr_reg); + goto regulator_put; + } + } + + if ((bdata->bus_reg_name != NULL) && (*bdata->bus_reg_name != 0)) { + rmi4_data->bus_reg = regulator_get(rmi4_data->dev, + bdata->bus_reg_name); + if (IS_ERR(rmi4_data->bus_reg)) { + logError(1, "%s %s: Failed to get bus pullup regulator\n", tag, + __func__); + retval = PTR_ERR(rmi4_data->bus_reg); + goto regulator_put; + } + } + + return 0; + +regulator_put: + if (rmi4_data->pwr_reg) { + regulator_put(rmi4_data->pwr_reg); + rmi4_data->pwr_reg = NULL; + } + + if (rmi4_data->bus_reg) { + regulator_put(rmi4_data->bus_reg); + rmi4_data->bus_reg = NULL; + } + + return retval; +} + +static int fts_enable_reg(struct fts_ts_info *rmi4_data, + bool enable) { + int retval; + + if (!enable) { + retval = 0; + goto disable_pwr_reg; + } + + if (rmi4_data->bus_reg) { + retval = regulator_enable(rmi4_data->bus_reg); + if (retval < 0) { + logError(1, "%s %s: Failed to enable bus regulator\n", tag, + __func__); + goto exit; + } + } + + if (rmi4_data->pwr_reg) { + retval = regulator_enable(rmi4_data->pwr_reg); + if (retval < 0) { + logError(1, "%s %s: Failed to enable power regulator\n", tag, + __func__); + goto disable_bus_reg; + } + } + + return OK; + +disable_pwr_reg: + if (rmi4_data->pwr_reg) + regulator_disable(rmi4_data->pwr_reg); + +disable_bus_reg: + if (rmi4_data->bus_reg) + regulator_disable(rmi4_data->bus_reg); + +exit: + return retval; +} + +static int fts_gpio_setup(int gpio, bool config, int dir, int state) +{ + int retval = 0; + unsigned char buf[16]; + + if (config) { + snprintf(buf, 16, "fts_gpio_%u\n", gpio); + + retval = gpio_request(gpio, buf); + if (retval) { + logError(1, "%s %s: Failed to get gpio %d (code: %d)", tag, + __func__, gpio, retval); + return retval; + } + + if (dir == 0) + retval = gpio_direction_input(gpio); + else + retval = gpio_direction_output(gpio, state); + if (retval) { + logError(1, "%s %s: Failed to set gpio %d direction", tag, + __func__, gpio); + return retval; + } + } else { + gpio_free(gpio); + } + + return retval; +} + +static int fts_set_gpio(struct fts_ts_info *rmi4_data) +{ + int retval; + const struct fts_i2c_platform_data *bdata = + rmi4_data->bdata; + + retval = fts_gpio_setup(bdata->irq_gpio, true, 0, 0); + if (retval < 0) { + logError(1, "%s %s: Failed to configure irq GPIO\n", tag, __func__); + goto err_gpio_irq; + } + + if (bdata->reset_gpio >= 0) { + retval = fts_gpio_setup(bdata->reset_gpio, true, 1, 0); + if (retval < 0) { + logError(1, "%s %s: Failed to configure reset GPIO\n", tag, __func__); + goto err_gpio_reset; + } + } + if (bdata->reset_gpio >= 0) { + gpio_set_value(bdata->reset_gpio, 0); + msleep(10); + gpio_set_value(bdata->reset_gpio, 1); + } + + setResetGpio(bdata->reset_gpio); + return OK; + +err_gpio_reset: + fts_gpio_setup(bdata->irq_gpio, false, 0, 0); + setResetGpio(GPIO_NOT_DEFINED); +err_gpio_irq: + return retval; +} + +static int parse_dt(struct device *dev, struct fts_i2c_platform_data *bdata) +{ + int retval; + const char *name; + struct device_node *np = dev->of_node; + + bdata->irq_gpio = of_get_named_gpio_flags(np, + "st,irq-gpio", 0, NULL); + + logError(0, "%s irq_gpio = %d\n", tag, bdata->irq_gpio); + + retval = of_property_read_string(np, "st,regulator_dvdd", &name); + if (retval == -EINVAL) + bdata->pwr_reg_name = NULL; + else if (retval < 0) + return retval; + bdata->pwr_reg_name = name; + logError(0, "%s pwr_reg_name = %s\n", tag, name); + + retval = of_property_read_string(np, "st,regulator_avdd", &name); + if (retval == -EINVAL) + bdata->bus_reg_name = NULL; + else if (retval < 0) + return retval; + bdata->bus_reg_name = name; + logError(0, "%s bus_reg_name = %s\n", tag, name); + + if (of_property_read_bool(np, "st, reset-gpio")) { + bdata->reset_gpio = of_get_named_gpio_flags(np, + "st, reset-gpio", 0, NULL); + logError(0, "%s reset_gpio =%d\n", tag, bdata->reset_gpio); + } else { + bdata->reset_gpio = GPIO_NOT_DEFINED; + } + + return OK; +} + +static int fts_probe(struct i2c_client *client, + const struct i2c_device_id *idp) { + struct fts_ts_info *info = NULL; + char fts_ts_phys[64]; + int error = 0; + struct device_node *dp = client->dev.of_node; + int retval; + + logError(1, "%s %s: driver probe begin!\n", tag, __func__); + + logError(1, "%s SET I2C Functionality and Dev INFO:\n", tag); + openChannel(client); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + logError(1, "%s Unsupported I2C functionality\n", tag); + error = -EIO; + goto ProbeErrorExit_0; + } + + info = kzalloc(sizeof (struct fts_ts_info), GFP_KERNEL); + if (!info) { + logError(1, "%s Out of memory... Impossible to allocate struct info!\n", tag); + error = -ENOMEM; + goto ProbeErrorExit_0; + } + + info->client = client; + + i2c_set_clientdata(client, info); + logError(1, "%s i2c address: %x\n", tag, client->addr); + info->dev = &info->client->dev; + if (dp) { + info->bdata = devm_kzalloc(&client->dev, sizeof (struct fts_i2c_platform_data), GFP_KERNEL); + if (!info->bdata) { + logError(1, "%s ERROR:info.bdata kzalloc failed\n", tag); + goto ProbeErrorExit_1; + } + parse_dt(&client->dev, info->bdata); + } + + logError(1, "%s SET Regulators:\n", tag); + retval = fts_get_reg(info, true); + if (retval < 0) { + logError(1, "%s ERROR: %s: Failed to get regulators\n", tag, __func__); + goto ProbeErrorExit_1; + } + + retval = fts_enable_reg(info, true); + if (retval < 0) { + logError(1, "%s %s: ERROR Failed to enable regulators\n", tag, __func__); + goto ProbeErrorExit_2; + } + + logError(1, "%s SET GPIOS:\n", tag); + retval = fts_set_gpio(info); + if (retval < 0) { + logError(1, "%s %s: ERROR Failed to set up GPIO's\n", tag, __func__); + goto ProbeErrorExit_2; + } + info->client->irq = gpio_to_irq(info->bdata->irq_gpio); + + logError(1, "%s SET Auto Fw Update:\n", tag); + info->fwu_workqueue = create_singlethread_workqueue("fts-fwu-queue"); + if (!info->fwu_workqueue) { + logError(1, "%s ERROR: Cannot create fwu work thread\n", tag); + goto ProbeErrorExit_3; + } + INIT_DELAYED_WORK(&info->fwu_work, fts_fw_update_auto); + + logError(1, "%s SET Event Handler:\n", tag); + info->event_wq = create_singlethread_workqueue("fts-event-queue"); + if (!info->event_wq) { + logError(1, "%s ERROR: Cannot create work thread\n", tag); + error = -ENOMEM; + goto ProbeErrorExit_4; + } + + INIT_WORK(&info->work, fts_event_handler); + + logError(1, "%s SET Input Device Property:\n", tag); + info->dev = &info->client->dev; + info->input_dev = input_allocate_device(); + if (!info->input_dev) { + logError(1, "%s ERROR: No such input device defined!\n", tag); + error = -ENODEV; + goto ProbeErrorExit_5; + } + info->input_dev->dev.parent = &client->dev; + info->input_dev->name = FTS_TS_DRV_NAME; + snprintf(fts_ts_phys, sizeof (fts_ts_phys), "%s/input0", + info->input_dev->name); + info->input_dev->phys = fts_ts_phys; + info->input_dev->id.bustype = BUS_I2C; + info->input_dev->id.vendor = 0x0001; + info->input_dev->id.product = 0x0002; + info->input_dev->id.version = 0x0100; + + __set_bit(EV_SYN, info->input_dev->evbit); + __set_bit(EV_KEY, info->input_dev->evbit); + __set_bit(EV_ABS, info->input_dev->evbit); + __set_bit(BTN_TOUCH, info->input_dev->keybit); + __set_bit(BTN_TOOL_FINGER, info->input_dev->keybit); + + input_mt_init_slots(info->input_dev, TOUCH_ID_MAX, INPUT_MT_DIRECT); + + /* input_mt_init_slots(info->input_dev, TOUCH_ID_MAX); */ + + /* input_set_abs_params(info->input_dev, ABS_MT_TRACKING_ID, 0, FINGER_MAX, 0, 0); */ + input_set_abs_params(info->input_dev, ABS_MT_POSITION_X, + X_AXIS_MIN, X_AXIS_MAX, 0, 0); + input_set_abs_params(info->input_dev, ABS_MT_POSITION_Y, + Y_AXIS_MIN, Y_AXIS_MAX, 0, 0); + input_set_abs_params(info->input_dev, ABS_MT_TOUCH_MAJOR, + AREA_MIN, AREA_MAX, 0, 0); + input_set_abs_params(info->input_dev, ABS_MT_TOUCH_MINOR, + AREA_MIN, AREA_MAX, 0, 0); + input_set_abs_params(info->input_dev, ABS_MT_PRESSURE, + PRESSURE_MIN, PRESSURE_MAX, 0, 0); + +#ifdef PHONE_GESTURE + input_set_capability(info->input_dev, EV_KEY, KEY_WAKEUP); + + input_set_capability(info->input_dev, EV_KEY, KEY_M); + input_set_capability(info->input_dev, EV_KEY, KEY_O); + input_set_capability(info->input_dev, EV_KEY, KEY_E); + input_set_capability(info->input_dev, EV_KEY, KEY_W); + input_set_capability(info->input_dev, EV_KEY, KEY_C); + input_set_capability(info->input_dev, EV_KEY, KEY_L); + input_set_capability(info->input_dev, EV_KEY, KEY_F); + input_set_capability(info->input_dev, EV_KEY, KEY_V); + input_set_capability(info->input_dev, EV_KEY, KEY_S); + input_set_capability(info->input_dev, EV_KEY, KEY_Z); + input_set_capability(info->input_dev, EV_KEY, KEY_WWW); + + input_set_capability(info->input_dev, EV_KEY, KEY_LEFT); + input_set_capability(info->input_dev, EV_KEY, KEY_RIGHT); + input_set_capability(info->input_dev, EV_KEY, KEY_UP); + input_set_capability(info->input_dev, EV_KEY, KEY_DOWN); + + input_set_capability(info->input_dev, EV_KEY, KEY_F1); + input_set_capability(info->input_dev, EV_KEY, KEY_F2); + input_set_capability(info->input_dev, EV_KEY, KEY_F3); + input_set_capability(info->input_dev, EV_KEY, KEY_F4); + input_set_capability(info->input_dev, EV_KEY, KEY_F5); + + input_set_capability(info->input_dev, EV_KEY, KEY_LEFTBRACE); + input_set_capability(info->input_dev, EV_KEY, KEY_RIGHTBRACE); +#endif + +#ifdef PHONE_KEY + /* KEY associated to the touch screen buttons */ + input_set_capability(info->input_dev, EV_KEY, KEY_HOMEPAGE); + input_set_capability(info->input_dev, EV_KEY, KEY_BACK); + input_set_capability(info->input_dev, EV_KEY, KEY_MENU); +#endif + + mutex_init(&(info->input_report_mutex)); + + /* register the multi-touch input device */ + error = input_register_device(info->input_dev); + if (error) { + logError(1, "%s ERROR: No such input device\n", tag); + error = -ENODEV; + goto ProbeErrorExit_5_1; + } + + /* track slots */ + info->touch_id = 0; + + /* init hardware device */ + logError(1, "%s Device Initialization:\n", tag); + error = fts_init(info); + if (error < OK) { + logError(1, "%s Cannot initialize the device ERROR %08X\n", tag, error); + error = -ENODEV; + goto ProbeErrorExit_6; + } + + info->gesture_enabled = 0; + info->glove_enabled = 0; + info->resume_bit = 1; + info->notifier = fts_noti_block; + error = fb_register_client(&info->notifier); + if (error) { + logError(1, "%s ERROR: register notifier failed!\n", tag); + goto ProbeErrorExit_6; + } + + logError(1, "%s SET Device File Nodes:\n", tag); + /* sysfs stuff */ + info->attrs.attrs = fts_attr_group; + error = sysfs_create_group(&client->dev.kobj, &info->attrs); + if (error) { + logError(1, "%s ERROR: Cannot create sysfs structure!\n", tag); + error = -ENODEV; + goto ProbeErrorExit_7; + } + +#ifdef SCRIPTLESS + /*I2C cmd*/ + if (fts_cmd_class == NULL) + fts_cmd_class = class_create(THIS_MODULE, FTS_TS_DRV_NAME); + info->i2c_cmd_dev = device_create(fts_cmd_class, + NULL, DCHIP_ID_0, info, "fts_i2c"); + if (IS_ERR(info->i2c_cmd_dev)) { + logError(1, "%s ERROR: Failed to create device for the sysfs!\n", tag); + goto ProbeErrorExit_8; + } + + dev_set_drvdata(info->i2c_cmd_dev, info); + + error = sysfs_create_group(&info->i2c_cmd_dev->kobj, + &i2c_cmd_attr_group); + if (error) { + logError(1, "%s ERROR: Failed to create sysfs group!\n", tag); + goto ProbeErrorExit_9; + } + +#endif + +#ifdef DRIVER_TEST + if (fts_cmd_class == NULL) + fts_cmd_class = class_create(THIS_MODULE, FTS_TS_DRV_NAME); + info->test_cmd_dev = device_create(fts_cmd_class, + NULL, DCHIP_ID_0, info, "fts_driver_test"); + if (IS_ERR(info->test_cmd_dev)) { + logError(1, "%s ERROR: Failed to create device for the sysfs!\n", tag); + goto ProbeErrorExit_10; + } + + dev_set_drvdata(info->test_cmd_dev, info); + + error = sysfs_create_group(&info->test_cmd_dev->kobj, + &test_cmd_attr_group); + if (error) { + logError(1, "%s ERROR: Failed to create sysfs group!\n", tag); + goto ProbeErrorExit_11; + } + +#endif + queue_delayed_work(info->fwu_workqueue, &info->fwu_work, msecs_to_jiffies(EXP_FN_WORK_DELAY_MS)); + logError(1, "%s Probe Finished!\n", tag); + return OK; + + /* error exit path */ +#ifdef DRIVER_TEST +ProbeErrorExit_11: +#ifndef SCRIPTLESS + device_destroy(fts_cmd_class, DCHIP_ID_0); +#endif + +ProbeErrorExit_10: +#ifndef SCRIPTLESS + sysfs_remove_group(&client->dev.kobj, &info->attrs); +#endif +#endif + +#ifdef SCRIPTLESS +ProbeErrorExit_9: + device_destroy(fts_cmd_class, DCHIP_ID_0); + +ProbeErrorExit_8: + sysfs_remove_group(&client->dev.kobj, &info->attrs); +#endif + +ProbeErrorExit_7: + fb_unregister_client(&info->notifier); + +ProbeErrorExit_6: + input_unregister_device(info->input_dev); + +ProbeErrorExit_5_1: + /* intput_free_device(info->input_dev ); */ + + ProbeErrorExit_5: + destroy_workqueue(info->event_wq); + +ProbeErrorExit_4: + destroy_workqueue(info->fwu_workqueue); + +ProbeErrorExit_3: + fts_enable_reg(info, false); + +ProbeErrorExit_2: + fts_get_reg(info, false); + +ProbeErrorExit_1: + kfree(info); + +ProbeErrorExit_0: + logError(1, "%s Probe Failed!\n", tag); + + return error; +} + +static int fts_remove(struct i2c_client *client) +{ + struct fts_ts_info *info = i2c_get_clientdata(client); + +#ifdef DRIVER_TEST + sysfs_remove_group(&info->test_cmd_dev->kobj, + &test_cmd_attr_group); +#endif + +#ifdef SCRIPTLESS + /*I2C cmd*/ + sysfs_remove_group(&info->i2c_cmd_dev->kobj, + &i2c_cmd_attr_group); + +#endif + +#if defined(SCRIPTLESS) || defined(DRIVER_TEST) + device_destroy(fts_cmd_class, DCHIP_ID_0); +#endif + + /* sysfs stuff */ + sysfs_remove_group(&client->dev.kobj, &info->attrs); + + /* remove interrupt and event handlers */ + fts_interrupt_uninstall(info); + + fb_unregister_client(&info->notifier); + + /* unregister the device */ + input_unregister_device(info->input_dev); + + /* intput_free_device(info->input_dev ); */ + + /* Empty the FIFO buffer */ + fts_command(info, FIFO_CMD_FLUSH); + /* flushFIFO(); */ + + /* Remove the work thread */ + destroy_workqueue(info->event_wq); + destroy_workqueue(info->fwu_workqueue); + + fts_enable_reg(info, false); + fts_get_reg(info, false); + + /* free all */ + kfree(info); + + return OK; +} + +static struct of_device_id fts_of_match_table[] = { + { + .compatible = "st,fts", + }, + {}, +}; +static const struct i2c_device_id fts_device_id[] = { + {FTS_TS_DRV_NAME, 0}, + {} +}; + +static struct i2c_driver fts_i2c_driver = { + .driver = { + .name = FTS_TS_DRV_NAME, + .of_match_table = fts_of_match_table, + }, + .probe = fts_probe, + .remove = fts_remove, + .id_table = fts_device_id, +}; + +static int __init fts_driver_init(void) +{ + return i2c_add_driver(&fts_i2c_driver); +} + +static void __exit fts_driver_exit(void) +{ + i2c_del_driver(&fts_i2c_driver); +} + +MODULE_DESCRIPTION("STMicroelectronics MultiTouch IC Driver"); +MODULE_AUTHOR("STMicroelectronics, Inc."); +MODULE_LICENSE("GPL"); + +late_initcall(fts_driver_init); +module_exit(fts_driver_exit); diff --git a/drivers/input/touchscreen/st/fts.h b/drivers/input/touchscreen/st/fts.h new file mode 100644 index 000000000000..7d72d226349f --- /dev/null +++ b/drivers/input/touchscreen/st/fts.h @@ -0,0 +1,249 @@ +/* + * fts.c + * + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * marco.cali@st.com + * + * 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. + * + */ + +#ifndef _LINUX_FTS_I2C_H_ +#define _LINUX_FTS_I2C_H_ + +#include "fts_lib/ftsSoftware.h" +#include "fts_lib/ftsHardware.h" + +#define FTS_POWER_ON 1 +#define FTS_POWER_OFF 0 + +/****************** CONFIGURATION SECTION ******************/ +/* #define PHONE_KEY */ + +/* #define PHONE_GESTURE */ + +#define SCRIPTLESS +#ifdef SCRIPTLESS +#define SCRIPTLESS_DEBUG +/* uncomment this macro definition to print debug +* message for script less support +*/ +#endif + +#define DRIVER_TEST + +#define FW_H_FILE +#ifdef FW_H_FILE +#define FW_SIZE_NAME myArray_size +#define FW_ARRAY_NAME myArray +#endif + +#define LIMITS_H_FILE +#ifdef LIMITS_H_FILE +#define LIMITS_SIZE_NAME myArray2_size +#define LIMITS_ARRAY_NAME myArray2 +#endif + +#define FTS_TS_DRV_NAME "fts" +#define FTS_TS_DRV_VERSION "4.1.0" + +#define X_AXIS_MAX 1440 +#define X_AXIS_MIN 0 +#define Y_AXIS_MAX 2560 +#define Y_AXIS_MIN 0 + +#define PRESSURE_MIN 0 +#define PRESSURE_MAX 127 + +#define FINGER_MAX 10 +#define STYLUS_MAX 1 +#define TOUCH_ID_MAX (FINGER_MAX + STYLUS_MAX) + +#define AREA_MIN PRESSURE_MIN +#define AREA_MAX PRESSURE_MAX + +/*********************************************************/ + +/* Flash programming */ + +#define INIT_FLAG_CNT 3 + +/* KEYS */ +#define KEY1 0x02 +#define KEY2 0x01 +#define KEY3 0x04 + +/* + * Configuration mode + */ +#define MODE_NORMAL 0 +#define MODE_GESTURE 1 +#define MODE_GLOVE 2 +#define MODE_SENSEOFF 3 + +/* + * Status Event Field: + * id of command that triggered the event + */ + +#define FTS_FLASH_WRITE_CONFIG 0x03 +#define FTS_FLASH_WRITE_COMP_MEMORY 0x04 +#define FTS_FORCE_CAL_SELF_MUTUAL 0x05 +#define FTS_FORCE_CAL_SELF 0x06 +#define FTS_WATER_MODE_ON 0x07 +#define FTS_WATER_MODE_OFF 0x08 + +#define EXP_FN_WORK_DELAY_MS 1000 + +#define CMD_STR_LEN 32 + +#ifdef SCRIPTLESS +/* + * I2C Command Read/Write Function + */ + +#define CMD_RESULT_STR_LEN 2048 +#endif + +#define TSP_BUF_SIZE 4096 + +struct fts_i2c_platform_data { + int (*power)(bool on); + int irq_gpio; + int reset_gpio; + const char *pwr_reg_name; + const char *bus_reg_name; + +}; + +/* + * Forward declaration + */ +struct fts_ts_info; +extern char tag[8]; + +/* + * Dispatch event handler + */ +typedef unsigned char * (*event_dispatch_handler_t) +(struct fts_ts_info *info, unsigned char *data); + +/* + * struct fts_ts_info - FTS capacitive touch screen device information + * @dev: Pointer to the structure device + * @client: I2C client structure + * @input_dev Input device structure + * @work Work thread + * @event_wq Event queue for work thread + * @cmd_done Asyncronous command notification + * @event_dispatch_table Event dispatch table handlers + * @fw_version Firmware version + * @attrs SysFS attributes + * @mode Device operating mode + * @touch_id Bitmask for touch id (mapped to input slots) + * @buttons Bitmask for buttons status + * @timer Timer when operating in polling mode + * @early_suspend Structure for early suspend functions + * @power Power on/off routine + */ + +struct fts_ts_info { + struct device *dev; + struct i2c_client *client; + struct input_dev *input_dev; + + struct work_struct work; + struct workqueue_struct *event_wq; + + struct delayed_work fwu_work; + struct workqueue_struct *fwu_workqueue; + struct completion cmd_done; + + event_dispatch_handler_t *event_dispatch_table; + + unsigned int fw_version; + unsigned int config_id; + + struct attribute_group attrs; + + unsigned int mode; + unsigned long touch_id; + unsigned int buttons; + +#ifdef FTS_USE_POLLING_MODE + struct hrtimer timer; +#endif + +#ifdef SCRIPTLESS + /*I2C cmd*/ + struct device *i2c_cmd_dev; + char cmd_read_result[CMD_RESULT_STR_LEN]; + char cmd_wr_result[CMD_RESULT_STR_LEN]; + char cmd_write_result[20]; +#endif + +#ifdef DRIVER_TEST + struct device *test_cmd_dev; +#endif + + int (*power)(bool on); + + struct fts_i2c_platform_data *bdata; + struct regulator *pwr_reg; + struct regulator *bus_reg; + + bool fw_force; + int debug_enable; + + int resume_bit; + int fwupdate_stat; + int touch_debug; + + struct notifier_block notifier; + bool sensor_sleep; + bool stay_awake; + + /* input lock */ + struct mutex input_report_mutex; + + /* switches */ + int gesture_enabled; + int glove_enabled; + +}; + +typedef enum { + ERR_ITO_NO_ERR, /* < 0 No ITO Error */ + ERR_ITO_PANEL_OPEN_FORCE, /* < 1 Panel Open Force */ + ERR_ITO_PANEL_OPEN_SENSE, /* < 2 Panel Open Sense */ + ERR_ITO_F2G, /* < 3 Force short to ground */ + ERR_ITO_S2G, /* < 4 Sense short to ground */ + ERR_ITO_F2VDD, /* < 5 Force short to VDD */ + ERR_ITO_S2VDD, /* < 6 Sense short to VDD */ + ERR_ITO_P2P_FORCE, /* < 7 Pin to Pin short (Force) */ + ERR_ITO_P2P_SENSE, /* < 8 Pin to Pin short (Sense) */ +} errItoSubTypes_t; + +int fts_chip_powercycle(struct fts_ts_info *info); +int fts_chip_powercycle2(struct fts_ts_info *info, unsigned long sleep); +int fts_get_fw_version(struct fts_ts_info *info); +extern unsigned int le_to_uint(const unsigned char *ptr); +extern unsigned int be_to_uint(const unsigned char *ptr); +extern int input_register_notifier_client(struct notifier_block *nb); +extern int input_unregister_notifier_client(struct notifier_block *nb); + +#ifdef SCRIPTLESS +extern struct attribute_group i2c_cmd_attr_group; +#endif + +#ifdef DRIVER_TEST +extern struct attribute_group test_cmd_attr_group; +#endif + +#endif diff --git a/drivers/input/touchscreen/st/fts_driver_test.c b/drivers/input/touchscreen/st/fts_driver_test.c new file mode 100644 index 000000000000..5c55d8e4ac88 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_driver_test.c @@ -0,0 +1,871 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* API used by Driver Test Apk * +* * +************************************************************************** +************************************************************************** + +*/ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/interrupt.h> +#include <linux/hrtimer.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/completion.h> + +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/regulator/consumer.h> +#include "fts.h" +#include "fts_lib/ftsCompensation.h" +#include "fts_lib/ftsIO.h" +#include "fts_lib/ftsError.h" +#include "fts_lib/ftsFrame.h" +#include "fts_lib/ftsFlash.h" +#include "fts_lib/ftsTest.h" +#include "fts_lib/ftsTime.h" +#include "fts_lib/ftsTool.h" + +#ifdef DRIVER_TEST + +#define MAX_PARAMS 10 + +/* DEFINE COMMANDS TO TEST */ +#define CMD_READ 0x00 +#define CMD_WRITE 0x01 +#define CMD_READU16 0x02 +#define CMD_READB2 0x03 +#define CMD_READB2U16 0x04 +#define CMD_POLLFOREVENT 0x05 +#define CMD_SYSTEMRESET 0x06 +#define CMD_CLEANUP 0x07 +#define CMD_GETFORCELEN 0x08 +#define CMD_GETSENSELEN 0x09 +#define CMD_GETMSFRAME 0x0A +/* #define CMD_GETMSKEYFRAME 0x0B */ +#define CMD_GETSSFRAME 0x0C +#define CMD_REQCOMPDATA 0x0D +#define CMD_READCOMPDATAHEAD 0x0E +#define CMD_READMSCOMPDATA 0x0F +#define CMD_READSSCOMPDATA 0x10 +#define CMD_READGNCOMPDATA 0x11 +#define CMD_GETFWVER 0x12 +#define CMD_FLASHSTATUS 0x13 +#define CMD_FLASHUNLOCK 0x14 +#define CMD_READFWFILE 0x15 +#define CMD_FLASHPROCEDURE 0x16 +#define CMD_ITOTEST 0x17 +#define CMD_INITTEST 0x18 +#define CMD_MSRAWTEST 0x19 +#define CMD_MSINITDATATEST 0x1A +#define CMD_SSRAWTEST 0x1B +#define CMD_SSINITDATATEST 0x1C +#define CMD_MAINTEST 0x1D +#define CMD_POWERCYCLE 0x1E +#define CMD_FWWRITE 0x1F +#define CMD_READCHIPINFO 0x20 +#define CMD_REQFRAME 0x21 + +u32 *functionToTest; +int numberParam; + +static ssize_t stm_driver_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { + int n; + char *p = (char *) buf; + + functionToTest = (u32 *) kmalloc(MAX_PARAMS * sizeof (u32), GFP_KERNEL); + if (functionToTest == NULL) { + logError(1, "%s impossible to allocate functionToTest!\n", tag); + return count; + } + memset(functionToTest, 0, MAX_PARAMS * sizeof (u32)); + + for (n = 0; n < (count + 1) / 3 && n < MAX_PARAMS; n++) { + sscanf(p, "%02X ", &functionToTest[n]); + p += 3; + logError(1, "%s functionToTest[%d] = %02X\n", tag, n, functionToTest[n]); + + } + + numberParam = n; + logError(1, "%s Number of Parameters = %d\n", tag, numberParam); + return count; +} + +static ssize_t stm_driver_test_show(struct device *dev, struct device_attribute *attr, + char *buf) { + char buff[CMD_STR_LEN] = {0}; + int res = -1, j, count; + /* int res2; */ + int size = 6 * 2; + int temp, i, byteToRead; + u8 *readData = NULL; + u8 *all_strbuff = NULL; + u8 *cmd; + + MutualSenseFrame frameMS; + SelfSenseFrame frameSS; + + DataHeader dataHead; + MutualSenseData compData; + SelfSenseData comData; + GeneralData gnData; + + u16 address; + u16 fw_version; + u16 config_id; + + Firmware fw; + + /* struct used for defining which test perform during the MP test */ + + TestToDo todoDefault; + + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + fw.data = NULL; + + todoDefault.MutualRaw = 1; + todoDefault.MutualRawGap = 1; + todoDefault.MutualCx1 = 0; + todoDefault.MutualCx2 = 1; + todoDefault.MutualCx2Adj = 1; + todoDefault.MutualCxTotal = 0; + todoDefault.MutualCxTotalAdj = 0; + + todoDefault.MutualKeyRaw = 0; + todoDefault.MutualKeyCx1 = 0; + todoDefault.MutualKeyCx2 = 0; + todoDefault.MutualKeyCxTotal = 0; + + todoDefault.SelfForceRaw = 1; + todoDefault.SelfForceRawGap = 0; + todoDefault.SelfForceIx1 = 0; + todoDefault.SelfForceIx2 = 0; + todoDefault.SelfForceIx2Adj = 0; + todoDefault.SelfForceIxTotal = 1; + todoDefault.SelfForceIxTotalAdj = 0; + todoDefault.SelfForceCx1 = 0; + todoDefault.SelfForceCx2 = 0; + todoDefault.SelfForceCx2Adj = 0; + todoDefault.SelfForceCxTotal = 0; + todoDefault.SelfForceCxTotalAdj = 0; + + todoDefault.SelfSenseRaw = 1; + todoDefault.SelfSenseRawGap = 0; + todoDefault.SelfSenseIx1 = 0; + todoDefault.SelfSenseIx2 = 0; + todoDefault.SelfSenseIx2Adj = 0; + todoDefault.SelfSenseIxTotal = 1; + todoDefault.SelfSenseIxTotalAdj = 0; + todoDefault.SelfSenseCx1 = 0; + todoDefault.SelfSenseCx2 = 0; + todoDefault.SelfSenseCx2Adj = 0; + todoDefault.SelfSenseCxTotal = 0; + todoDefault.SelfSenseCxTotalAdj = 0; + + if (numberParam >= 1 && functionToTest != NULL) { + res = fts_disableInterrupt(); + if (res < 0) { + logError(0, "%s stm_driver_test_show: ERROR %08X\n", tag, res); + res = (res | ERROR_DISABLE_INTER); + goto END; + } + switch (functionToTest[0]) { + case CMD_READ: + if (numberParam >= 4) { + temp = (int) functionToTest[1]; + if (numberParam == 4 + (temp - 1) && temp != 0) { + cmd = (u8 *) kmalloc(temp * sizeof (u8), GFP_KERNEL); + for (i = 0; i < temp; i++) { + cmd[i] = functionToTest[i + 2]; + } + byteToRead = functionToTest[i + 2]; + readData = (u8 *) kmalloc(byteToRead * sizeof (u8), GFP_KERNEL); + res = fts_readCmd(cmd, temp, readData, byteToRead); + size += (byteToRead * sizeof (u8))*2; + } else { + logError(1, "%s Wrong parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_WRITE: + if (numberParam >= 3) { /* need to pass: cmdLength cmd[0] cmd[1] … cmd[cmdLength-1] */ + temp = (int) functionToTest[1]; + if (numberParam == 3 + (temp - 1) && temp != 0) { + cmd = (u8 *) kmalloc(temp * sizeof (u8), GFP_KERNEL); + for (i = 0; i < temp; i++) { + cmd[i] = functionToTest[i + 2]; + } + res = fts_writeCmd(cmd, temp); + } else { + logError(1, "%s Wrong parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_FWWRITE: + if (numberParam >= 3) { /* need to pass: cmdLength cmd[0] cmd[1] … cmd[cmdLength-1] */ + temp = (int) functionToTest[1]; + if (numberParam == 3 + (temp - 1) && temp != 0) { + cmd = (u8 *) kmalloc(temp * sizeof (u8), GFP_KERNEL); + for (i = 0; i < temp; i++) { + cmd[i] = functionToTest[i + 2]; + } + res = fts_writeFwCmd(cmd, temp); + } else { + logError(1, "%s Wrong parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_READU16: + if (numberParam == 6) { /* need to pass: cmd addr[0] addr[1] byteToRead hasDummyByte */ + byteToRead = functionToTest[4]; + readData = (u8 *) kmalloc(byteToRead * sizeof (u8), GFP_KERNEL); + res = readCmdU16((u8) functionToTest[1], (u16) ((((u8) functionToTest[2] & 0x00FF) << 8) + ((u8) functionToTest[3] & 0x00FF)), readData, byteToRead, functionToTest[5]); + size += (byteToRead * sizeof (u8))*2; + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_READB2: + if (numberParam == 4) { /* need to pass: addr[0] addr[1] byteToRead */ + byteToRead = functionToTest[3]; + readData = (u8 *) kmalloc(byteToRead * sizeof (u8), GFP_KERNEL); + res = readB2((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF)), readData, byteToRead); + size += (byteToRead * sizeof (u8))*2; + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_READB2U16: + if (numberParam == 4) { /* need to pass: addr[0] addr[1] byteToRead */ + byteToRead = functionToTest[3]; + readData = (u8 *) kmalloc(byteToRead * sizeof (u8), GFP_KERNEL); + res = readB2U16((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF)), readData, byteToRead); + size += (byteToRead * sizeof (u8))*2; + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_POLLFOREVENT: + if (numberParam >= 5) { /* need to pass: eventLength event[0] event[1] event[eventLength-1] timeTowait */ + temp = (int) functionToTest[1]; + if (numberParam == 5 + (temp - 1) && temp != 0) { + readData = (u8 *) kmalloc(FIFO_EVENT_SIZE * sizeof (u8), GFP_KERNEL); + res = pollForEvent((int *) &functionToTest[2], temp, readData, ((functionToTest[temp + 2] & 0x00FF) << 8)+(functionToTest[temp + 3] & 0x00FF)); + if (res >= OK) + res = OK; /* pollForEvent return the number of error found */ + size += (FIFO_EVENT_SIZE * sizeof (u8))*2; + byteToRead = FIFO_EVENT_SIZE; + } else { + logError(1, "%s Wrong parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_SYSTEMRESET: + res = fts_system_reset(); + + break; + + case CMD_READCHIPINFO: + if (numberParam == 2) { /* need to pass: doRequest */ + res = readChipInfo(functionToTest[1]); + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + + break; + + case CMD_CLEANUP: /* TOUCH ENABLE/DISABLE */ + if (numberParam == 2) { /* need to pass: enableTouch */ + res = cleanUp(functionToTest[1]); + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + + break; + + case CMD_GETFORCELEN: /* read number Tx channels */ + temp = getForceLen(); + if (temp < OK) + res = temp; + else { + size += (1 * sizeof (u8))*2; + res = OK; + } + break; + + case CMD_GETSENSELEN: /* read number Rx channels */ + temp = getSenseLen(); + if (temp < OK) + res = temp; + else { + size += (1 * sizeof (u8))*2; + res = OK; + } + break; + + case CMD_REQFRAME: /* request a frame */ + if (numberParam == 3) { + logError(0, "%s Requesting Frame\n", tag); + res = requestFrame((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF))); + + if (res < OK) { + logError(0, "%s Error requesting frame ERROR %02X\n", tag, res); + } else { + logError(0, "%s Requesting Frame Finished!\n", tag); + } + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_GETMSFRAME: + if (numberParam == 3) { + logError(0, "%s Get 1 MS Frame\n", tag); + flushFIFO(); /* delete the events related to some touch (allow to call this function while touching the sreen without having a flooding of the FIFO) */ + res = getMSFrame2((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF)), &frameMS); + if (res < 0) { + logError(0, "%s Error while taking the MS frame... ERROR %02X\n", tag, res); + + } else { + logError(0, "%s The frame size is %d words\n", tag, res); + size = (res * sizeof (short) + 8)*2; + /* set res to OK because if getMSFrame is + successful res = number of words read + */ + res = OK; + print_frame_short("MS frame =", array1dTo2d_short(frameMS.node_data, frameMS.node_data_size, frameMS.header.sense_node), frameMS.header.force_node, frameMS.header.sense_node); + } + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + /*read self raw*/ + case CMD_GETSSFRAME: + if (numberParam == 3) { + logError(0, "%s Get 1 SS Frame\n", tag); + flushFIFO(); /* delete the events related to some touch (allow to call this function while touching the sreen without having a flooding of the FIFO) */ + res = getSSFrame2((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF)), &frameSS); + + if (res < OK) { + logError(0, "%s Error while taking the SS frame... ERROR %02X\n", tag, res); + + } else { + logError(0, "%s The frame size is %d words\n", tag, res); + size = (res * sizeof (short) + 8)*2+1; + /* set res to OK because if getMSFrame is + successful res = number of words read + */ + res = OK; + print_frame_short("SS force frame =", array1dTo2d_short(frameSS.force_data, frameSS.header.force_node, 1), frameSS.header.force_node, 1); + print_frame_short("SS sense frame =", array1dTo2d_short(frameSS.sense_data, frameSS.header.sense_node, frameSS.header.sense_node), 1, frameSS.header.sense_node); + } + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_REQCOMPDATA: /* request comp data */ + if (numberParam == 3) { + logError(0, "%s Requesting Compensation Data\n", tag); + res = requestCompensationData((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF))); + + if (res < OK) { + logError(0, "%s Error requesting compensation data ERROR %02X\n", tag, res); + } else { + logError(0, "%s Requesting Compensation Data Finished!\n", tag); + } + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_READCOMPDATAHEAD: /* read comp data header */ + if (numberParam == 3) { + logError(0, "%s Requesting Compensation Data\n", tag); + res = requestCompensationData((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF))); + if (res < OK) { + logError(0, "%s Error requesting compensation data ERROR %02X\n", tag, res); + } else { + logError(0, "%s Requesting Compensation Data Finished!\n", tag); + res = readCompensationDataHeader((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF)), &dataHead, &address); + if (res < OK) { + logError(0, "%s Read Compensation Data Header ERROR %02X\n", tag, res); + } else { + logError(0, "%s Read Compensation Data Header OK!\n", tag); + size += (2 * sizeof (u8))*2; + } + } + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_READMSCOMPDATA: /* read mutual comp data */ + if (numberParam == 3) { + logError(0, "%s Get MS Compensation Data\n", tag); + res = readMutualSenseCompensationData((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF)), &compData); + + if (res < OK) { + logError(0, "%s Error reading MS compensation data ERROR %02X\n", tag, res); + } else { + logError(0, "%s MS Compensation Data Reading Finished!\n", tag); + size = ((compData.node_data_size + 9) * sizeof (u8))*2; + print_frame_u8("MS Data (Cx2) =", array1dTo2d_u8(compData.node_data, compData.node_data_size, compData.header.sense_node), compData.header.force_node, compData.header.sense_node); + } + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_READSSCOMPDATA: + if (numberParam == 3) { /* read self comp data */ + logError(0, "%s Get SS Compensation Data...\n", tag); + res = readSelfSenseCompensationData((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF)), &comData); + if (res < OK) { + logError(0, "%s Error reading SS compensation data ERROR %02X\n", tag, res); + } else { + logError(0, "%s SS Compensation Data Reading Finished!\n", tag); + size = ((comData.header.force_node + comData.header.sense_node)*2 + 12) * sizeof (u8)*2; + print_frame_u8("SS Data Ix2_fm = ", array1dTo2d_u8(comData.ix2_fm, comData.header.force_node, comData.header.force_node), 1, comData.header.force_node); + print_frame_u8("SS Data Cx2_fm = ", array1dTo2d_u8(comData.cx2_fm, comData.header.force_node, comData.header.force_node), 1, comData.header.force_node); + print_frame_u8("SS Data Ix2_sn = ", array1dTo2d_u8(comData.ix2_sn, comData.header.sense_node, comData.header.sense_node), 1, comData.header.sense_node); + print_frame_u8("SS Data Cx2_sn = ", array1dTo2d_u8(comData.cx2_sn, comData.header.sense_node, comData.header.sense_node), 1, comData.header.sense_node); + } + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_READGNCOMPDATA: + if (numberParam == 3) { /* read self comp data */ + logError(0, "%s Get General Compensation Data...\n", tag); + res = readGeneralCompensationData((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF)), &gnData); + if (res < OK) { + logError(0, "%s Error reading General compensation data ERROR %02X\n", tag, res); + } else { + logError(0, "%s General Compensation Data Reading Finished!\n", tag); + size = (14) * sizeof (u8)*2; + } + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_GETFWVER: + res = getFirmwareVersion(&fw_version, &config_id); + if (res < OK) { + logError(1, "%s Error reading firmware version and config id ERROR %02X\n", tag, res); + } else { + logError(0, "%s getFirmwareVersion Finished!\n", tag); + size += (4) * sizeof (u8)*2; + } + break; +#ifdef FTM3_CHIP + case CMD_FLASHSTATUS: + res = flash_status(); /* return 0 = flash ready, 1 = flash busy, <0 error */ + if (res < OK) { + logError(1, "%s Error reading flash status ERROR %02X\n", tag, res); + } else { + logError(0, "%s Flash Status: %d\n", tag, res); + size += (1 * sizeof (u8))*2; + temp = res; /* need to store the value for further display */ + res = OK; /* set res =ok for returning code */ + } + break; +#endif + + case CMD_FLASHUNLOCK: + res = flash_unlock(); + if (res < OK) { + logError(1, "%s Impossible Unlock Flash ERROR %02X\n", tag, res); + } else { + logError(0, "%s Flash Unlock OK!\n", tag); + } + break; + + case CMD_READFWFILE: + if (numberParam == 2) { /* read fw file */ + logError(0, "%s Reading FW File...\n", tag); + res = readFwFile(PATH_FILE_FW, &fw, functionToTest[1]); + if (res < OK) { + logError(0, "%s Error reading FW File ERROR %02X\n", tag, res); + } else { + logError(0, "%s Read FW File Finished!\n", tag); + } + kfree(fw.data); + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_FLASHPROCEDURE: + if (numberParam == 3) { /* flashing procedure */ + logError(0, "%s Starting Flashing Procedure...\n", tag); + res = flashProcedure(PATH_FILE_FW, functionToTest[1], functionToTest[2]); + if (res < OK) { + logError(0, "%s Error during flash procedure ERROR %02X\n", tag, res); + } else { + logError(0, "%s Flash Procedure Finished!\n", tag); + } + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + /*ITO TEST*/ + case CMD_ITOTEST: + res = production_test_ito(); + break; + + /*Initialization*/ + case CMD_INITTEST: + if (numberParam == 2) { /* need to specify if if save value on Flash */ + if (functionToTest[1] == 0x01) + res = production_test_initialization(); + else + res = production_test_splited_initialization(false); + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_MSRAWTEST: /* MS Raw DATA TEST */ + if (numberParam == 2) /* need to specify if stopOnFail */ + res = production_test_ms_raw(LIMITS_FILE, functionToTest[1], &todoDefault); + else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_MSINITDATATEST: /* MS CX DATA TEST */ + if (numberParam == 2) /* need to specify if stopOnFail */ + res = production_test_ms_cx(LIMITS_FILE, functionToTest[1], &todoDefault); + else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_SSRAWTEST: /* SS RAW DATA TEST */ + if (numberParam == 2) /* need to specify if stopOnFail */ + res = production_test_ss_raw(LIMITS_FILE, functionToTest[1], &todoDefault); + else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_SSINITDATATEST: /* SS IX CX DATA TEST */ + if (numberParam == 2) /* need to specify if stopOnFail */ + res = production_test_ss_ix_cx(LIMITS_FILE, functionToTest[1], &todoDefault); + else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + /*PRODUCTION TEST*/ + case CMD_MAINTEST: + if (numberParam == 3) /* need to specify if stopOnFail and saveInit */ + res = production_test_main(LIMITS_FILE, functionToTest[1], functionToTest[2], &todoDefault, INIT_FIELD); + else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_POWERCYCLE: + res = fts_chip_powercycle(info); + break; + + default: + logError(1, "%s COMMAND ID NOT VALID!! Inset a value between 00 and 1E..\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + + /*res2 = fts_enableInterrupt(); enabling the interrupt was disabled on purpose in this node because it can be used for testing procedure and between one step and another the interrupt wan to be kept disabled + if (res2 < 0) { + logError(0, "%s stm_driver_test_show: ERROR %08X\n", tag, (res2 | ERROR_ENABLE_INTER)); + }*/ + } else { + logError(1, "%s NO COMMAND SPECIFIED!!! do: 'echo [cmd_code] [args] > stm_fts_cmd' before looking for result!\n", tag); + res = ERROR_OP_NOT_ALLOW; + functionToTest = NULL; + } + +END: /* here start the reporting phase, assembling the data to send in the file node */ + all_strbuff = (u8 *) kmalloc(size, GFP_KERNEL); + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof (buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%08X", res); + strlcat(all_strbuff, buff, size); + + if (res >= OK) { + /*all the other cases are already fine printing only the res.*/ + switch (functionToTest[0]) { + case CMD_READ: + case CMD_READU16: + case CMD_READB2: + case CMD_READB2U16: + case CMD_POLLFOREVENT: + for (j = 0; j < byteToRead; j++) { + snprintf(buff, sizeof (buff), "%02X", readData[j]); + strlcat(all_strbuff, buff, size); + } + break; + + case CMD_GETFORCELEN: + case CMD_GETSENSELEN: + case CMD_FLASHSTATUS: + snprintf(buff, sizeof (buff), "%02X", (u8) temp); + strlcat(all_strbuff, buff, size); + break; + + case CMD_GETMSFRAME: + snprintf(buff, sizeof (buff), "%02X", (u8) frameMS.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", (u8) frameMS.header.sense_node); + strlcat(all_strbuff, buff, size); + + for (j = 0; j < frameMS.node_data_size; j++) { + snprintf(buff, sizeof (buff), "%04X", frameMS.node_data[j]); + strlcat(all_strbuff, buff, size); + } + + kfree(frameMS.node_data); + break; + + case CMD_GETSSFRAME: + snprintf(buff, sizeof (buff), "%02X", (u8) frameSS.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", (u8) frameSS.header.sense_node); + strlcat(all_strbuff, buff, size); + + /* Copying self raw data Force */ + for (j = 0; j < frameSS.header.force_node; j++) { + snprintf(buff, sizeof (buff), "%04X", frameSS.force_data[j]); + strlcat(all_strbuff, buff, size); + } + + /* Copying self raw data Sense */ + for (j = 0; j < frameSS.header.sense_node; j++) { + snprintf(buff, sizeof (buff), "%04X", frameSS.sense_data[j]); + strlcat(all_strbuff, buff, size); + } + + kfree(frameSS.force_data); + kfree(frameSS.sense_data); + break; + + case CMD_READMSCOMPDATA: + snprintf(buff, sizeof (buff), "%02X", (u8) compData.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", (u8) compData.header.sense_node); + strlcat(all_strbuff, buff, size); + + /* Cpying CX1 value */ + snprintf(buff, sizeof (buff), "%02X", compData.cx1); + strlcat(all_strbuff, buff, size); + + /* Copying CX2 values */ + for (j = 0; j < compData.node_data_size; j++) { + snprintf(buff, sizeof (buff), "%02X", *(compData.node_data + j)); + strlcat(all_strbuff, buff, size); + } + + kfree(compData.node_data); + break; + + case CMD_READSSCOMPDATA: + snprintf(buff, sizeof (buff), "%02X", comData.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", comData.header.sense_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", comData.f_ix1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", comData.s_ix1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", comData.f_cx1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", comData.s_cx1); + strlcat(all_strbuff, buff, size); + + /* Copying IX2 Force */ + for (j = 0; j < comData.header.force_node; j++) { + snprintf(buff, sizeof (buff), "%02X", comData.ix2_fm[j]); + strlcat(all_strbuff, buff, size); + } + + /* Copying IX2 Sense */ + for (j = 0; j < comData.header.sense_node; j++) { + snprintf(buff, sizeof (buff), "%02X", comData.ix2_sn[j]); + strlcat(all_strbuff, buff, size); + } + + /* Copying CX2 Force */ + for (j = 0; j < comData.header.force_node; j++) { + snprintf(buff, sizeof (buff), "%02X", comData.cx2_fm[j]); + strlcat(all_strbuff, buff, size); + } + + /* Copying CX2 Sense */ + for (j = 0; j < comData.header.sense_node; j++) { + snprintf(buff, sizeof (buff), "%02X", comData.cx2_sn[j]); + strlcat(all_strbuff, buff, size); + } + + kfree(comData.ix2_fm); + kfree(comData.ix2_sn); + kfree(comData.cx2_fm); + kfree(comData.cx2_sn); + break; + + case CMD_READGNCOMPDATA: + snprintf(buff, sizeof (buff), "%02X", gnData.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", gnData.header.sense_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", gnData.ftsd_lp_timer_cal0); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", gnData.ftsd_lp_timer_cal1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", gnData.ftsd_lp_timer_cal2); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", gnData.ftsd_lp_timer_cal3); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", gnData.ftsa_lp_timer_cal0); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", gnData.ftsa_lp_timer_cal1); + strlcat(all_strbuff, buff, size); + break; + + case CMD_GETFWVER: + snprintf(buff, sizeof (buff), "%04X", fw_version); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%04X", config_id); + strlcat(all_strbuff, buff, size); + break; + + case CMD_READCOMPDATAHEAD: + snprintf(buff, sizeof (buff), "%02X", dataHead.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", dataHead.sense_node); + strlcat(all_strbuff, buff, size); + break; + + default: + break; + + } + } + + snprintf(buff, sizeof (buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + numberParam = 0; /* need to reset the number of parameters in order to wait the next comand, comment if you want to repeat the last comand sent just doing a cat */ + /* logError(0,"%s numberParameters = %d\n",tag, numberParam); */ + kfree(all_strbuff); + kfree(functionToTest); + return count; + +} + +/*static DEVICE_ATTR(stm_driver_test, (S_IRWXU|S_IRWXG), stm_driver_test_show, stm_driver_test_store);*/ +static DEVICE_ATTR(stm_driver_test, (S_IRUGO | S_IWUSR | S_IWGRP), stm_driver_test_show, stm_driver_test_store); + +static struct attribute *test_cmd_attributes[] = { + &dev_attr_stm_driver_test.attr, + NULL, +}; + +struct attribute_group test_cmd_attr_group = { + .attrs = test_cmd_attributes, +}; + +#endif diff --git a/drivers/input/touchscreen/st/fts_fw.h b/drivers/input/touchscreen/st/fts_fw.h new file mode 100644 index 000000000000..d928a06951ea --- /dev/null +++ b/drivers/input/touchscreen/st/fts_fw.h @@ -0,0 +1,10 @@ +#ifndef FTS_FW_H +#define FTS_FW_H +/* This is an auto generated header file +* --->Remember to change the name of the two variables!<--- */ +const uint32_t myArray_size; + +const uint8_t myArray[] = { +}; + +#endif diff --git a/drivers/input/touchscreen/st/fts_gui.c b/drivers/input/touchscreen/st/fts_gui.c new file mode 100644 index 000000000000..f695137ada09 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_gui.c @@ -0,0 +1,359 @@ +#include <linux/device.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/interrupt.h> +#include <linux/hrtimer.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/completion.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/regulator/consumer.h> +#include "fts.h" +#include "fts_lib/ftsCompensation.h" +#include "fts_lib/ftsIO.h" +#include "fts_lib/ftsError.h" +#include "fts_lib/ftsFrame.h" +#include "fts_lib/ftsTest.h" +#include "fts_lib/ftsTime.h" +#include "fts_lib/ftsTool.h" + +#ifdef SCRIPTLESS + +unsigned int data[CMD_RESULT_STR_LEN] = {0}; +unsigned char pAddress_i2c[CMD_RESULT_STR_LEN] = {0}; +int byte_count_read; +char Out_buff[TSP_BUF_SIZE]; + +/*I2C CMd functions: functions to interface with GUI without script */ + +ssize_t fts_i2c_wr_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + int i; + char buff[16]; + memset(Out_buff, 0x00, ARRAY_SIZE(Out_buff)); + if (byte_count_read == 0) { + snprintf(Out_buff, sizeof(Out_buff), "{FAILED}"); + return snprintf(buf, TSP_BUF_SIZE, "{%s}\n", Out_buff); + } +#ifdef SCRIPTLESS_DEBUG + printk("%s:DATA READ {", __func__); + for (i = 0; i < byte_count_read; i++) { + printk(" %02X", (unsigned int)info->cmd_wr_result[i]); + if (i < (byte_count_read-1)) { + printk(" "); + } + } + printk("}\n"); +#endif + snprintf(buff, sizeof(buff), "{"); + strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); + for (i = 0; i < (byte_count_read+2); i++) { + if ((i == 0)) { + char temp_byte_count_read = (byte_count_read >> 8) & 0xFF; + snprintf(buff, sizeof(buff), "%02X", temp_byte_count_read); + } else if (i == 1) { + char temp_byte_count_read = (byte_count_read) & 0xFF; + snprintf(buff, sizeof(buff), "%02X", temp_byte_count_read); + + } else { + snprintf(buff, sizeof(buff), "%02X", info->cmd_wr_result[i-2]); + } + /* snprintf(buff, sizeof(buff), "%02X", info->cmd_wr_result[i]); */ + strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); + if (i < (byte_count_read+1)) { + snprintf(buff, sizeof(buff), " "); + strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); + } + } + snprintf(buff, sizeof(buff), "}"); + strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); + return snprintf(buf, TSP_BUF_SIZE, "%s\n", Out_buff); +} + +ssize_t fts_i2c_wr_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + unsigned char pAddress[8] = {0}; + unsigned int byte_count = 0 ; + int i ; + + unsigned int data[8] = {0}; + memset(data, 0x00, ARRAY_SIZE(data)); + memset(info->cmd_wr_result, 0x00, ARRAY_SIZE(info->cmd_wr_result)); + sscanf(buf, "%x %x %x %x %x %x %x %x ", (data+7), (data), (data+1), + (data+2), (data+3), (data+4), (data+5), (data+6)); + + byte_count = data[7]; + + /*if (sizeof(buf) != byte_count ) + { + printk("%s : Byte count is wrong\n",__func__); + return count; + }*/ +#ifdef SCRIPTLESS_DEBUG + printk("\n"); + printk("%s: Input Data 1:", __func__); + + for (i = 0 ; i < 7; i++) { + printk(" %02X", data[i]); + pAddress[i] = (unsigned char)data[i]; + } + printk("\n"); +#else + for (i = 0 ; i < 7; i++) { + pAddress[i] = (unsigned char)data[i]; + } +#endif + byte_count_read = data[byte_count-1]; + ret = fts_writeCmd(pAddress, 3); + msleep(20); + ret = fts_readCmd(&pAddress[3], (byte_count-4), info->cmd_wr_result, + byte_count_read); +#ifdef SCRIPTLESS_DEBUG + printk("%s:DATA READ\n{", __func__); + for (i = 0; i < (2+byte_count_read); i++) { + if ((i == 0)) { + char temp_byte_count_read = (byte_count_read >> 8) & 0xFF; + printk("%02X", (unsigned int)temp_byte_count_read); + } else if (i == 1) { + char temp_byte_count_read = (byte_count_read) & 0xFF; + printk("%02X", (unsigned int)temp_byte_count_read); + + } else { + printk("%02X", (unsigned int)info->cmd_read_result[i-2]); + } + if (i < (byte_count_read+1)) { + printk(" "); + } + + } + printk("}\n"); +#endif + if (ret) + dev_err(dev, "Unable to read register\n"); + return count; +} + +ssize_t fts_i2c_read_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + int i ; + char buff[16]; + + memset(Out_buff, 0x00, ARRAY_SIZE(Out_buff)); + if (byte_count_read == 0) { + snprintf(Out_buff, sizeof(Out_buff), "{FAILED}"); + return snprintf(buf, TSP_BUF_SIZE, "{%s}\n", Out_buff); + } +#ifdef SCRIPTLESS_DEBUG + printk("%s:DATA READ {", __func__); + for (i = 0; i < byte_count_read; i++) { + printk("%02X", (unsigned int)info->cmd_read_result[i]); + if (i < (byte_count_read-1)) { + printk(" "); + } + } + printk("}\n"); +#endif + snprintf(buff, sizeof(buff), "{"); + strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); + for (i = 0; i < (byte_count_read+2); i++) { + if ((i == 0)) { + char temp_byte_count_read = (byte_count_read >> 8) & 0xFF; + snprintf(buff, sizeof(buff), "%02X", temp_byte_count_read); + } else if (i == 1) { + char temp_byte_count_read = (byte_count_read) & 0xFF; + snprintf(buff, sizeof(buff), "%02X", temp_byte_count_read); + + } else { + snprintf(buff, sizeof(buff), "%02X", info->cmd_read_result[i-2]); + } + strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); + if (i < (byte_count_read+1)) { + snprintf(buff, sizeof(buff), " "); + strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); + } + } + snprintf(buff, sizeof(buff), "}"); + strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); + + return snprintf(buf, TSP_BUF_SIZE, "%s\n", Out_buff); +} + +ssize_t fts_i2c_read_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + unsigned char pAddress[8] = {0}; + unsigned int byte_count = 0; + int i ; + unsigned int data[8] = {0}; + + byte_count_read = 0; + memset(data, 0x00, ARRAY_SIZE(data)); + memset(info->cmd_read_result, 0x00, ARRAY_SIZE(info->cmd_read_result)); + sscanf(buf, "%x %x %x %x %x %x %x %x ", (data+7), (data), (data+1), (data+2), (data+3), (data+4), (data+5), (data+6)); + byte_count = data[7]; + + if (byte_count > 7) { +#ifdef SCRIPTLESS_DEBUG + printk("%s : Byte count is more than 7\n", __func__); +#endif + return count; + } + /*if (sizeof(buf) != byte_count ) + { + printk("%s : Byte count is wrong\n",__func__); + return count; + }*/ +#ifdef SCRIPTLESS_DEBUG + printk("\n"); + printk("%s: Input Data 1:", __func__); + for (i = 0 ; i < byte_count; i++) { + printk(" %02X", data[i]); + pAddress[i] = (unsigned char)data[i]; + } + printk("\n"); +#else + for (i = 0 ; i < byte_count; i++) { + pAddress[i] = (unsigned char)data[i]; + } +#endif + byte_count_read = data[byte_count-1]; + ret = fts_readCmd(pAddress, (byte_count-1), info->cmd_read_result, byte_count_read); +#ifdef SCRIPTLESS_DEBUG + printk("%s:DATA READ\n{", __func__); + for (i = 0; i < (byte_count_read+2); i++) { + if ((i == 0)) { + char temp_byte_count_read = (byte_count_read >> 8) & 0xFF; + printk("%02X", (unsigned int)temp_byte_count_read); + } else if (i == 1) { + char temp_byte_count_read = (byte_count_read) & 0xFF; + printk("%02X", (unsigned int)temp_byte_count_read); + + } else { + printk("%02X", (unsigned int)info->cmd_read_result[i-2]); + } + if (i < (byte_count_read+1)) { + printk(" "); + } + } + printk("}\n"); +#endif + if (ret) + dev_err(dev, "Unable to read register\n"); + return count; +} + +ssize_t fts_i2c_write_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + return snprintf(buf, TSP_BUF_SIZE, "%s", info->cmd_write_result); + +} + +ssize_t fts_i2c_write_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + unsigned int byte_count = 0; + int i ; + memset(data, 0x00, ARRAY_SIZE(data)); + memset(pAddress_i2c, 0x00, ARRAY_SIZE(pAddress_i2c)); + memset(info->cmd_write_result, 0x00, ARRAY_SIZE(info->cmd_write_result)); + sscanf(buf, "%x %x", data, (data + 1)); + byte_count = data[0] << 8 | data[1]; + + if (byte_count <= ARRAY_SIZE(pAddress_i2c)) { + for (i = 0; i < (byte_count); i++) { + sscanf(&buf[3*(i+2)], "%x ", (data+i)); + } } else { +#ifdef SCRIPTLESS_DEBUG + printk("%s : message size is more than allowed limit of 512 bytes\n", __func__); +#endif + snprintf(info->cmd_write_result, sizeof(info->cmd_write_result), "{Write NOT OK}\n"); + } +#ifdef SCRIPTLESS_DEBUG + printk("\n"); + printk("%s: Byte_count= %02d | Count = %02d | size of buf:%02d\n", __func__, byte_count, (int)count, (int)sizeof(buf)); + printk("%s: Input Data 1:", __func__); + for (i = 0 ; i < byte_count; i++) { + printk("%02X", data[i]); + pAddress_i2c[i] = (unsigned char)data[i]; + } + printk("\n"); +#else + for (i = 0; i < byte_count; i++) { + pAddress_i2c[i] = (unsigned char)data[i]; + } +#endif + if ((pAddress_i2c[0] == 0xb3) && (pAddress_i2c[3] == 0xb1)) { + ret = fts_writeCmd(pAddress_i2c, 3); + msleep(20); + ret = fts_writeCmd(&pAddress_i2c[3], byte_count-3); + } else { + ret = fts_writeCmd(pAddress_i2c, byte_count); + } + +#ifdef SCRIPTLESS_DEBUG + printk("%s:DATA :", __func__); + for (i = 0; i < byte_count; i++) { + printk(" %02X", (unsigned int)pAddress_i2c[i]); + } + printk(" byte_count: %02X\n", byte_count); +#endif + if (ret < 0) { + dev_err(dev, "{Write NOT OK}\n"); + snprintf(info->cmd_write_result, sizeof(info->cmd_write_result), "{Write NOT OK}\n"); + } else { + snprintf(info->cmd_write_result, sizeof(info->cmd_write_result), "{Write OK}\n"); +#ifdef SCRIPTLESS_DEBUG + printk("%s : {Write OK}\n", __func__); +#endif + } + return count; +} + +static DEVICE_ATTR(iread, (S_IWUSR|S_IWGRP), NULL, fts_i2c_read_store); +static DEVICE_ATTR(iread_result, (S_IRUSR|S_IRGRP), fts_i2c_read_show, NULL); +static DEVICE_ATTR(iwr, (S_IWUSR|S_IWGRP), NULL, fts_i2c_wr_store); +static DEVICE_ATTR(iwr_result, (S_IRUSR|S_IRGRP), fts_i2c_wr_show, NULL); +static DEVICE_ATTR(iwrite, (S_IWUSR|S_IWGRP), NULL, fts_i2c_write_store); +static DEVICE_ATTR(iwrite_result, (S_IRUSR|S_IRGRP), fts_i2c_write_show, NULL); + +static struct attribute *i2c_cmd_attributes[] = { + &dev_attr_iread.attr, + &dev_attr_iread_result.attr, + &dev_attr_iwr.attr, + &dev_attr_iwr_result.attr, + &dev_attr_iwrite.attr, + &dev_attr_iwrite_result.attr, + NULL, +}; + +struct attribute_group i2c_cmd_attr_group = { + .attrs = i2c_cmd_attributes, +}; + +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/Makefile b/drivers/input/touchscreen/st/fts_lib/Makefile new file mode 100644 index 000000000000..24eca48fc3cd --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the FTS touchscreen driver. +# + +obj-$(CONFIG_TOUCHSCREEN_ST_I2C) += ftsCompensation.o \ + ftsCrossCompile.o ftsError.o ftsFrame.o ftsIO.o ftsTest.o \ + ftsTime.o ftsTool.o ftsFlash.o ftsGesture.o diff --git a/drivers/input/touchscreen/st/fts_lib/ftsCompensation.c b/drivers/input/touchscreen/st/fts_lib/ftsCompensation.c new file mode 100644 index 000000000000..2ca0067f34b0 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsCompensation.c @@ -0,0 +1,591 @@ +/* +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FTS functions for getting Initialization Data * +* * +************************************************************************** +************************************************************************** +*/ + +#include "ftsCrossCompile.h" +#include "ftsCompensation.h" +#include "ftsError.h" +#include "ftsFrame.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTool.h" + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <stdarg.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/serio.h> +#include <linux/time.h> +#include <linux/pm.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/power_supply.h> +#include <linux/firmware.h> +#include <linux/regulator/consumer.h> +#include <linux/of_gpio.h> +/* #include <linux/sec_sysfs.h> */ + +static char tag[8] = "[ FTS ]\0"; + +chipInfo ftsInfo; + +int requestCompensationData(u16 type) +{ + int retry = 0; + int ret; + u16 answer; + + int event_to_search[3]; + u8 readEvent[FIFO_EVENT_SIZE]; + + u8 cmd[3] = { FTS_CMD_REQU_COMP_DATA, 0x00, 0x00 }; + /* B8 is the command for asking compensation data */ + u16ToU8(type, &cmd[1]); + + event_to_search[0] = (int)EVENTID_COMP_DATA_READ; + event_to_search[1] = cmd[1]; + event_to_search[2] = cmd[2]; + + while (retry < COMP_DATA_READ_RETRY) { + logError(0, "%s %s", tag, printHex("Command = ", cmd, 3)); + ret = fts_writeFwCmd(cmd, 3); + /* send the request to the chip to load in memory the Compensation Data */ + if (ret < OK) { + logError(1, "%s requestCompensationData: ERROR %02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + ret = pollForEvent(event_to_search, 3, readEvent, TIMEOUT_REQU_COMP_DATA); + if (ret < OK) { + logError(0, "%s Event did not Found at %d attemp! \n", tag, retry+1); + retry += 1; + } else { + retry = 0; + break; + } + } + + if (retry == COMP_DATA_READ_RETRY) { + logError(1, "%s requestCompensationData: ERROR %02X\n", tag, ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } + + u8ToU16_le(&readEvent[1], &answer); + + if (answer == type) + return OK; + logError(1, "%s The event found has a different type of Compensation data ERROR %02X \n", tag, ERROR_DIFF_COMP_TYPE); + return ERROR_DIFF_COMP_TYPE; + +} + +int readCompensationDataHeader(u16 type, DataHeader *header, u16 *address) +{ + + u16 offset = ADDR_FRAMEBUFFER_DATA; + u16 answer; + u8 data[COMP_DATA_HEADER]; + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, offset, data, COMP_DATA_HEADER, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s readCompensationDataHeader: ERROR %02X \n", tag, ERROR_I2C_R); + return ERROR_I2C_R; + } + logError(0, "%s Read Data Header done! \n", tag); + + if (data[0] != HEADER_SIGNATURE) { + logError(1, "%s readCompensationDataHeader: ERROR %02X The Header Signature was wrong! %02X != %02X \n", tag, ERROR_WRONG_COMP_SIGN, data[0], HEADER_SIGNATURE); + return ERROR_WRONG_COMP_SIGN; + } + + u8ToU16_le(&data[1], &answer); + + if (answer != type) { + logError(1, "%s readCompensationDataHeader: ERROR %02X\n", tag, ERROR_DIFF_COMP_TYPE); + return ERROR_DIFF_COMP_TYPE; + } + + logError(0, "%s Type of Compensation data OK! \n", tag); + + header->type = type; + header->force_node = (int)data[4]; + header->sense_node = (int)data[5]; + + *address = offset + COMP_DATA_HEADER; + + return OK; + +} + +int readMutualSenseGlobalData(u16 *address, MutualSenseData *global) +{ + + u8 data[COMP_DATA_GLOBAL]; + + logError(0, "%s Address for Global data= %02X \n", tag, *address); + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, *address, data, COMP_DATA_GLOBAL, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s readMutualSenseGlobalData: ERROR %02X\n", tag, ERROR_I2C_R); + return ERROR_I2C_R; + } + logError(0, "%s Global data Read !\n", tag); + + global->tuning_ver = data[0]; + global->cx1 = data[1]; + + logError(0, "%s tuning_ver = %d CX1 = %d \n", tag, global->tuning_ver, global->cx1); + + *address += COMP_DATA_GLOBAL; + return OK; + +} + +int readMutualSenseNodeData(u16 address, MutualSenseData *node) +{ + + int size = node->header.force_node*node->header.sense_node; + + logError(0, "%s Address for Node data = %02X \n", tag, address); + + node->node_data = (u8 *)kmalloc(size*(sizeof(u8)), GFP_KERNEL); + + if (node->node_data == NULL) { + logError(1, "%s readMutualSenseNodeData: ERROR %02X", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + logError(0, "%s Node Data to read %d bytes \n", tag, size); + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, address, node->node_data, size, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s readMutualSenseNodeData: ERROR %02X \n", tag, ERROR_I2C_R); + return ERROR_I2C_R; + } + node->node_data_size = size; + + logError(0, "%s Read node data ok! \n", tag); + + return size; + +} + +int readMutualSenseCompensationData(u16 type, MutualSenseData *data) +{ + + int ret; + u16 address; + + if (!(type == MS_TOUCH_ACTIVE || type == MS_TOUCH_LOW_POWER || + type == MS_TOUCH_ULTRA_LOW_POWER || type == MS_KEY)) { + logError(1, "%s readMutualSenseCompensationData: Choose a MS type of compensation data ERROR %02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = requestCompensationData(type); + if (ret < 0) { + logError(1, "%s readMutualSenseCompensationData: ERROR %02X\n", tag, ERROR_REQU_COMP_DATA); + return (ret|ERROR_REQU_COMP_DATA); + } + + ret = readCompensationDataHeader(type, &(data->header), &address); + if (ret < 0) { + logError(1, "%s readMutualSenseCompensationData: ERROR %02X\n", tag, ERROR_COMP_DATA_HEADER); + return (ret|ERROR_COMP_DATA_HEADER); + } + + ret = readMutualSenseGlobalData(&address, data); + if (ret < 0) { + logError(1, "%s readMutualSenseCompensationData: ERROR %02X \n", tag, ERROR_COMP_DATA_GLOBAL); + return (ret|ERROR_COMP_DATA_GLOBAL); + } + + ret = readMutualSenseNodeData(address, data); + if (ret < 0) { + logError(1, "%s readMutualSenseCompensationData: ERROR %02X\n", tag, ERROR_COMP_DATA_NODE); + return (ret|ERROR_COMP_DATA_NODE); + } + + return OK; + +} + +int readSelfSenseGlobalData(u16 *address, SelfSenseData *global) +{ + + u8 data[COMP_DATA_GLOBAL]; + + logError(0, "%s Address for Global data= %02X \n", tag, *address); + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, *address, data, COMP_DATA_GLOBAL, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s readSelfSenseGlobalData: ERROR %02X \n", tag, ERROR_I2C_R); + return ERROR_I2C_R; + } + + logError(0, "%s Global data Read !\n", tag); + + global->tuning_ver = data[0]; + global->f_ix1 = data[1]; + global->s_ix1 = data[2]; + global->f_cx1 = data[3]; + global->s_cx1 = data[4]; + global->f_max_n = data[5]; + global->s_max_n = data[6]; + + logError(0, "%s tuning_ver = %d f_ix1 = %d s_ix1 = %d f_cx1 = %d s_cx1 = %d \n", tag, global->tuning_ver, global->f_ix1, global->s_ix1, global->f_cx1, global->s_cx1); + logError(0, "%s max_n = %d s_max_n = %d \n", tag, global->f_max_n, global->s_max_n); + + *address += COMP_DATA_GLOBAL; + + return OK; + +} + +int readSelfSenseNodeData(u16 address, SelfSenseData *node) +{ + + int size = node->header.force_node*2+node->header.sense_node*2; + u8 data[size]; + + node->ix2_fm = (u8 *)kmalloc(node->header.force_node*(sizeof(u8)), GFP_KERNEL); + node->cx2_fm = (u8 *)kmalloc(node->header.force_node*(sizeof(u8)), GFP_KERNEL); + node->ix2_sn = (u8 *)kmalloc(node->header.sense_node*(sizeof(u8)), GFP_KERNEL); + node->cx2_sn = (u8 *)kmalloc(node->header.sense_node*(sizeof(u8)), GFP_KERNEL); + + if (node->ix2_fm == NULL || node->cx2_fm == NULL || node->ix2_sn == NULL + || node->cx2_sn == NULL) { + logError(1, "%s readSelfSenseNodeData: ERROR %02X", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + logError(0, "%s Address for Node data = %02X \n", tag, address); + + logError(0, "%s Node Data to read %d bytes \n", tag, size); + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, address, data, size, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s readSelfSenseNodeData: ERROR %02X\n", tag, ERROR_I2C_R); + return ERROR_I2C_R; + } + + logError(0, "%s Read node data ok! \n", tag); + + memcpy(node->ix2_fm, data, node->header.force_node); + memcpy(node->ix2_sn, &data[node->header.force_node], node->header.sense_node); + memcpy(node->cx2_fm, &data[node->header.force_node + node->header.sense_node], node->header.force_node); + memcpy(node->cx2_sn, &data[node->header.force_node*2 + node->header.sense_node], node->header.sense_node); + + return OK; + +} + +int readSelfSenseCompensationData(u16 type, SelfSenseData *data) +{ + + int ret; + u16 address; + + if (!(type == SS_TOUCH || type == SS_KEY || type == SS_HOVER || type == SS_PROXIMITY)) { + logError(1, "%s readSelfSenseCompensationData: Choose a SS type of compensation data ERROR %02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = requestCompensationData(type); + if (ret < 0) { + logError(1, "%s readSelfSenseCompensationData: ERROR %02X\n", tag, ERROR_REQU_COMP_DATA); + return (ret|ERROR_REQU_COMP_DATA); + } + + ret = readCompensationDataHeader(type, &(data->header), &address); + if (ret < 0) { + logError(1, "%s readSelfSenseCompensationData: ERROR %02X\n", tag, ERROR_COMP_DATA_HEADER); + return (ret|ERROR_COMP_DATA_HEADER); + } + + ret = readSelfSenseGlobalData(&address, data); + if (ret < 0) { + logError(1, "%s readSelfSenseCompensationData: ERROR %02X\n", tag, ERROR_COMP_DATA_GLOBAL); + return (ret|ERROR_COMP_DATA_GLOBAL); + } + + ret = readSelfSenseNodeData(address, data); + if (ret < 0) { + logError(1, "%s readSelfSenseCompensationData: ERROR %02X\n", tag, ERROR_COMP_DATA_NODE); + return (ret|ERROR_COMP_DATA_NODE); + } + + return OK; + +} + +int readGeneralGlobalData(u16 address, GeneralData *global) +{ + u8 data[COMP_DATA_GLOBAL]; + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, address, data, COMP_DATA_GLOBAL, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s readGeneralGlobalData: ERROR %02X \n", tag, ERROR_I2C_R); + return ERROR_I2C_R; + } + + global->ftsd_lp_timer_cal0 = data[0]; + global->ftsd_lp_timer_cal1 = data[1]; + global->ftsd_lp_timer_cal2 = data[2]; + global->ftsd_lp_timer_cal3 = data[3]; + global->ftsa_lp_timer_cal0 = data[4]; + global->ftsa_lp_timer_cal1 = data[5]; + + return OK; + +} + +int readGeneralCompensationData(u16 type, GeneralData *data) +{ + + int ret; + u16 address; + + if (!(type == GENERAL_TUNING)) { + logError(1, "%s readGeneralCompensationData: Choose a GENERAL type of compensation data ERROR %02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = requestCompensationData(type); + if (ret < 0) { + logError(1, "%s readGeneralCompensationData: ERROR %02X \n", tag, ERROR_REQU_COMP_DATA); + return ERROR_REQU_COMP_DATA; + } + + ret = readCompensationDataHeader(type, &(data->header), &address); + if (ret < 0) { + logError(1, "%s readGeneralCompensationData: ERROR %02X\n", tag, ERROR_COMP_DATA_HEADER); + return ERROR_COMP_DATA_HEADER; + } + + ret = readGeneralGlobalData(address, data); + if (ret < 0) { + logError(1, "%s readGeneralCompensationData: ERROR %02X\n", tag, ERROR_COMP_DATA_GLOBAL); + return ERROR_COMP_DATA_GLOBAL; + } + + return OK; + +} + +int defaultChipInfo(int i2cError) +{ + int i; + logError(0, "%s Setting default Chip Info... \n", tag); + ftsInfo.u32_echoEn = 0x00000000; + ftsInfo.u8_msScrConfigTuneVer = 0; + ftsInfo.u8_ssTchConfigTuneVer = 0; + ftsInfo.u8_msScrCxmemTuneVer = 0; + ftsInfo.u8_ssTchCxmemTuneVer = 0; + if (i2cError == 1) { + ftsInfo.u16_fwVer = 0xFFFF; + ftsInfo.u16_cfgId = 0xFFFF; + for (i = 0; i < EXTERNAL_RELEASE_INFO_SIZE; i++) { + ftsInfo.u8_extReleaseInfo[i] = 0xFF; + } + } else { + ftsInfo.u16_fwVer = 0x0000; + ftsInfo.u16_cfgId = 0x0000; + for (i = 0; i < EXTERNAL_RELEASE_INFO_SIZE; i++) { + ftsInfo.u8_extReleaseInfo[i] = 0x00; + } + } + ftsInfo.u32_mpPassFlag = INIT_FIELD; + logError(0, "%s default Chip Info DONE! \n", tag); + return OK; + +} + +int readChipInfo(int doRequest) +{ + int ret, i; + u16 answer; + u8 data[CHIP_INFO_SIZE+3]; + /* +3 because need to read all the field of the struct plus the signature and 2 address bytes */ + int index = 0; + + logError(0, "%s Starting Read Chip Info... \n", tag); + if (doRequest == 1) { + ret = requestCompensationData(CHIP_INFO); + if (ret < 0) { + logError(1, "%s readChipInfo: ERROR %02X\n", tag, ERROR_REQU_COMP_DATA); + ret = (ret | ERROR_REQU_COMP_DATA); + goto FAIL; + } + } + + logError(0, "%s Byte to read = %d bytes \n", tag, CHIP_INFO_SIZE+3); + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, ADDR_FRAMEBUFFER_DATA, data, CHIP_INFO_SIZE+3, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s readChipInfo: ERROR %02X\n", tag, ERROR_I2C_R); + ret = ERROR_I2C_R; + goto FAIL; + } + + logError(0, "%s Read data ok! \n", tag); + + logError(0, "%s Starting parsing of data... \n", tag); + + if (data[0] != HEADER_SIGNATURE) { + logError(1, "%s readChipInfo: ERROR %02X The Header Signature was wrong! %02X != %02X \n", tag, ERROR_WRONG_COMP_SIGN, data[0], HEADER_SIGNATURE); + ret = ERROR_WRONG_COMP_SIGN; + goto FAIL; + } + + u8ToU16_le(&data[1], &answer); + + if (answer != CHIP_INFO) { + logError(1, "%s readChipInfo: ERROR %02X\n", tag, ERROR_DIFF_COMP_TYPE); + ret = ERROR_DIFF_COMP_TYPE; + goto FAIL; + } + + index += 3; + ftsInfo.u8_loadCnt = data[index++]; + ftsInfo.u8_infoVer = data[index++]; + u8ToU16(&data[index], &ftsInfo.u16_ftsdId); + index += 2; + ftsInfo.u8_ftsdVer = data[index++]; + ftsInfo.u8_ftsaId = data[index++]; + ftsInfo.u8_ftsaVer = data[index++]; + ftsInfo.u8_tchRptVer = data[index++]; + + logError(1, "%s External Release = ", tag); + for (i = 0; i < EXTERNAL_RELEASE_INFO_SIZE; i++) { + ftsInfo.u8_extReleaseInfo[i] = data[index++]; + logError(1, "%02X ", ftsInfo.u8_extReleaseInfo[i]); + } + logError(1, "\n"); + + for (i = 0; i < sizeof(ftsInfo.u8_custInfo); i++) { + ftsInfo.u8_custInfo[i] = data[index++]; + } + + u8ToU16(&data[index], &ftsInfo.u16_fwVer); + index += 2; + logError(1, "%s FW VERSION = %04X \n", tag, ftsInfo.u16_fwVer); + + u8ToU16(&data[index], &ftsInfo.u16_cfgId); + index += 2; + logError(1, "%s CONFIG ID = %04X \n", tag, ftsInfo.u16_cfgId); + + ftsInfo.u32_projId = ((data[index + 3] & 0x000000FF) << 24) + ((data[index + 2] & 0x000000FF) << 16) + ((data[index + 1] & 0x000000FF) << 8) + (data[index] & 0x000000FF); + index += 4; + + u8ToU16(&data[index], &ftsInfo.u16_scrXRes); + index += 2; + + u8ToU16(&data[index], &ftsInfo.u16_scrYRes); + index += 2; + + ftsInfo.u8_scrForceLen = data[index++]; + logError(1, "%s Force Len = %d \n", tag, ftsInfo.u8_scrForceLen); + + ftsInfo.u8_scrSenseLen = data[index++]; + logError(1, "%s Sense Len = %d \n", tag, ftsInfo.u8_scrSenseLen); + + for (i = 0; i < 8; i++) { + ftsInfo.u64_scrForceEn[i] = data[index++]; + } + + for (i = 0; i < 8; i++) { + ftsInfo.u64_scrSenseEn[i] = data[index++]; + } + + ftsInfo.u8_msKeyLen = data[index++]; + logError(1, "%s MS Key Len = %d \n", tag, ftsInfo.u8_msKeyLen); + + for (i = 0; i < 8; i++) { + ftsInfo.u64_msKeyForceEn[i] = data[index++]; + } + + for (i = 0; i < 8; i++) { + ftsInfo.u64_msKeySenseEn[i] = data[index++]; + } + + ftsInfo.u8_ssKeyLen = data[index++]; + logError(1, "%s SS Key Len = %d \n", tag, ftsInfo.u8_ssKeyLen); + + for (i = 0; i < 8; i++) { + ftsInfo.u64_ssKeyForceEn[i] = data[index++]; + } + + for (i = 0; i < 8; i++) { + ftsInfo.u64_ssKeySenseEn[i] = data[index++]; + } + + ftsInfo.u8_frcTchXLen = data[index++]; + + ftsInfo.u8_frcTchYLen = data[index++]; + + for (i = 0; i < 8; i++) { + ftsInfo.u64_frcTchForceEn[i] = data[index++]; + } + + for (i = 0; i < 8; i++) { + ftsInfo.u64_frcTchSenseEn[i] = data[index++]; + } + + ftsInfo.u8_msScrConfigTuneVer = data[index++]; + logError(1, "%s CFG MS TUNING VERSION = %02X \n", tag, ftsInfo.u8_msScrConfigTuneVer); + ftsInfo.u8_msScrLpConfigTuneVer = data[index++]; + ftsInfo.u8_msScrHwulpConfigTuneVer = data[index++]; + ftsInfo.u8_msKeyConfigTuneVer = data[index++]; + ftsInfo.u8_ssTchConfigTuneVer = data[index++]; + logError(1, "%s CFG SS TUNING VERSION = %02X \n", tag, ftsInfo.u8_ssTchConfigTuneVer); + ftsInfo.u8_ssKeyConfigTuneVer = data[index++]; + ftsInfo.u8_ssHvrConfigTuneVer = data[index++]; + ftsInfo.u8_frcTchConfigTuneVer = data[index++]; + ftsInfo.u8_msScrCxmemTuneVer = data[index++]; + logError(1, "%s CX MS TUNING VERSION = %02X \n", tag, ftsInfo.u8_msScrCxmemTuneVer); + ftsInfo.u8_msScrLpCxmemTuneVer = data[index++]; + ftsInfo.u8_msScrHwulpCxmemTuneVer = data[index++]; + ftsInfo.u8_msKeyCxmemTuneVer = data[index++]; + ftsInfo.u8_ssTchCxmemTuneVer = data[index++]; + logError(1, "%s CX SS TUNING VERSION = %02X \n", tag, ftsInfo.u8_ssTchCxmemTuneVer); + ftsInfo.u8_ssKeyCxmemTuneVer = data[index++]; + ftsInfo.u8_ssHvrCxmemTuneVer = data[index++]; + ftsInfo.u8_frcTchCxmemTuneVer = data[index++]; + ftsInfo.u32_mpPassFlag = ((data[index + 3] & 0x000000FF) << 24) + ((data[index + 2] & 0x000000FF) << 16) + ((data[index + 1] & 0x000000FF) << 8) + (data[index] & 0x000000FF); + index += 4; + logError(1, "%s MP SIGNATURE = %08X \n", tag, ftsInfo.u32_mpPassFlag); + ftsInfo.u32_featEn = ((data[index + 3] & 0x000000FF) << 24) + ((data[index + 2] & 0x000000FF) << 16) + ((data[index + 1] & 0x000000FF) << 8) + (data[index] & 0x000000FF); + index += 4; + ftsInfo.u32_echoEn = ((data[index + 3] & 0x000000FF) << 24) + ((data[index + 2] & 0x000000FF) << 16) + ((data[index + 1] & 0x000000FF) << 8) + (data[index] & 0x000000FF); + index += 4; + logError(1, "%s FEATURES = %08X \n", tag, ftsInfo.u32_echoEn); + + logError(1, "%s Parsed %d bytes! \n", tag, index); + + if (index != CHIP_INFO_SIZE + 3) { + logError(1, "%s readChipInfo: index = %d different from %d ERROR %02X\n", tag, index, CHIP_INFO_SIZE+3, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + logError(1, "%s Chip Info Read DONE!\n", tag); + return OK; + +FAIL: + defaultChipInfo(isI2cError(ret)); + return ret; + +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsCompensation.h b/drivers/input/touchscreen/st/fts_lib/ftsCompensation.h new file mode 100644 index 000000000000..c4cc5913fc18 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsCompensation.h @@ -0,0 +1,146 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FTS functions for getting Initialization Data * +* * +************************************************************************** +************************************************************************** + +*/ + +#include "ftsCrossCompile.h" +#include "ftsSoftware.h" + +#define COMP_DATA_READ_RETRY 2 + +/* Bytes dimension of Compensation Data Format */ + +#define COMP_DATA_HEADER 8 +#define COMP_DATA_GLOBAL 8 + +#define HEADER_SIGNATURE 0xA5 + +/* Possible Compensation/Frame Data Type */ +#define GENERAL_TUNING 0x0100 +#define MS_TOUCH_ACTIVE 0x0200 +#define MS_TOUCH_LOW_POWER 0x0400 +#define MS_TOUCH_ULTRA_LOW_POWER 0x0800 +#define MS_KEY 0x1000 +#define SS_TOUCH 0x2000 +#define SS_KEY 0x4000 +#define SS_HOVER 0x8000 +#define SS_PROXIMITY 0x0001 +#define CHIP_INFO 0xFFFF + +#define TIMEOUT_REQU_COMP_DATA 1000 /* ms */ + +/* CHIP INFO */ +#define CHIP_INFO_SIZE 138/* bytes to read from framebuffer (exclude the signature and the type because already checked during the reading) */ +#define EXTERNAL_RELEASE_INFO_SIZE 8/* bytes */ + +typedef struct { + int force_node, sense_node; + u16 type; +} DataHeader; + +typedef struct { + DataHeader header; + u8 tuning_ver; + u8 cx1; + u8 *node_data; + int node_data_size; +} MutualSenseData; + +typedef struct { + DataHeader header; + u8 tuning_ver; + u8 f_ix1, s_ix1; + u8 f_cx1, s_cx1; + u8 f_max_n, s_max_n; + + u8 *ix2_fm; + u8 *ix2_sn; + u8 *cx2_fm; + u8 *cx2_sn; + +} SelfSenseData; + +typedef struct { + DataHeader header; + u8 ftsd_lp_timer_cal0; + u8 ftsd_lp_timer_cal1; + u8 ftsd_lp_timer_cal2; + + u8 ftsd_lp_timer_cal3; + u8 ftsa_lp_timer_cal0; + u8 ftsa_lp_timer_cal1; + +} GeneralData; + +typedef struct { + u8 u8_loadCnt; /* 03 - Load Counter */ + u8 u8_infoVer; /* 04 - New chip info version */ + u16 u16_ftsdId; /* 05 - FTSD ID */ + u8 u8_ftsdVer; /* 07 - FTSD version */ + u8 u8_ftsaId; /* 08 - FTSA ID */ + u8 u8_ftsaVer; /* 09 - FTSA version */ + u8 u8_tchRptVer; /* 0A - Touch report version (e.g. ST, Samsung etc) */ + u8 u8_extReleaseInfo[EXTERNAL_RELEASE_INFO_SIZE]; /* 0B - External release information */ + u8 u8_custInfo[12]; /* 13 - Customer information */ + u16 u16_fwVer; /* 1F - Firmware version */ + u16 u16_cfgId; /* 21 - Configuration ID */ + u32 u32_projId; /* 23 - Project ID */ + u16 u16_scrXRes; /* 27 - X resolution on main screen */ + u16 u16_scrYRes; /* 29 - Y resolution on main screen */ + u8 u8_scrForceLen; /* 2B - Number of force channel on main screen */ + u8 u8_scrSenseLen; /* 2C - Number of sense channel on main screen */ + u8 u64_scrForceEn[8]; /* 2D - Force channel enabled on main screen */ + u8 u64_scrSenseEn[8]; /* 35 - Sense channel enabled on main screen */ + u8 u8_msKeyLen; /* 3D - Number of MS Key channel */ + u8 u64_msKeyForceEn[8]; /* 3E - MS Key force channel enable */ + u8 u64_msKeySenseEn[8]; /* 46 - MS Key sense channel enable */ + u8 u8_ssKeyLen; /* 4E - Number of SS Key channel */ + u8 u64_ssKeyForceEn[8]; /* 4F - SS Key force channel enable */ + u8 u64_ssKeySenseEn[8]; /* 57 - SS Key sense channel enable */ + u8 u8_frcTchXLen; /* 5F - Number of force touch force channel */ + u8 u8_frcTchYLen; /* 60 - Number of force touch sense channel */ + u8 u64_frcTchForceEn[8]; /* 61 - Force touch force channel enable */ + u8 u64_frcTchSenseEn[8]; /* 69 - Force touch sense channel enable */ + u8 u8_msScrConfigTuneVer; /* 71 - MS screen tuning version in config */ + u8 u8_msScrLpConfigTuneVer; /* 72 - MS screen LP mode tuning version in config */ + u8 u8_msScrHwulpConfigTuneVer; /* 73 - MS screen ultra low power mode tuning version in config */ + u8 u8_msKeyConfigTuneVer; /* 74 - MS Key tuning version in config */ + u8 u8_ssTchConfigTuneVer; /* 75 - SS touch tuning version in config */ + u8 u8_ssKeyConfigTuneVer; /* 76 - SS Key tuning version in config */ + u8 u8_ssHvrConfigTuneVer; /* 77 - SS hover tuning version in config */ + u8 u8_frcTchConfigTuneVer; /* 78 - Force touch tuning version in config */ + u8 u8_msScrCxmemTuneVer; /* 79 - MS screen tuning version in cxmem */ + u8 u8_msScrLpCxmemTuneVer; /* 7A - MS screen LP mode tuning version in cxmem */ + u8 u8_msScrHwulpCxmemTuneVer; /* 7B - MS screen ultra low power mode tuning version in cxmem */ + u8 u8_msKeyCxmemTuneVer; /* 7C - MS Key tuning version in cxmem */ + u8 u8_ssTchCxmemTuneVer; /* 7D - SS touch tuning version in cxmem */ + u8 u8_ssKeyCxmemTuneVer; /* 7E - SS Key tuning version in cxmem */ + u8 u8_ssHvrCxmemTuneVer; /* 7F - SS hover tuning version in cxmem */ + u8 u8_frcTchCxmemTuneVer; /* 80 - Force touch tuning version in cxmem */ + u32 u32_mpPassFlag; /* 81 - Mass production pass flag */ + u32 u32_featEn; /* 85 - Supported features */ + u32 u32_echoEn; /* 89 - enable of particular features: first bit is Echo Enables */ +} chipInfo; + +int requestCompensationData(u16 type); +int readCompensationDataHeader(u16 type, DataHeader *header, u16 *address); +int readMutualSenseGlobalData(u16 *address, MutualSenseData *global); +int readMutualSenseNodeData(u16 address, MutualSenseData *node); +int readMutualSenseCompensationData(u16 type, MutualSenseData *data); +int readSelfSenseGlobalData(u16 *address, SelfSenseData *global); +int readSelfSenseNodeData(u16 address, SelfSenseData *node); +int readSelfSenseCompensationData(u16 type, SelfSenseData *data); +int readGeneralGlobalData(u16 address, GeneralData *global); +int readGeneralCompensationData(u16 type, GeneralData *data); +int defaultChipInfo(int i2cError); +int readChipInfo(int doRequest); diff --git a/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.c b/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.c new file mode 100644 index 000000000000..502dace75e4f --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.c @@ -0,0 +1,43 @@ +#include "ftsCrossCompile.h" +#include "ftsError.h" + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/serio.h> +#include <linux/init.h> +#include <linux/pm.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/power_supply.h> +#include <linux/firmware.h> +#include <linux/regulator/consumer.h> +#include <linux/of_gpio.h> +/* #include <linux/sec_sysfs.h> */ +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/spi/spidev.h> +#include <linux/fcntl.h> +#include <linux/syscalls.h> + +/* static char tag[8]="[ FTS ]\0"; */ +void *stmalloc(size_t size) +{ + return kmalloc(size, GFP_KERNEL); + +} + +void stfree(void *ptr) +{ + kfree(ptr); +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.h b/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.h new file mode 100644 index 000000000000..8b287dd342f8 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.h @@ -0,0 +1,34 @@ +/* #define NDK */ +/* #define DEBUG */ + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/serio.h> +#include <linux/init.h> +#include <linux/pm.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/power_supply.h> +#include <linux/firmware.h> +#include <linux/regulator/consumer.h> +#include <linux/of_gpio.h> +/* #include <linux/sec_sysfs.h> */ +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/spi/spidev.h> +#include <linux/fcntl.h> +#include <linux/syscalls.h> + +void *stmalloc(size_t size); +void stfree(void *ptr); diff --git a/drivers/input/touchscreen/st/fts_lib/ftsError.c b/drivers/input/touchscreen/st/fts_lib/ftsError.c new file mode 100644 index 000000000000..844e5019fec6 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsError.c @@ -0,0 +1,105 @@ +/* +*************************************************************************** +* STMicroelectronics +************************************************************************** +* marco.cali@st.com +************************************************************************** +* +* FTS error/info kernel log reporting +* +************************************************************************** +************************************************************************** +*/ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/interrupt.h> +#include <linux/hrtimer.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/completion.h> + +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/regulator/consumer.h> + +#include "../fts.h" +#include "ftsCrossCompile.h" +#include "ftsError.h" +#include "ftsIO.h" +#include "ftsTool.h" + +void logError(int force, const char *msg, ...) +{ + if (force == 1 +#ifdef DEBUG + || 1 +#endif + ) { + va_list args; + va_start(args, msg); + vprintk(msg, args); + va_end(args); + } +} + +int isI2cError(int error) +{ + if (((error & 0x000000FF) >= (ERROR_I2C_R & 0x000000FF)) && ((error & 0x000000FF) <= (ERROR_I2C_O & 0x000000FF))) + return 1; + else + return 0; +} + +int errorHandler(u8 *event, int size) +{ + int res = OK; + struct fts_ts_info *info = NULL; + + if (getClient() != NULL) + info = i2c_get_clientdata(getClient()); + + if (info != NULL && event != NULL && size > 1 && event[0] == EVENTID_ERROR_EVENT) { + logError(1, "%s errorHandler: Starting handling...\n", tag); + switch (event[1]) + /* TODO: write an error log for undefinied command subtype 0xBA*/ + { + case EVENT_TYPE_ESD_ERROR: /* esd */ + res = fts_chip_powercycle(info); + if (res < OK) { + logError(1, "%s errorHandler: Error performing powercycle ERROR %08X\n", tag, res); + } + + res = fts_system_reset(); + if (res < OK) { + logError(1, "%s errorHandler: Cannot reset the device ERROR %08X\n", tag, res); + } + res = (ERROR_HANDLER_STOP_PROC|res); + break; + + case EVENT_TYPE_WATCHDOG_ERROR: /* watchdog */ + res = fts_system_reset(); + if (res < OK) { + logError(1, "%s errorHandler: Cannot reset the device ERROR %08X\n", tag, res); + } + res = (ERROR_HANDLER_STOP_PROC|res); + break; + + default: + logError(1, "%s errorHandler: No Action taken! \n", tag); + break; + + } + logError(1, "%s errorHandler: handling Finished! res = %08X\n", tag, res); + return res; + } + logError(1, "%s errorHandler: event Null or not correct size! ERROR %08X \n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsError.h b/drivers/input/touchscreen/st/fts_lib/ftsError.h new file mode 100644 index 000000000000..fc8fa5003158 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsError.h @@ -0,0 +1,75 @@ +/* +************************************************************************** +** STMicroelectronics +************************************************************************** +** marco.cali@st.com +************************************************************************** +* +* FTS error/info kernel log reporting +* +************************************************************************** +************************************************************************** +*/ + + +/*FIRST LEVEL ERROR CODE*/ +#define OK ((int)0x00000000) /*No ERROR*/ +#define ERROR_ALLOC ((int)0x80000001) /*allocation of memory failed*/ +#define ERROR_I2C_R ((int)0x80000002) /*i2c read failed*/ +#define ERROR_I2C_W ((int)0x80000003) /*i2c write failed*/ +#define ERROR_I2C_WR ((int)0x80000004) /*i2c write/read failed*/ +#define ERROR_I2C_O ((int)0x80000005) /*error during opening a i2c device*/ +#define ERROR_OP_NOT_ALLOW ((int)0x80000006) /*operation not allowed*/ +#define ERROR_TIMEOUT ((int)0x80000007) /*timeout expired! exceed the max number of retries or the max waiting time*/ +#define ERROR_FILE_NOT_FOUND ((int)0x80000008) /*the file that i want to open is not found*/ +#define ERROR_FILE_PARSE ((int)0x80000009) /*error during parsing the file*/ +#define ERROR_FILE_READ ((int)0x8000000A) /*error during reading the file*/ +#define ERROR_LABEL_NOT_FOUND ((int)0x8000000B) /*label not found*/ +#define ERROR_FW_NO_UPDATE ((int)0x8000000C) /*fw in the chip newer than the one in the memmh*/ +#define ERROR_FLASH_UNKNOWN ((int)0x8000000D) /*flash status busy or unknown*/ + +/*SECOND LEVEL ERROR CODE*/ +#define ERROR_DISABLE_INTER ((int)0x80000200) /*unable to disable the interrupt*/ +#define ERROR_ENABLE_INTER ((int)0x80000300) /*unable to activate the interrup*/ +#define ERROR_READ_B2 ((int)0x80000400) /*B2 command failed*/ +#define ERROR_GET_OFFSET ((int)0x80000500) /*unable to read an offset from memory*/ +#define ERROR_GET_FRAME_DATA ((int)0x80000600) /*unable to retrieve the data of a required frame*/ +#define ERROR_DIFF_COMP_TYPE ((int)0x80000700) /*FW answers with an event that has a different address respect the request done*/ +#define ERROR_WRONG_COMP_SIGN ((int)0x80000800) /*the signature of the compensation data is not A5*/ +#define ERROR_SENSE_ON_FAIL ((int)0x80000900) /*the command Sense On failed*/ +#define ERROR_SENSE_OFF_FAIL ((int)0x80000A00) /*the command Sense Off failed*/ +#define ERROR_SYSTEM_RESET_FAIL ((int)0x80000B00) /*the command SYSTEM RESET failed*/ +#define ERROR_FLASH_NOT_READY ((int)0x80000C00) /*flash status not ready within a timeout*/ +#define ERROR_FW_VER_READ ((int)0x80000D00) /*unable to retrieve fw_vers or the config_id*/ +#define ERROR_GESTURE_ENABLE_FAIL ((int)0x80000E00) /*unable to enable/disable the gesture*/ +#define ERROR_GESTURE_START_ADD ((int)0x80000F00) /*unable to start to add custom gesture*/ +#define ERROR_GESTURE_FINISH_ADD ((int)0x80001000) /*unable to finish to add custom gesture*/ +#define ERROR_GESTURE_DATA_ADD ((int)0x80001100) /*unable to add custom gesture data*/ +#define ERROR_GESTURE_REMOVE ((int)0x80001200) /*unable to remove custom gesture data*/ +#define ERROR_FEATURE_ENABLE_DISABLE ((int)0x80001300) /*unable to enable/disable a feature mode in the IC*/ +/*THIRD LEVEL ERROR CODE*/ +#define ERROR_CH_LEN ((int)0x80010000) /*unable to retrieve the force and/or sense length*/ +#define ERROR_REQU_COMP_DATA ((int)0x80020000) /*compensation data request failed*/ +#define ERROR_COMP_DATA_HEADER ((int)0x80030000) /*unable to retrieve the compensation data header*/ +#define ERROR_COMP_DATA_GLOBAL ((int)0x80040000) /*unable to retrieve the global compensation data*/ +#define ERROR_COMP_DATA_NODE ((int)0x80050000) /*unable to retrieve the compensation data for each node*/ +#define ERROR_TEST_CHECK_FAIL ((int)0x80060000) /*check of production limits or of fw answers failed*/ +#define ERROR_MEMH_READ ((int)0x80070000) /*memh reading failed*/ +#define ERROR_FLASH_BURN_FAILED ((int)0x80080000) /*flash burn failed*/ +#define ERROR_MS_TUNING ((int)0x80090000) /*ms tuning failed*/ +#define ERROR_SS_TUNING ((int)0x800A0000) /*ss tuning failed*/ +#define ERROR_LP_TIMER_TUNING ((int)0x800B0000) /*lp timer calibration failed*/ +#define ERROR_SAVE_CX_TUNING ((int)0x800C0000) /*save cx data to flash failed*/ +#define ERROR_HANDLER_STOP_PROC ((int)0x800D0000) /*stop the poll of the FIFO if particular errors are found*/ +#define ERROR_CHECK_ECHO_FAIL ((int)0x800E0000) /*unable to retrieve echo event*/ + +/*FOURTH LEVEL ERROR CODE*/ +#define ERROR_PROD_TEST_DATA ((int)0x81000000) /*production data test failed*/ +#define ERROR_FLASH_PROCEDURE ((int)0x82000000) /*complete flash procedure failed*/ +#define ERROR_PROD_TEST_ITO ((int)0x83000000) /*production ito test failed*/ +#define ERROR_PROD_TEST_INITIALIZATION ((int)0x84000000) /*production initialization test failed*/ +#define ERROR_GET_INIT_STATUS ((int)0x85000000) /*mismatch of the MS or SS tuning_version*/ + +void logError(int force, const char *msg, ...); +int isI2cError(int error); +int errorHandler(u8 *event, int size); diff --git a/drivers/input/touchscreen/st/fts_lib/ftsFlash.c b/drivers/input/touchscreen/st/fts_lib/ftsFlash.c new file mode 100644 index 000000000000..59c73f4c4edb --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsFlash.c @@ -0,0 +1,1071 @@ +/* + + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS API for Flashing the IC * + * * + ************************************************************************** + ************************************************************************** + + */ + +#include "ftsCrossCompile.h" +#include "ftsCompensation.h" +#include "ftsError.h" +#include "ftsFlash.h" +#include "ftsFrame.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTest.h" +#include "ftsTime.h" +#include "ftsTool.h" +#include "../fts.h" /* needed for the FW_H_FILE define */ + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <stdarg.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/serio.h> +#include <linux/time.h> +#include <linux/pm.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/power_supply.h> +#include <linux/firmware.h> +#include <linux/regulator/consumer.h> +#include <linux/of_gpio.h> +/* #include <linux/sec_sysfs.h> */ + +#ifdef FW_H_FILE +#include <../fts_fw.h> +#endif + +/* static char tag[8] = "[ FTS ]\0"; */ +extern chipInfo ftsInfo; + +int getFirmwareVersion(u16 *fw_vers, u16 *config_id) +{ + u8 fwvers[DCHIP_FW_VER_BYTE]; + u8 confid[CONFIG_ID_BYTE]; + int res; + + res = readCmdU16(FTS_CMD_HW_REG_R, DCHIP_FW_VER_ADDR, fwvers, DCHIP_FW_VER_BYTE, DUMMY_HW_REG); + if (res < OK) { + logError(1, "%s getFirmwareVersion: unable to read fw_version ERROR %02X\n", tag, ERROR_FW_VER_READ); + return (res | ERROR_FW_VER_READ); + } + + u8ToU16(fwvers, fw_vers); /* fw version use big endian */ + if (*fw_vers != 0) { /* if fw_version is 00 00 means that there is no firmware running in the chip therefore will be impossible find the config_id */ + res = readB2(CONFIG_ID_ADDR, confid, CONFIG_ID_BYTE); + if (res < OK) { + logError(1, "%s getFirmwareVersion: unable to read config_id ERROR %02X\n", tag, ERROR_FW_VER_READ); + return (res | ERROR_FW_VER_READ); + } + u8ToU16(confid, config_id); /* config id use little endian */ + } else { + *config_id = 0x0000; + } + + logError(0, "%s FW VERS = %04X\n", tag, *fw_vers); + logError(0, "%s CONFIG ID = %04X\n", tag, *config_id); + return OK; + +} + +#ifdef FTM3_CHIP + +int flash_status(void) +{ + u8 cmd[2] = {FLASH_CMD_READSTATUS, 0x00}; + u8 readData; + + logError(0, "%s Reading flash_status...\n", tag); + if (fts_readCmd(cmd, 2, &readData, FLASH_STATUS_BYTES) < 0) { + logError(1, "%s flash_status: ERROR % 02X\n", tag, ERROR_I2C_R); + return ERROR_I2C_R; + } + + readData &= 0x01; + /* logError(0, "%s flash_status = %d\n", tag,readData); */ + return (int) readData; + +} + +int flash_status_ready(void) +{ + + int status = flash_status(); + + if (status == ERROR_I2C_R) { + logError(1, "%s flash_status_ready: ERROR % 02X\n", tag, ERROR_I2C_R); + return ERROR_I2C_R; + } + + if (status != FLASH_READY) { + logError(1, "%s flash_status_ready: flash busy or unknown STATUS = % 02X\n", tag, status); + return ERROR_FLASH_UNKNOWN; + } + + return FLASH_READY; + +} + +int wait_for_flash_ready(void) +{ + int status; + int(*code)(void); + + code = flash_status_ready; + + logError(0, "%s Waiting for flash ready...\n", tag); + status = attempt_function(code, FLASH_WAIT_BEFORE_RETRY, FLASH_RETRY_COUNT); + + if (status != FLASH_READY) { + logError(1, "%s wait_for_flash_ready: ERROR % 02X\n", tag, ERROR_FLASH_NOT_READY); + return (status | ERROR_FLASH_NOT_READY); + } + + logError(0, "%s Flash ready!\n", tag); + return OK; +} + +int flash_unlock(void) +{ + + int status; + u8 cmd[3] = {FLASH_CMD_UNLOCK, FLASH_UNLOCK_CODE0, FLASH_UNLOCK_CODE1}; /* write the comand to perform the unlock */ + + logError(0, "%s Try to unlock flash...\n", tag); + status = wait_for_flash_ready(); + + if (status != OK) { + logError(1, "%s flash_unlock: ERROR % 02X\n", tag, ERROR_FLASH_NOT_READY); + return (status | ERROR_FLASH_NOT_READY); /* Flash not ready within the choosen time, better exit! */ + } + + logError(0, "%s Command unlock ...\n", tag); + if (fts_writeCmd(cmd, sizeof (cmd)) < 0) { + logError(1, "%s flash_unlock: ERROR % 02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + + status = wait_for_flash_ready(); + + if (status != OK) { + logError(1, "%s flash_unlock: ERROR % 02X\n", tag, ERROR_FLASH_NOT_READY); + return (status | ERROR_FLASH_NOT_READY); /* Flash not ready within the choosen time, better exit! */ + } + + logError(0, "%s Unlock flash DONE!\n", tag); + + return OK; + +} + +/*int parseMemhFile(const char *pathToFile, u8** data, int* length, int dimension) +{ + + int i = 0; + unsigned long ul; + u8* buff = NULL; + int fd = -1; + int n, size, pointer = 0; + char *data_file, *line; + const struct firmware *fw = NULL; + struct device *dev = NULL; + + line = (char *) kmalloc(11 * sizeof (char), GFP_KERNEL); + if (line == NULL) { + logError(1, "%s parseMemhFile: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + logError(0, "%s parseMemhFile: allocating %d bytes\n", tag, dimension); + buff = (u8*) kmalloc(dimension * sizeof (u8), GFP_KERNEL); + if (buff == NULL) { + logError(1, "%s parseMemhFile: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + dev = getDev(); + if (dev != NULL) + fd = request_firmware(&fw, pathToFile, dev); + + if (fd == 0) { + size = fw->size; + logError(0, "%s The size of the firmware file is %d bytes...\n", tag, size); + data_file = (char *) fw->data; + logError(0, "%s Start to reading %s...\n", tag, pathToFile); + + while (size - pointer > 0 && (i * 4 + 4) <= dimension) { + if (readLine(&data_file[pointer], &line, size - pointer, &n) < 0) { + break; + } + pointer += n; + logError(0, "%s Pointer= %d riga = %s\n", tag, pointer, line); + ul = simple_strtoul(line, NULL, 16); + + buff[i * 4] = (u8) ((ul & 0x000000FF) >> 0); + buff[i * 4 + 1] = (u8) ((ul & 0x0000FF00) >> 8); + buff[i * 4 + 2] = (u8) ((ul & 0x00FF0000) >> 16); + buff[i * 4 + 3] = (u8) ((ul & 0xFF000000) >> 24); + i++; + } + + kfree(line); + + *length = i * 4; + if (*length < dimension) { + logError(1, "%s parseMemhFile: Read only %d instead of %d... ERROR %02X\n", tag, *length, dimension, ERROR_FILE_PARSE); + release_firmware(fw); + return ERROR_FILE_PARSE; + } + *data = buff; + + logError(0, "%s READ DONE %d bytes!\n", tag, *length); + release_firmware(fw); + return OK; + } else { + logError(1, "%s parseProductionTestLimits: ERROR %02X\n", tag, ERROR_FILE_NOT_FOUND); + return ERROR_FILE_NOT_FOUND; + } + +}*/ + +int parseBinFile(const char *pathToFile, u8 **data, int *length, int dimension) +{ + + int fd = -1; + int fw_size = 0; + u8 *fw_data = NULL; + +#ifndef FW_H_FILE + const struct firmware *fw = NULL; + struct device *dev = NULL; + dev = getDev(); + + if (dev != NULL) + fd = request_firmware(&fw, pathToFile, dev); + else { + logError(1, "%s parseBinFile: No device found! ERROR %02X\n", ERROR_FILE_PARSE); + return ERROR_FILE_PARSE; + } +#else + fd = 0; +#endif + + if (fd == 0) { +#ifndef FW_H_FILE + fw_size = fw->size; + fw_data = (u8 *) (fw->data); +#else + fw_size = FW_SIZE_NAME; + fw_data = (u8 *) FW_ARRAY_NAME; +#endif + if (fw_size - FW_HEADER_SIZE != FW_SIZE) { + logError(1, "%s parseBinFile: Read only %d instead of %d... ERROR %02X\n", tag, fw_size, FW_SIZE, ERROR_FILE_PARSE); +#ifndef FW_H_FILE + release_firmware(fw); +#endif + return ERROR_FILE_PARSE; + } + *data = (u8 *) kmalloc(dimension * sizeof (u8), GFP_KERNEL); + if (*data == NULL) { + logError(1, "%s parseBinFile: ERROR %02X\n", tag, ERROR_ALLOC); +#ifndef FW_H_FILE + release_firmware(fw); +#endif + return ERROR_ALLOC; + } + + memcpy(*data, ((u8 *) (fw_data) + FW_HEADER_SIZE), dimension); + *length = dimension; + + logError(0, "%s READ FW DONE %d bytes!\n", tag, *length); +#ifndef FW_H_FILE + release_firmware(fw); +#endif + return OK; + } + logError(1, "%s parseBinFile: File Not Found! ERROR %02X\n", tag, ERROR_FILE_NOT_FOUND); + return ERROR_FILE_NOT_FOUND; +} + +int readFwFile(const char *path, Firmware *fw, int keep_cx) +{ + int res; + int size; + + if (keep_cx) { + size = FW_SIZE - FW_CX_SIZE; + logError(1, "%s readFwFile: Selected 124k Configuration!\n", tag); + } else { + size = FW_SIZE; + logError(1, "%s readFwFile: Selected 128k Configuration!\n", tag); + } + + /* res = parseMemhFile(path, &(fw->data), &(fw->data_size), size); */ + res = parseBinFile(path, &(fw->data), &(fw->data_size), size); + if (res < OK) { + logError(1, "%s readFwFile: ERROR %02X\n", tag, ERROR_MEMH_READ); + return (res | ERROR_MEMH_READ); + } + + fw->fw_ver = (u16) (((fw->data[FW_VER_MEMH_BYTE1] & 0x00FF) << 8) + (fw->data[FW_VER_MEMH_BYTE0] & 0x00FF)); + fw->config_id = (u16) (((fw->data[(FW_CODE_SIZE) + FW_OFF_CONFID_MEMH_BYTE1] & 0x00FF) << 8) + (fw->data[(FW_CODE_SIZE) + FW_OFF_CONFID_MEMH_BYTE0] & 0x00FF)); + + logError(0, "%s FW VERS File = %04X\n", tag, fw->fw_ver); + logError(0, "%s CONFIG ID File = %04X\n", tag, fw->config_id); + return OK; + +} + +int fillMemory(u32 address, u8 *data, int size) +{ + + int remaining = size; + int toWrite = 0; + + u8 *buff = (u8 *) kmalloc((MEMORY_CHUNK + 3) * sizeof (u8), GFP_KERNEL); + if (buff == NULL) { + logError(1, "%s fillMemory: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + while (remaining > 0) { + if (remaining >= MEMORY_CHUNK) { + if ((address + MEMORY_CHUNK) < FLASH_ADDR_SWITCH_CMD) { + buff[0] = FLASH_CMD_WRITE_LOWER_64; + toWrite = MEMORY_CHUNK; + remaining -= MEMORY_CHUNK; + } else { + if (address < FLASH_ADDR_SWITCH_CMD) { + int delta = FLASH_ADDR_SWITCH_CMD - address; + buff[0] = FLASH_CMD_WRITE_LOWER_64; + toWrite = delta; + remaining -= delta; + } else { + buff[0] = FLASH_CMD_WRITE_UPPER_64; + toWrite = MEMORY_CHUNK; + remaining -= MEMORY_CHUNK; + } + } + } else { + if ((address + remaining) < FLASH_ADDR_SWITCH_CMD) { + buff[0] = FLASH_CMD_WRITE_LOWER_64; + toWrite = remaining; + remaining = 0; + } else { + if (address < FLASH_ADDR_SWITCH_CMD) { + int delta = FLASH_ADDR_SWITCH_CMD - address; + buff[0] = FLASH_CMD_WRITE_LOWER_64; + toWrite = delta; + remaining -= delta; + } else { + buff[0] = FLASH_CMD_WRITE_UPPER_64; + toWrite = remaining; + remaining = 0; + } + } + } + + buff[1] = (u8) ((address & 0x0000FF00) >> 8); + buff[2] = (u8) (address & 0x000000FF); + memcpy(buff + 3, data, toWrite); + logError(0, "%s Command = %02X , address = %02X %02X, bytes = %d\n", tag, buff[0], buff[1], buff[2], toWrite); + if (fts_writeCmd(buff, 3 + toWrite) < 0) { + logError(1, "%s fillMemory: ERROR %02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + + address += toWrite; + data += toWrite; + + } + return OK; +} + +int flash_burn(Firmware fw, int force_burn) +{ + u8 cmd; + int res; + + if (!force_burn && (ftsInfo.u16_fwVer >= fw.fw_ver) && (ftsInfo.u16_cfgId >= fw.config_id)) { + logError(1, "%s flash_burn: Firmware in the chip newer or equal to the one to burn! NO UPDATE ERROR %02X\n", tag, ERROR_FW_NO_UPDATE); + return (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED); + } + + /* programming procedure start */ + + logError(0, "%s Programming Procedure for flashing started:\n\n", tag); + + logError(0, "%s 1) SYSTEM RESET:\n", tag); + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s system reset FAILED!\n", tag); + if (res != (ERROR_SYSTEM_RESET_FAIL | ERROR_TIMEOUT)) /* if there is no firmware i will not get the controller ready event and there will be a timeout but i can keep going, but if there is an I2C error i have to exit */ + return (res | ERROR_FLASH_BURN_FAILED); + } else + logError(0, "%s system reset COMPLETED!\n\n", tag); + + logError(0, "%s 2) FLASH UNLOCK:\n", tag); + res = flash_unlock(); + if (res < 0) { + logError(1, "%s flash unlock FAILED! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s flash unlock COMPLETED!\n\n", tag); + + /* Write the lower part of the Program RAM */ + logError(0, "%s 3) PREPARING DATA FOR FLASH BURN:\n", tag); + + res = fillMemory(FLASH_ADDR_CODE, fw.data, fw.data_size); + if (res < 0) { + logError(1, "%s Error During filling the memory! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s Data copy COMPLETED!\n\n", tag); + + logError(0, "%s 4) ERASE FLASH:\n", tag); + res = wait_for_flash_ready(); + if (res < 0) { + logError(1, "%s Flash not ready! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + + logError(0, "%s Command erase ...\n", tag); + cmd = FLASH_CMD_ERASE; + if (fts_writeCmd(&cmd, 1) < 0) { + logError(1, "%s Error during erasing flash! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (ERROR_I2C_W | ERROR_FLASH_BURN_FAILED); + } + + res = wait_for_flash_ready(); + if (res < 0) { + logError(1, "%s Flash not ready 2! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + + logError(0, "%s Flash erase COMPLETED!\n\n", tag); + + logError(0, "%s 5) BURN FLASH:\n", tag); + logError(0, "%s Command burn ...\n", tag); + cmd = FLASH_CMD_BURN; + if (fts_writeCmd(&cmd, 1) < 0) { + logError(1, "%s Error during burning data! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (ERROR_I2C_W | ERROR_FLASH_BURN_FAILED); + } + + res = wait_for_flash_ready(); + if (res < 0) { + logError(1, "%s Flash not ready! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + + logError(0, "%s Flash burn COMPLETED!\n\n", tag); + + logError(0, "%s 6) SYSTEM RESET:\n", tag); + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s system reset FAILED! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s system reset COMPLETED!\n\n", tag); + + logError(0, "%s 7) FINAL CHECK:\n", tag); + res = readChipInfo(0); + if (res < 0) { + logError(1, "%s flash_burn: Unable to retrieve Chip INFO! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + + if ((ftsInfo.u16_fwVer != fw.fw_ver) && (ftsInfo.u16_cfgId != fw.config_id)) { + logError(1, "%s Firmware in the chip different from the one that was burn! fw: %x != %x , conf: %x != %x\n", tag, ftsInfo.u16_fwVer, fw.fw_ver, ftsInfo.u16_cfgId, fw.config_id); + return ERROR_FLASH_BURN_FAILED; + } + + logError(0, "%s Final check OK! fw: %02X , conf: %02X\n", tag, ftsInfo.u16_fwVer, ftsInfo.u16_cfgId); + + return OK; +} + +int flashProcedure(const char *path, int force, int keep_cx) +{ + Firmware fw; + int res; + + fw.data = NULL; + logError(0, "%s Reading Fw file...\n", tag); + res = readFwFile(path, &fw, keep_cx); + if (res < OK) { + logError(1, "%s flashProcedure: ERROR %02X\n", tag, (res | ERROR_FLASH_PROCEDURE)); + kfree(fw.data); + return (res | ERROR_FLASH_PROCEDURE); + } + logError(0, "%s Fw file read COMPLETED!\n", tag); + + logError(0, "%s Starting flashing procedure...\n", tag); + res = flash_burn(fw, force); + if (res < OK && res != (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED)) { + logError(1, "%s flashProcedure: ERROR %02X\n", tag, ERROR_FLASH_PROCEDURE); + kfree(fw.data); + return (res | ERROR_FLASH_PROCEDURE); + } + logError(0, "%s flashing procedure Finished!\n", tag); + kfree(fw.data); + + /* cleanUp(0); //after talking with Kusuma, the SenseOn should be issued only at the very end of the initialization process, if senso on here it can trigger autotune protection */ + return res; +} + +#else + +int wait_for_flash_ready(u8 type) +{ + u8 cmd[2] = {FLASH_CMD_READ_REGISTER, type}; + u8 readData; + int i, res = -1; + + logError(0, "%s Waiting for flash ready ...\n", tag); + for (i = 0; i < FLASH_RETRY_COUNT && res != 0; i++) { + if (fts_readCmd(cmd, sizeof (cmd), &readData, 1) < 0) { + logError(1, "%s wait_for_flash_ready: ERROR % 02X\n", tag, ERROR_I2C_W); + } else{ + res = readData & 0x80; + /* logError(0, "%s flash status = %d \n", tag, res); */ + } + msleep(FLASH_WAIT_BEFORE_RETRY); + } + + if (i == FLASH_RETRY_COUNT && res != 0) { + logError(1, "%s Wait for flash TIMEOUT! ERROR %02X\n", tag, ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } + + logError(0, "%s Flash READY!\n", tag); + return OK; +} + +int fts_warm_boot(void) +{ + + u8 cmd[4] = {FTS_CMD_HW_REG_W, 0x00, 0x00, WARM_BOOT_VALUE}; /* write the command to perform the warm boot */ + u16ToU8_be(ADDR_WARM_BOOT, &cmd[1]); + + logError(0, "%s Command warm boot ...\n", tag); + if (fts_writeCmd(cmd, sizeof (cmd)) < 0) { + logError(1, "%s flash_unlock: ERROR % 02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + + logError(0, "%s Warm boot DONE!\n", tag); + + return OK; +} + +int parseBinFile(const char *pathToFile, Firmware *fwData, int keep_cx) +{ + + int fd = -1; + int dimension, index = 0; + u32 temp; + u8 *data; + int res, i, fw_size; + +#ifndef FW_H_FILE + const struct firmware *fw = NULL; + struct device *dev = NULL; + dev = getDev(); + + if (dev != NULL) + fd = request_firmware(&fw, pathToFile, dev); + else { + logError(1, "%s parseBinFile: No device found! ERROR %02X\n", ERROR_FILE_PARSE); + return ERROR_FILE_PARSE; + } + + fw_size = fw->size; +#else +fd = 0; +fw_size = SIZE_NAME; +#endif + + if (fd == 0 && fw_size > 0) { /* the file should contain at least the header plus the content_crc */ + if (fw_size < FW_HEADER_SIZE+FW_BYTES_ALIGN) { + logError(1, "%s parseBinFile: Read only %d instead of %d... ERROR %02X\n", tag, fw_size, FW_HEADER_SIZE+FW_BYTES_ALIGN, ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } else { + /* start parsing of bytes */ +#ifndef FW_H_FILE + data = (u8 *) (fw->data); +#else + data = (u8 *) (ARRAY_NAME); +#endif + u8ToU32(&data[index], &temp); + if (temp != FW_HEADER_SIGNATURE) { + logError(1, "%s parseBinFile: Wrong Signature %08X ... ERROR %02X\n", tag, temp, ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } + logError(0, "%s parseBinFile: Fw Signature OK!\n", tag); + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + if (temp != FW_FTB_VER) { + logError(1, "%s parseBinFile: Wrong ftb_version %08X ... ERROR %02X\n", tag, temp, ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } + logError(0, "%s parseBinFile: ftb_version OK!\n", tag); + index += FW_BYTES_ALIGN; + if (data[index] != DCHIP_ID_0 || data[index+1] != DCHIP_ID_1) { + logError(1, "%s parseBinFile: Wrong target %02X != %02X %02X != %02X ... ERROR %08X\n", tag, data[index], DCHIP_ID_0, data[index+1], DCHIP_ID_1, ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + logError(1, "%s parseBinFile: Fw ID = %08X\n", tag, temp); + + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + fwData->fw_ver = temp; + logError(1, "%s parseBinFile: FILE Fw Version = %04X\n", tag, fwData->fw_ver); + + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + fwData->config_id = temp; + logError(1, "%s parseBinFile: FILE Config ID = %08X\n", tag, temp); + + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + logError(1, "%s parseBinFile: Config Version = %08X\n", tag, temp); + + index += FW_BYTES_ALIGN*2; /* skip reserved data */ + + index += FW_BYTES_ALIGN; + logError(1, "%s parseBinFile: File External Release = ", tag); + for (i = 0; i < EXTERNAL_RELEASE_INFO_SIZE; i++) { + fwData->externalRelease[i] = data[index++]; + logError(1, "%02X ", fwData->externalRelease[i]); + } + logError(1, "\n"); + + /* index += FW_BYTES_ALIGN; */ + u8ToU32(&data[index], &temp); + fwData->sec0_size = temp; + logError(1, "%s parseBinFile: sec0_size = %08X (%d bytes)\n", tag, fwData->sec0_size, fwData->sec0_size); + + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + fwData->sec1_size = temp; + logError(1, "%s parseBinFile: sec1_size = %08X (%d bytes)\n", tag, fwData->sec1_size, fwData->sec1_size); + + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + fwData->sec2_size = temp; + logError(1, "%s parseBinFile: sec2_size = %08X (%d bytes)\n", tag, fwData->sec2_size, fwData->sec2_size); + + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + fwData->sec3_size = temp; + logError(1, "%s parseBinFile: sec3_size = %08X (%d bytes)\n", tag, fwData->sec3_size, fwData->sec3_size); + + index += FW_BYTES_ALIGN; /* skip header crc */ + + if (!keep_cx) { + dimension = fwData->sec0_size + fwData->sec1_size + fwData->sec2_size + fwData->sec3_size; + temp = fw_size; + } else { + dimension = fwData->sec0_size + fwData->sec1_size; /* sec2 may contain cx data (future implementation) sec3 atm not used */ + temp = fw_size - fwData->sec2_size - fwData->sec3_size; + } + + if (dimension+FW_HEADER_SIZE+FW_BYTES_ALIGN != temp) { + logError(1, "%s parseBinFile: Read only %d instead of %d... ERROR %02X\n", tag, fw_size, dimension+FW_HEADER_SIZE+FW_BYTES_ALIGN, ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } + + fwData->data = (u8 *) kmalloc(dimension * sizeof (u8), GFP_KERNEL); + if (fwData->data == NULL) { + logError(1, "%s parseBinFile: ERROR %02X\n", tag, ERROR_ALLOC); + res = ERROR_ALLOC; + goto END; + } + + index += FW_BYTES_ALIGN; + memcpy(fwData->data, &data[index], dimension); + fwData->data_size = dimension; + + logError(0, "%s READ FW DONE %d bytes!\n", tag, fwData->data_size); + res = OK; + goto END; + } + } else { + logError(1, "%s parseBinFile: File Not Found! ERROR %02X\n", tag, ERROR_FILE_NOT_FOUND); + return ERROR_FILE_NOT_FOUND; + } + +END: +#ifndef FW_H_FILE + release_firmware(fw); +#endif + return res; +} + +int readFwFile(const char *path, Firmware *fw, int keep_cx) +{ + int res; + + res = parseBinFile(path, fw, keep_cx); + if (res < OK) { + logError(1, "%s readFwFile: ERROR %02X\n", tag, ERROR_MEMH_READ); + return (res | ERROR_MEMH_READ); + } + + return OK; + +} + +int flash_unlock(void) +{ + u8 cmd[3] = {FLASH_CMD_UNLOCK, FLASH_UNLOCK_CODE0, FLASH_UNLOCK_CODE1}; /* write the command to perform the unlock */ + + logError(0, "%s Command unlock ...\n", tag); + if (fts_writeCmd(cmd, sizeof (cmd)) < 0) { + logError(1, "%s flash_unlock: ERROR % 02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + + /* msleep(FLASH_WAIT_TIME); */ + logError(0, "%s Unlock flash DONE!\n", tag); + + return OK; + +} + +int flash_erase_unlock(void) +{ + u8 cmd[3] = {FLASH_CMD_WRITE_REGISTER, FLASH_ERASE_UNLOCK_CODE0, FLASH_ERASE_UNLOCK_CODE1}; /* write the command to perform the unlock for erasing the flash */ + + logError(0, "%s Try to erase unlock flash...\n", tag); + + logError(0, "%s Command erase unlock ...\n", tag); + if (fts_writeCmd(cmd, sizeof (cmd)) < 0) { + logError(1, "%s flash_erase_unlock: ERROR % 02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + + logError(0, "%s Erase Unlock flash DONE!\n", tag); + + return OK; + +} + +int flash_full_erase(void) +{ + + int status; + u8 cmd[3] = {FLASH_CMD_WRITE_REGISTER, FLASH_ERASE_CODE0, FLASH_ERASE_CODE1}; + /* write the command to erase the flash */ + + logError(0, "%s Command full erase sent ...\n", tag); + if (fts_writeCmd(cmd, sizeof (cmd)) < 0) { + logError(1, "%s flash_full_erase: ERROR % 02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + + status = wait_for_flash_ready(FLASH_ERASE_CODE0); + + if (status != OK) { + logError(1, "%s flash_full_erase: ERROR % 02X\n", tag, ERROR_FLASH_NOT_READY); + return (status | ERROR_FLASH_NOT_READY); + /* Flash not ready within the chosen time, better exit! */ + } + + logError(0, "%s Full Erase flash DONE!\n", tag); + + return OK; + +} + +int start_flash_dma(void) +{ + int status; + u8 cmd[3] = {FLASH_CMD_WRITE_REGISTER, FLASH_DMA_CODE0, FLASH_DMA_CODE1}; + /* write the command to erase the flash */ + + logError(0, "%s Command flash DMA ...\n", tag); + if (fts_writeCmd(cmd, sizeof (cmd)) < 0) { + logError(1, "%s start_flash_dma: ERROR % 02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + + status = wait_for_flash_ready(FLASH_DMA_CODE0); + + if (status != OK) { + logError(1, "%s start_flash_dma: ERROR % 02X\n", tag, ERROR_FLASH_NOT_READY); + return (status | ERROR_FLASH_NOT_READY); + /* Flash not ready within the chosen time, better exit! */ + } + + logError(0, "%s flash DMA DONE!\n", tag); + + return OK; +} + +int fillFlash(u32 address, u8 *data, int size) +{ + + int remaining = size; + int toWrite = 0; + int byteBlock = 0; + int wheel = 0; + u32 addr = 0; + int res; + int delta; + + u8 *buff = (u8 *) kmalloc((DMA_CHUNK + 3) * sizeof (u8), GFP_KERNEL); + if (buff == NULL) { + logError(1, "%s fillFlash: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + while (remaining > 0) { + byteBlock = 0; + addr = 0; + while (byteBlock < FLASH_CHUNK && remaining > 0) { + buff[0] = FLASH_CMD_WRITE_64K; + if (remaining >= DMA_CHUNK) { + if ((byteBlock + DMA_CHUNK) <= FLASH_CHUNK) { + /* logError(1, "%s fillFlash: 1\n", tag); */ + toWrite = DMA_CHUNK; + remaining -= DMA_CHUNK; + byteBlock += DMA_CHUNK; + } else { + /* logError(1, "%s fillFlash: 2\n", tag); */ + delta = FLASH_CHUNK - byteBlock; + toWrite = delta; + remaining -= delta; + byteBlock += delta; + } + } else { + if ((byteBlock + remaining) <= FLASH_CHUNK) { + /* logError(1, "%s fillFlash: 3\n", tag); */ + toWrite = remaining; + byteBlock += remaining; + remaining = 0; + + } else { + /* logError(1, "%s fillFlash: 4\n", tag); */ + delta = FLASH_CHUNK - byteBlock; + toWrite = delta; + remaining -= delta; + byteBlock += delta; + } + } + + buff[1] = (u8) ((addr & 0x0000FF00) >> 8); + buff[2] = (u8) (addr & 0x000000FF); + memcpy(&buff[3], data, toWrite); + if (fts_writeCmd(buff, 3 + toWrite) < 0) { + logError(1, "%s fillFlash: ERROR %02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + + addr += toWrite; + data += toWrite; + } + + kfree(buff); + + /* configuring the DMA */ + byteBlock = byteBlock / 4 - 1; + + buff = (u8 *) kmalloc((9) * sizeof (u8), GFP_KERNEL); + buff[0] = FLASH_CMD_WRITE_REGISTER; + buff[1] = FLASH_DMA_CONFIG; + buff[2] = 0x00; + buff[3] = 0x00; + + addr = address + ((wheel * FLASH_CHUNK)/4); + buff[4] = (u8) ((addr & 0x000000FF)); + buff[5] = (u8) ((addr & 0x0000FF00) >> 8); + buff[6] = (u8) (byteBlock & 0x000000FF); + buff[7] = (u8) ((byteBlock & 0x0000FF00) >> 8); + buff[8] = 0x00; + + logError(0, "%s Command = %02X , address = %02X %02X, words = %02X %02X\n", tag, buff[0], buff[5], buff[4], buff[7], buff[6]); + if (fts_writeCmd(buff, 9) < OK) { + logError(1, "%s Error during filling Flash! ERROR %02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + + /* msleep(FLASH_WAIT_TIME); */ + res = start_flash_dma(); + if (res < OK) { + logError(1, "%s Error during flashing DMA! ERROR %02X\n", tag, res); + return res; + } + wheel++; + } + return OK; +} + +int flash_burn(Firmware fw, int force_burn) +{ + int res; + + if (!force_burn) { + for (res = EXTERNAL_RELEASE_INFO_SIZE-1; res >= 0; res--) { + if (fw.externalRelease[res] > ftsInfo.u8_extReleaseInfo[res]) + goto start; + } + logError(1, "%s flash_burn: Firmware in the chip newer or equal to the one to burn! NO UPDATE ERROR %02X\n", tag, ERROR_FW_NO_UPDATE); + return (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED); + } + + /* programming procedure start */ +start: + logError(0, "%s Programming Procedure for flashing started:\n\n", tag); + + logError(0, "%s 1) SYSTEM RESET:\n", tag); + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s system reset FAILED!\n", tag); + if (res != (ERROR_SYSTEM_RESET_FAIL | ERROR_TIMEOUT)) + /* if there is no firmware i will not get the controller + *ready event and there will be a timeout but i can keep going, + *but if there is an I2C error i have to exit + */ + return (res | ERROR_FLASH_BURN_FAILED); + } else + logError(0, "%s system reset COMPLETED!\n\n", tag); + + logError(0, "%s 2) WARM BOOT:\n", tag); + res = fts_warm_boot(); + if (res < OK) { + logError(1, "%s warm boot FAILED!\n", tag); + return (res | ERROR_FLASH_BURN_FAILED); + } else + logError(0, "%s warm boot COMPLETED!\n\n", tag); + + /* msleep(FLASH_WAIT_TIME); */ + logError(0, "%s 3) FLASH UNLOCK:\n", tag); + res = flash_unlock(); + if (res < OK) { + logError(1, "%s flash unlock FAILED! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s flash unlock COMPLETED!\n\n", tag); + + /* msleep(200); */ + logError(0, "%s 4) FLASH ERASE UNLOCK:\n", tag); + res = flash_erase_unlock(); + if (res < 0) { + logError(1, "%s flash unlock FAILED! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s flash unlock COMPLETED!\n\n", tag); + + /* msleep(FLASH_WAIT_TIME); */ + logError(0, "%s 5) FLASH ERASE:\n", tag); + res = flash_full_erase(); + if (res < 0) { + logError(1, "%s flash erase FAILED! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s flash erase COMPLETED!\n\n", tag); + + /* msleep(FLASH_WAIT_TIME); */ + logError(0, "%s 6) LOAD PROGRAM:\n", tag); + res = fillFlash(FLASH_ADDR_CODE, &fw.data[0], fw.sec0_size); + if (res < OK) { + logError(1, "%s load program ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(1, "%s load program DONE!\n", tag); + + logError(0, "%s 7) LOAD CONFIG:\n", tag); + res = fillFlash(FLASH_ADDR_CONFIG, &(fw.data[fw.sec0_size]), fw.sec1_size); + if (res < OK) { + logError(1, "%s load config ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(1, "%s load config DONE!\n", tag); + + logError(0, "%s Flash burn COMPLETED!\n\n", tag); + + logError(0, "%s 8) SYSTEM RESET:\n", tag); + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s system reset FAILED! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s system reset COMPLETED!\n\n", tag); + + logError(0, "%s 9) FINAL CHECK:\n", tag); + res = readChipInfo(0); + if (res < 0) { + logError(1, "%s flash_burn: Unable to retrieve Chip INFO! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + + for (res = 0; res < EXTERNAL_RELEASE_INFO_SIZE; res++) { + if (fw.externalRelease[res] != ftsInfo.u8_extReleaseInfo[res]) { + /* external release is prined during readChipInfo */ + logError(1, "%s Firmware in the chip different from the one that was burn! fw: %x != %x , conf: %x != %x\n", tag, ftsInfo.u16_fwVer, fw.fw_ver, ftsInfo.u16_cfgId, fw.config_id); + return ERROR_FLASH_BURN_FAILED; + } + } + + logError(0, "%s Final check OK! fw: %02X, conf: %02X\n", tag, ftsInfo.u16_fwVer, ftsInfo.u16_cfgId); + + return OK; +} + +int flashProcedure(const char *path, int force, int keep_cx) +{ + Firmware fw; + int res; + + fw.data = NULL; + logError(0, "%s Reading Fw file...\n", tag); + res = readFwFile(path, &fw, keep_cx); + if (res < OK) { + logError(1, "%s flashProcedure: ERROR %02X\n", tag, (res | ERROR_FLASH_PROCEDURE)); + kfree(fw.data); + return (res | ERROR_FLASH_PROCEDURE); + } + logError(0, "%s Fw file read COMPLETED!\n", tag); + + logError(0, "%s Starting flashing procedure...\n", tag); + res = flash_burn(fw, force); + if (res < OK && res != (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED)) { + logError(1, "%s flashProcedure: ERROR %02X\n", tag, ERROR_FLASH_PROCEDURE); + kfree(fw.data); + return (res | ERROR_FLASH_PROCEDURE); + } + logError(0, "%s flashing procedure Finished!\n", tag); + kfree(fw.data); + return res; +} + +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/ftsFlash.h b/drivers/input/touchscreen/st/fts_lib/ftsFlash.h new file mode 100644 index 000000000000..69635e07a9f4 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsFlash.h @@ -0,0 +1,79 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FTS API for Flashing the IC * +* * +************************************************************************** +************************************************************************** + +*/ + +#include "ftsSoftware.h" + +/* Flash possible status */ +#define FLASH_READY 0 +#define FLASH_BUSY 1 +#define FLASH_UNKNOWN -1 + +#define FLASH_STATUS_BYTES 1 + +/* Flash timing parameters */ +#define FLASH_RETRY_COUNT 1000 +#define FLASH_WAIT_BEFORE_RETRY 50 /* ms */ + +#define FLASH_WAIT_TIME 200 /* ms */ + +/* PATHS FW FILES */ +/* #define PATH_FILE_FW "fw.memh" */ +#ifdef FTM3_CHIP +#define PATH_FILE_FW "st_fts.bin" +#else +#define PATH_FILE_FW "st_fts.ftb" /* new bin file structure */ +#endif + +#ifndef FTM3_CHIP +#define FLASH_CHUNK (64*1024) +#define DMA_CHUNK 32 +#endif + +typedef struct { + u8 *data; + u16 fw_ver; + u16 config_id; + u8 externalRelease[EXTERNAL_RELEASE_INFO_SIZE]; + int data_size; +#ifndef FTM3_CHIP + u32 sec0_size; + u32 sec1_size; + u32 sec2_size; + u32 sec3_size; +#endif +} Firmware; + +#ifdef FTM3_CHIP +int flash_status(void); +int flash_status_ready(void); +int wait_for_flash_ready(void); +int parseBinFile(const char *pathToFile, u8 **data, int *length, int dimension); +/* int parseMemhFile(const char* pathToFile, u8** data, int* length, int dimension); */ +#else +int wait_for_flash_ready(u8 type); +int fts_warm_boot(void); +int parseBinFile(const char *pathToFile, Firmware *fw, int keep_cx); +int flash_erase_unlock(void); +int flash_full_erase(void); +int start_flash_dma(void); +int fillFlash(u32 address, u8 *data, int size); +#endif + +int flash_unlock(void); +int fillMemory(u32 address, u8 *data, int size); +int getFirmwareVersion(u16 *fw_vers, u16 *config_id); +int readFwFile(const char *path, Firmware *fw, int keep_cx); +int flash_burn(Firmware fw, int force_burn); +int flashProcedure(const char *path, int force, int keep_cx); diff --git a/drivers/input/touchscreen/st/fts_lib/ftsFrame.c b/drivers/input/touchscreen/st/fts_lib/ftsFrame.c new file mode 100644 index 000000000000..c502559319a0 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsFrame.c @@ -0,0 +1,569 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FTS functions for getting frames * +* * +************************************************************************** +************************************************************************** + +*/ + +#include "ftsCrossCompile.h" +#include "ftsCompensation.h" +#include "ftsError.h" +#include "ftsFrame.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTool.h" +#include "ftsTime.h" + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <stdarg.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/serio.h> +#include <linux/time.h> +#include <linux/pm.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/power_supply.h> +#include <linux/firmware.h> +#include <linux/regulator/consumer.h> +#include <linux/of_gpio.h> +/* #include <linux/sec_sysfs.h> */ + +static char tag[8] = "[ FTS ]\0"; +static int sense_len, force_len; + +/*int getOffsetFrame(u16 address, u16 *offset) +{ + + u8 data[2]; + u8 cmd = { FTS_CMD_FRAMEBUFFER_R }; + + if (readCmdU16(cmd, address, data, OFFSET_LENGTH, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s getOffsetFrame: ERROR %02X\n", tag, ERROR_I2C_R); + return ERROR_I2C_R; + } + else { + u8ToU16(data, offset); + logError(0, "%s %s", tag, printHex("Offest = ", data, OFFSET_LENGTH)); + return OK; + } + +}*/ + +int getChannelsLength(void) +{ + + int ret; + u8 *data = (u8 *)kmalloc(2*sizeof(u8), GFP_KERNEL); + + if (data == NULL) { + logError(1, "%s getChannelsLength: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + ret = readB2(ADDR_SENSE_LEN, data, 2); + if (ret < OK) { + logError(1, "%s getChannelsLength: ERROR %02X\n", tag, ERROR_READ_B2); + return (ret|ERROR_READ_B2); + } + + sense_len = (int)data[0]; + force_len = (int)data[1]; + + logError(0, "%s Force_len = %d Sense_Len = %d\n", tag, force_len, sense_len); + + kfree(data); + + return OK; +} + +int getFrameData(u16 address, int size, short **frame) +{ + int i, j, ret; + u8 *data = (u8 *)kmalloc(size*sizeof(u8), GFP_KERNEL); + if (data == NULL) { + logError(1, "%s getFrameData: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + ret = readCmdU16(FTS_CMD_FRAMEBUFFER_R, address, data, size, DUMMY_FRAMEBUFFER); + if (ret < OK) { + logError(1, "%s getFrameData: ERROR %02X\n", tag, ERROR_I2C_R); + kfree(data); + return ERROR_I2C_R; + } + j = 0; + for (i = 0; i < size; i += 2) { + (*frame)[j] = (short)((data[i + 1] << 8) + data[i]); + j++; + } + kfree(data); + return OK; +} + +/*int getMSFrame(u16 type, short **frame, int keep_first_row) +{ + u16 offset; + int size, ret; + + if (getSenseLen() == 0 || getForceLen() == 0) { + ret=getChannelsLength(); + if (ret<OK) { + logError(1, "%s getMSFrame: ERROR %02X\n", tag,ERROR_CH_LEN); + return (ret|ERROR_CH_LEN); + } + } + + ret = getOffsetFrame(type, &offset); + if (ret<OK) { + logError(1, "%s getMSFrame: ERROR %02X\n", tag, ERROR_GET_OFFSET); + return (ret | ERROR_GET_OFFSET); + } + + switch (type) { + case ADDR_RAW_TOUCH: + case ADDR_FILTER_TOUCH: + case ADDR_NORM_TOUCH: + case ADDR_CALIB_TOUCH: + if (keep_first_row ==1) + size = ((force_len+1)*sense_len); + else { + size = ((force_len)*sense_len); + offset+= (sense_len * BYTES_PER_NODE); + } + break; + + default: + logError(1, "%s getMSFrame: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *frame = (short*)kmalloc(size*sizeof(short), GFP_KERNEL); + if (*frame==NULL) { + logError(1, "%s getMSFrame: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + ret=getFrameData(offset, size*BYTES_PER_NODE, frame); + if (ret<OK) { + logError(1, "%s getMSFrame: ERROR %02X\n", tag, ERROR_GET_FRAME_DATA); + return (ret|ERROR_GET_FRAME_DATA); + } + + logError(0, "%s Frame acquired!\n", tag); + return size; + +} + +int getMSKeyFrame(u16 type, short **frame) { + u16 offset; + int size, ret; + u16 address; + MutualSenseData data; + + if (type != ADDR_RAW_MS_KEY) { + logError(1, "%s getMSKeyFrame: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = getOffsetFrame(type, &offset); + if (ret<OK) { + logError(1, "%s getMSKeyFrame: ERROR %02X\n", tag, ERROR_GET_OFFSET); + return (ret | ERROR_GET_OFFSET); + } + + ret = requestCompensationData(MS_KEY); + if (ret < OK) { + logError(1, "%s getMSKeyFrame: readMutualSenseCompensationData ERROR %02X\n", tag, ERROR_REQU_COMP_DATA); + return (ret | ERROR_REQU_COMP_DATA); + } + + ret = readCompensationDataHeader(MS_KEY, &(data.header), &address); + if (ret < OK) { + logError(1, "%s getMSKeyFrame: readMutualSenseCompensationData ERROR %02X\n", tag, ERROR_COMP_DATA_HEADER); + return (ret | ERROR_COMP_DATA_HEADER); + } + + if (data.header.force_node>data.header.sense_node) + size = data.header.force_node; + else + size = data.header.sense_node; + + *frame = (short*)kmalloc(size*sizeof(short), GFP_KERNEL); + if (frame == NULL) { + logError(1, "%s getMSKeyFrame: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + ret = getFrameData(offset, size*BYTES_PER_NODE, frame); + if (ret<OK) { + logError(1, "%s getMSKeyFrame: ERROR %02X\n", tag, ERROR_GET_FRAME_DATA); + return (ret | ERROR_GET_FRAME_DATA); + } + + logError(0, "%s Frame acquired!\n", tag); +} + +int getSSFrame(u16 type, short **frame) { + u16 offset; + int size, ret; + + if (getSenseLen() == 0 || getForceLen() == 0) { + ret = getChannelsLength(); + if (ret<0) { + logError(1, "%s getSSFrame: ERROR %02X\n", tag, ERROR_CH_LEN); + return (ret | ERROR_CH_LEN); + } + } + + switch (type) { + case ADDR_RAW_HOVER_FORCE: + case ADDR_FILTER_HOVER_FORCE: + case ADDR_NORM_HOVER_FORCE: + case ADDR_CALIB_HOVER_FORCE: + case ADDR_RAW_PRX_FORCE: + case ADDR_FILTER_PRX_FORCE: + case ADDR_NORM_PRX_FORCE: + case ADDR_CALIB_PRX_FORCE: + size = ((force_len)* 1); + break; + + case ADDR_RAW_HOVER_SENSE: + case ADDR_FILTER_HOVER_SENSE: + case ADDR_NORM_HOVER_SENSE: + case ADDR_CALIB_HOVER_SENSE: + case ADDR_RAW_PRX_SENSE: + case ADDR_FILTER_PRX_SENSE: + case ADDR_NORM_PRX_SENSE: + case ADDR_CALIB_PRX_SENSE: + size = ((1)*sense_len); + break; + + default: + logError(1, "%s getSSFrame: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + + } + + ret = getOffsetFrame(type, &offset); + if (ret<OK) { + logError(1, "%s getSSFrame: ERROR %02X\n", tag, ERROR_GET_OFFSET); + return (ret | ERROR_GET_OFFSET); + } + + *frame = (short*)kmalloc(size*sizeof(short), GFP_KERNEL); + if (*frame == NULL) { + logError(1, "%s getSSFrame: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + ret = getFrameData(offset, size*BYTES_PER_NODE, frame); + if (ret<OK) { + logError(1, "%s getSSFrame: ERROR %02X\n", tag, ERROR_GET_FRAME_DATA); + return (ret | ERROR_GET_FRAME_DATA); + } + + logError(0, "%s Frame acquired!\n", tag); + return size; + +} + +int getNmsFrame(u16 type, short ***frames, int *size, int keep_first_row, int fs, int n) { + int i; + StopWatch global, local; + int temp; + + *frames = (short **)kmalloc(n*sizeof(short *), GFP_KERNEL); + + if (*frames == NULL) { + logError(1, "%s getNmsFrame: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + fs = (1*1000 / fs) ; + + startStopWatch(&global); + for (i = 0; i < n; i++) { + + startStopWatch(&local); + + *size = getMSFrame(type, ((*frames)+i), keep_first_row); + if (*size < OK) { + logError(1, "%s getNFrame: getFrame failed\n",tag); + return *size; + } + + stopStopWatch(&local); + temp = elapsedMillisecond(&local); + logError(0, "%s Iteration %d performed in %d ms... the process wait for %ld ms\n\n", tag, i, temp, (unsigned long)(fs - temp)); + + if (temp < fs) + msleep((unsigned long)(fs - temp)); + + } + + stopStopWatch(&global); + temp = elapsedMillisecond(&global); + logError(0, "%s Global Iteration performed in %d ms\n", tag, temp); + temp /= n; + logError(0, "%s Mean Iteration performed in %d ms\n", tag, temp); + return (1000 / (temp)); + +}*/ + +int getSenseLen(void) +{ + int ret; + if (sense_len != 0) + return sense_len; + ret = getChannelsLength(); + if (ret < OK) + return ret; + else + return sense_len; +} + +int getForceLen(void) +{ + int ret; + if (force_len != 0) + return force_len; + ret = getChannelsLength(); + if (ret < OK) + return ret; + else + return force_len; +} + +int requestFrame(u16 type) +{ + int retry = 0; + int ret; + u16 answer; + + int event_to_search[1]; + u8 readEvent[FIFO_EVENT_SIZE]; + + u8 cmd[3] = { FTS_CMD_REQU_FRAME_DATA, 0x00, 0x00 }; + /* B7 is the command for asking frame data */ + event_to_search[0] = (int)EVENTID_FRAME_DATA_READ; + + u16ToU8(type, &cmd[1]); + + while (retry < FRAME_DATA_READ_RETRY) { + logError(0, "%s %s", tag, printHex("Command = ", cmd, 3)); + ret = fts_writeFwCmd(cmd, 3); + /* send the request to the chip to load in memory the Frame Data */ + if (ret < OK) { + logError(1, "%s requestFrame: ERROR %02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + ret = pollForEvent(event_to_search, 1, readEvent, TIMEOUT_REQU_COMP_DATA); + if (ret < OK) { + logError(0, "%s Event did not Found at %d attemp!\n", tag, retry + 1); + retry += 1; + } else { + retry = 0; + break; + } + } + + if (retry == FRAME_DATA_READ_RETRY) { + logError(1, "%s requestFrame: ERROR %02X\n", tag, ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } + + u8ToU16_le(&readEvent[1], &answer); + + if (answer == type) + return OK; + logError(1, "%s The event found has a different type of Frame data ERROR %02X\n", tag, ERROR_DIFF_COMP_TYPE); + return ERROR_DIFF_COMP_TYPE; + +} + +int readFrameDataHeader(u16 type, DataHeader *header) +{ + + u16 offset = ADDR_FRAMEBUFFER_DATA; + u16 answer; + u8 data[FRAME_DATA_HEADER]; + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, offset, data, FRAME_DATA_HEADER, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s readFrameDataHeader: ERROR %02X\n", tag, ERROR_I2C_R); + return ERROR_I2C_R; + } + logError(0, "%s Read Data Header done!\n", tag); + + if (data[0] != FRAME_HEADER_SIGNATURE) { + logError(1, "%s readFrameDataHeader: ERROR %02X The Header Signature was wrong! %02X != %02X\n", tag, ERROR_WRONG_COMP_SIGN, data[0], HEADER_SIGNATURE); + return ERROR_WRONG_COMP_SIGN; + } + + u8ToU16_le(&data[1], &answer); + + if (answer != type) { + logError(1, "%s readFrameDataHeader: ERROR %02X\n", tag, ERROR_DIFF_COMP_TYPE); + return ERROR_DIFF_COMP_TYPE; + } + + logError(0, "%s Type of Frame data OK!\n", tag); + + header->type = type; + header->force_node = (int)data[4]; + header->sense_node = (int)data[5]; + + return OK; + +} + +int getMSFrame2(u16 type, MutualSenseFrame *frame) +{ + u16 offset = ADDR_FRAMEBUFFER_DATA+FRAME_DATA_HEADER; + int size, ret; + + if (!(type == MS_TOUCH_ACTIVE || type == MS_TOUCH_LOW_POWER || type == MS_TOUCH_ULTRA_LOW_POWER || type == MS_KEY)) { + logError(1, "%s getMSFrame: Choose a MS type of frame data ERROR %02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = requestFrame(type); + if (ret < 0) { + logError(1, "%s readMutualSenseCompensationData: ERROR %02X\n", tag, ERROR_REQU_COMP_DATA); + return (ret | ERROR_REQU_COMP_DATA); + } + + ret = readFrameDataHeader(type, &(frame->header)); + if (ret < 0) { + logError(1, "%s readMutualSenseCompensationData: ERROR %02X\n", tag, ERROR_COMP_DATA_HEADER); + return (ret | ERROR_COMP_DATA_HEADER); + } + + switch (type) { + case MS_TOUCH_ACTIVE: + case MS_TOUCH_LOW_POWER: + case MS_TOUCH_ULTRA_LOW_POWER: + size = frame->header.force_node*frame->header.sense_node; + break; + case MS_KEY: + if (frame->header.force_node > frame->header.sense_node) + /* or use directly the number in the ftsChip */ + size = frame->header.force_node; + else + size = frame->header.sense_node; + frame->header.force_node = 1; + frame->header.sense_node = size; + break; + + default: + logError(1, "%s getMSFrame: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + frame->node_data = (short *)kmalloc(size*sizeof(short), GFP_KERNEL); + if (frame->node_data == NULL) { + logError(1, "%s getMSFrame: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + ret = getFrameData(offset, size*BYTES_PER_NODE, &(frame->node_data)); + if (ret < OK) { + logError(1, "%s getMSFrame: ERROR %02X\n", tag, ERROR_GET_FRAME_DATA); + return (ret | ERROR_GET_FRAME_DATA); + } + /* if you want to access one node i,j, you should compute the offset like: offset = i*columns + j = > frame[i, j] */ + + logError(0, "%s Frame acquired!\n", tag); + frame->node_data_size = size; + return size; /* return the number of data put inside frame */ + +} + +int getSSFrame2(u16 type, SelfSenseFrame *frame) +{ + u16 offset = ADDR_FRAMEBUFFER_DATA + FRAME_DATA_HEADER; + int size, ret; + short *temp = NULL; + + if (!(type == SS_TOUCH || type == SS_KEY || type == SS_HOVER || type == SS_PROXIMITY)) { + logError(1, "%s getSSFrame: Choose a SS type of frame data ERROR %02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = requestFrame(type); + if (ret < 0) { + logError(1, "%s getSSFrame: ERROR %02X\n", tag, ERROR_REQU_COMP_DATA); + return (ret | ERROR_REQU_COMP_DATA); + } + + ret = readFrameDataHeader(type, &(frame->header)); + if (ret < 0) { + logError(1, "%s getSSFrame: ERROR %02X\n", tag, ERROR_COMP_DATA_HEADER); + return (ret | ERROR_COMP_DATA_HEADER); + } + + switch (type) { + case SS_TOUCH: + case SS_HOVER: + case SS_PROXIMITY: + size = frame->header.force_node+frame->header.sense_node; + break; + + default: + logError(1, "%s getSSFrame: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + temp = (short *)kmalloc(size*sizeof(short), GFP_KERNEL); + if (temp == NULL) { + logError(1, "%s getSSFrame: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + ret = getFrameData(offset, size*BYTES_PER_NODE, &temp); + if (ret < OK) { + logError(1, "%s getMSFrame: ERROR %02X\n", tag, ERROR_GET_FRAME_DATA); + return (ret | ERROR_GET_FRAME_DATA); + } + + frame->force_data = (short *)kmalloc(frame->header.force_node*sizeof(short), GFP_KERNEL); + if (frame->force_data == NULL) { + logError(1, "%s getSSFrame: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + memcpy(frame->force_data, temp, frame->header.force_node*sizeof(short)); + + frame->sense_data = (short *)kmalloc(frame->header.sense_node*sizeof(short), GFP_KERNEL); + if (frame->sense_data == NULL) { + logError(1, "%s getSSFrame: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + memcpy(frame->sense_data, &temp[frame->header.force_node], frame->header.sense_node*sizeof(short)); + + logError(0, "%s Frame acquired!\n", tag); + kfree(temp); + return size; /* return the number of data put inside frame */ + +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsFrame.h b/drivers/input/touchscreen/st/fts_lib/ftsFrame.h new file mode 100644 index 000000000000..89f4e5080a53 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsFrame.h @@ -0,0 +1,49 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FTS functions for getting frames * +* * +************************************************************************** +************************************************************************** + +*/ + +#include "ftsSoftware.h" + +/* Number of data bytes for each node */ +#define BYTES_PER_NODE 2 +#define OFFSET_LENGTH 2 +#define FRAME_DATA_HEADER 8 +#define FRAME_HEADER_SIGNATURE 0xB5 +#define FRAME_DATA_READ_RETRY 2 + +typedef struct { + DataHeader header; + short *node_data; + int node_data_size; +} MutualSenseFrame; + +typedef struct { + DataHeader header; + short *force_data; + short *sense_data; +} SelfSenseFrame; + +/* int getOffsetFrame(u16 address, u16 *offset); */ +int getChannelsLength(void); +int getFrameData(u16 address, int size, short **frame); +/* int getMSFrame(u16 type, short **frame, int keep_first_row); */ +/* int getMSKeyFrame(u16 type, short **frame); */ +/* int getSSFrame(u16 type, short **frame); */ +/* int getNmsFrame(u16 type, short ***frames, int * sizes, int keep_first_row, int fs, int n); */ +int getSenseLen(void); +int getForceLen(void); +int requestFrame(u16 type); +int readFrameDataHeader(u16 type, DataHeader *header); +int getMSFrame2(u16 type, MutualSenseFrame *frame); +int getSSFrame2(u16 type, SelfSenseFrame *frame); diff --git a/drivers/input/touchscreen/st/fts_lib/ftsGesture.c b/drivers/input/touchscreen/st/fts_lib/ftsGesture.c new file mode 100644 index 000000000000..fda4ab281948 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsGesture.c @@ -0,0 +1,393 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FTS Gesture Utilities * +* * +************************************************************************** +************************************************************************** + +*/ + +#include "ftsSoftware.h" +#include "ftsError.h" +#include "ftsGesture.h" +#include "ftsIO.h" +#include "ftsTool.h" + +static char tag[8] = "[ FTS ]\0"; + +static u8 gesture_mask[GESTURE_MASK_SIZE] = { 0 }; +static u8 custom_gestures[GESTURE_CUSTOM_NUMBER][GESTURE_CUSTOM_POINTS]; +static u8 custom_gesture_index[GESTURE_CUSTOM_NUMBER] = { 0 }; + +int enableGesture(u8 *mask, int size) +{ + u8 cmd[size+2]; + u8 readData[FIFO_EVENT_SIZE]; + int i, res; + int event_to_search[4] = {EVENTID_GESTURE, EVENT_TYPE_ENB, 0x00, GESTURE_ENABLE}; + + logError(0, "%s Trying to enable gesture...\n", tag); + cmd[0] = FTS_CMD_GESTURE_CMD; + cmd[1] = GESTURE_ENABLE; + + if (size <= GESTURE_MASK_SIZE) { + if (mask != NULL) { + for (i = 0; i < size; i++) { + cmd[i + 2] = mask[i]; + gesture_mask[i] = gesture_mask[i]|mask[i]; + /* back up of the gesture enabled */ + } + while (i < GESTURE_MASK_SIZE) { + cmd[i + 2] = gesture_mask[i]; + i++; + } + } else { + for (i = 0; i < GESTURE_MASK_SIZE; i++) { + cmd[i + 2] = gesture_mask[i]; + } + } + + res = fts_writeFwCmd(cmd, GESTURE_MASK_SIZE + 2); + if (res < OK) { + logError(1, "%s enableGesture: ERROR %08X\n", tag, res); + return res; + } + + res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s enableGesture: pollForEvent ERROR %08X\n", tag, res); + return res; + } + + if (readData[4] != 0x00) { + logError(1, "%s enableGesture: ERROR %08X\n", tag, ERROR_GESTURE_ENABLE_FAIL); + return ERROR_GESTURE_ENABLE_FAIL; + } + + logError(0, "%s enableGesture DONE!\n", tag); + return OK; + } else { + logError(1, "%s enableGesture: Size not valid! %d > %d ERROR %08X\n", tag, size, GESTURE_MASK_SIZE); + return ERROR_OP_NOT_ALLOW; + } + +} + +int disableGesture(u8 *mask, int size) +{ + u8 cmd[2+GESTURE_MASK_SIZE]; + u8 readData[FIFO_EVENT_SIZE]; + u8 temp; + int i, res; + int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, 0x00, GESTURE_DISABLE }; + + logError(0, "%s Trying to disable gesture...\n", tag); + cmd[0] = FTS_CMD_GESTURE_CMD; + cmd[1] = GESTURE_DISABLE; + + if (size <= GESTURE_MASK_SIZE) { + if (mask != NULL) { + for (i = 0; i < size; i++) { + cmd[i + 2] = mask[i]; + temp = gesture_mask[i] ^ mask[i]; + /* enabled XOR disabled */ + gesture_mask[i] = temp & gesture_mask[i]; + /* disable the gestures that were enabled */ + } + while (i < GESTURE_MASK_SIZE) { + cmd[i + 2] = gesture_mask[i]; + /* disable all the other gesture not specified */ + gesture_mask[i] = 0x00; + i++; + } + } else { + for (i = 0; i < GESTURE_MASK_SIZE; i++) { + cmd[i + 2] = gesture_mask[i]; + } + } + + res = fts_writeFwCmd(cmd, 2 + GESTURE_MASK_SIZE); + if (res < OK) { + logError(1, "%s disableGesture: ERROR %08X\n", tag, res); + return res; + } + + res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s disableGesture: pollForEvent ERROR %08X\n", tag, res); + return res; + } + + if (readData[4] != 0x00) { + logError(1, "%s disableGesture: ERROR %08X\n", tag, ERROR_GESTURE_ENABLE_FAIL); + return ERROR_GESTURE_ENABLE_FAIL; + } + + logError(0, "%s disableGesture DONE!\n", tag); + return OK; + } else { + logError(1, "%s disableGesture: Size not valid! %d > %d ERROR %08X\n", tag, size, GESTURE_MASK_SIZE); + return ERROR_OP_NOT_ALLOW; + } +} + +int startAddCustomGesture(u8 gestureID) +{ + u8 cmd[3] = { FTS_CMD_GESTURE_CMD, GESTURE_START_ADD, gestureID }; + int res; + u8 readData[FIFO_EVENT_SIZE]; + int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, gestureID, GESTURE_START_ADD }; + + res = fts_writeFwCmd(cmd, 3); + if (res < OK) { + logError(1, "%s startAddCustomGesture: Impossible to start adding custom gesture ID = %02X! ERROR %08X\n", tag, gestureID, res); + return res; + } + + res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s startAddCustomGesture: start add event not found! ERROR %08X\n", tag, res); + return res; + } + + if (readData[2] != gestureID || readData[4] != 0x00) { /* check of gestureID is redundant */ + logError(1, "%s startAddCustomGesture: start add event status not OK! ERROR %08X\n", tag, readData[4]); + return ERROR_GESTURE_START_ADD; + } + + return OK; +} + +int finishAddCustomGesture(u8 gestureID) +{ + u8 cmd[3] = { FTS_CMD_GESTURE_CMD, GESTURE_FINISH_ADD, gestureID }; + int res; + u8 readData[FIFO_EVENT_SIZE]; + int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, gestureID, GESTURE_FINISH_ADD }; + + res = fts_writeFwCmd(cmd, 3); + if (res < OK) { + logError(1, "%s finishAddCustomGesture: Impossible to finish adding custom gesture ID = %02X! ERROR %08X\n", tag, gestureID, res); + return res; + } + + res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s finishAddCustomGesture: finish add event not found! ERROR %08X\n", tag, res); + return res; + } + + if (readData[2] != gestureID || readData[4] != 0x00) { /* check of gestureID is redundant */ + logError(1, "%s finishAddCustomGesture: finish add event status not OK! ERROR %08X\n", tag, readData[4]); + return ERROR_GESTURE_FINISH_ADD; + } + + return OK; + +} + +int loadCustomGesture(u8 *template, u8 gestureID) +{ + int res, i; + int remaining = GESTURE_CUSTOM_POINTS; + int toWrite, offset = 0; + u8 cmd[TEMPLATE_CHUNK + 5]; + int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, gestureID, GESTURE_DATA_ADD }; + u8 readData[FIFO_EVENT_SIZE]; + + logError(0, "%s Starting adding custom gesture procedure...\n", tag); + + res = startAddCustomGesture(gestureID); + if (res < OK) { + logError(1, "%s loadCustomGesture: unable to start adding procedure! ERROR %08X\n", tag, res); + return res; + } + + cmd[0] = FTS_CMD_GESTURE_CMD; + cmd[1] = GESTURE_DATA_ADD; + cmd[2] = gestureID; + while (remaining > 0) { + if (remaining > TEMPLATE_CHUNK) { + toWrite = TEMPLATE_CHUNK; + } else { + toWrite = remaining; + } + + cmd[3] = toWrite; + cmd[4] = offset; + for (i = 0; i < toWrite; i++) { + cmd[i + 5] = template[i]; + } + + res = fts_writeFwCmd(cmd, toWrite + 5); + if (res < OK) { + logError(1, "%s loadCustomGesture: unable to start adding procedure! ERROR %08X\n", tag, res); + return res; + } + + res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s loadCustomGesture: add event not found! ERROR %08X\n", tag, res); + return res; + } + + if (readData[2] != gestureID || readData[4] != 0x00) { /* check of gestureID is redundant */ + logError(1, "%s loadCustomGesture: add event status not OK! ERROR %08X\n", tag, readData[4]); + return ERROR_GESTURE_DATA_ADD; + } + + remaining -= toWrite; + offset += toWrite / 2; + } + + res = finishAddCustomGesture(gestureID); + if (res < OK) { + logError(1, "%s loadCustomGesture: unable to finish adding procedure! ERROR %08X\n", tag, res); + return res; + } + + logError(0, "%s Adding custom gesture procedure DONE!\n", tag); + return OK; + +} + +int reloadCustomGesture(void) +{ + int res, i; + + logError(0, "%s Starting reload Gesture Template...\n", tag); + + for (i = 0; i < GESTURE_CUSTOM_NUMBER; i++) { + if (custom_gesture_index[i] == 1) { + res = loadCustomGesture(custom_gestures[i], GESTURE_CUSTOM_OFFSET+i); + if (res < OK) { + logError(1, "%s reloadCustomGesture: Impossible to load custom gesture ID = %02X! ERROR %08X\n", tag, GESTURE_CUSTOM_OFFSET + i, res); + return res; + } + } + } + + logError(0, "%s Reload Gesture Template DONE!\n", tag); + return OK; + +} + +int enterGestureMode(int reload) +{ + u8 cmd = FTS_CMD_GESTURE_MODE; + int res, ret; + + res = fts_disableInterrupt(); + if (res < OK) { + logError(1, "%s enterGestureMode: ERROR %08X\n", tag, res|ERROR_DISABLE_INTER); + return res | ERROR_DISABLE_INTER; + } + + if (reload == 1) { + + res = reloadCustomGesture(); + if (res < OK) { + logError(1, "%s enterGestureMode: impossible reload custom gesture! ERROR %08X\n", tag, res); + goto END; + } + + res = disableGesture(NULL, 0); + if (res < OK) { + logError(1, "%s enterGestureMode: disableGesture ERROR %08X\n", tag, res); + goto END; + } + + res = enableGesture(NULL, 0); + if (res < OK) { + logError(1, "%s enterGestureMode: enableGesture ERROR %08X\n", tag, res); + goto END; + } + } + + res = fts_writeFwCmd(&cmd, 1); + if (res < OK) { + logError(1, "%s enterGestureMode: enter gesture mode ERROR %08X\n", tag, res); + goto END; + } + + res = OK; +END: + ret = fts_enableInterrupt(); + if (ret < OK) { + logError(1, "%s enterGestureMode: fts_enableInterrupt ERROR %08X\n", tag, res | ERROR_ENABLE_INTER); + res |= ret | ERROR_ENABLE_INTER; + } + + return res; +} + +int addCustomGesture(u8 *data, int size, u8 gestureID) +{ + int index, res, i; + + index = gestureID - GESTURE_CUSTOM_OFFSET; + + logError(0, "%s Starting Custom Gesture Adding procedure...\n", tag); + if (size != GESTURE_CUSTOM_POINTS && gestureID != GES_ID_CUST1 && gestureID != GES_ID_CUST2 && gestureID != GES_ID_CUST3 && gestureID != GES_ID_CUST4 && gestureID && GES_ID_CUST5) { + logError(1, "%s addCustomGesture: Invalid size (%d) or Custom GestureID (%02X)! ERROR %08X\n", tag, size, gestureID, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + for (i = 0; i < GESTURE_CUSTOM_POINTS; i++) { + custom_gestures[index][i] = data[i]; + } + + res = loadCustomGesture(custom_gestures[index], gestureID); + if (res < OK) { + logError(1, "%s addCustomGesture: impossible to load the custom gesture! ERROR %08X\n", tag, res); + return res; + } + + custom_gesture_index[index] = 1; + logError(0, "%s Custom Gesture Adding procedure DONE!\n", tag); + return OK; +} + +int removeCustomGesture(u8 gestureID) +{ + int res, index; + u8 cmd[3] = { FTS_CMD_GESTURE_CMD, GETURE_REMOVE_CUSTOM, gestureID }; + int event_to_search[4] = {EVENTID_GESTURE, EVENT_TYPE_ENB, gestureID, GETURE_REMOVE_CUSTOM }; + u8 readData[FIFO_EVENT_SIZE]; + + index = gestureID - GESTURE_CUSTOM_OFFSET; + + logError(0, "%s Starting Custom Gesture Removing procedure...\n", tag); + if (gestureID != GES_ID_CUST1 && gestureID != GES_ID_CUST2 && gestureID != GES_ID_CUST3 && gestureID != GES_ID_CUST4 && gestureID && GES_ID_CUST5) { + logError(1, "%s removeCustomGesture: Invalid size (%d) or Custom GestureID (%02X)! ERROR %08X\n", tag, gestureID, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + res = fts_writeFwCmd(cmd, 3);/* when a gesture is removed, it is also disabled automatically */ + if (res < OK) { + logError(1, "%s removeCustomGesture: Impossible to remove custom gesture ID = %02X! ERROR %08X\n", tag, gestureID, res); + return res; + } + + res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s removeCustomGesture: remove event not found! ERROR %08X\n", tag, res); + return res; + } + + if (readData[2] != gestureID || readData[4] != 0x00) { /* check of gestureID is redundant */ + logError(1, "%s removeCustomGesture: remove event status not OK! ERROR %08X\n", tag, readData[4]); + return ERROR_GESTURE_REMOVE; + } + + custom_gesture_index[index] = 0; + logError(0, "%s Custom Gesture Remove procedure DONE!\n", tag); + return OK; + +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsGesture.h b/drivers/input/touchscreen/st/fts_lib/ftsGesture.h new file mode 100644 index 000000000000..a9c3e3c05573 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsGesture.h @@ -0,0 +1,74 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FTS Gesture Utilities * +* * +************************************************************************** +************************************************************************** + +*/ + +#define GESTURE_MASK_SIZE 8 + +#define GESTURE_CUSTOM_POINTS (30*2) +/* for each custom gesture should be provided 30 points (each point is a couple of x,y) */ +#define GESTURE_CUSTOM_NUMBER 5 /* fw support up to 5 custom gestures */ + +#define TEMPLATE_CHUNK (10*2) +/* number of points to transfer with each I2C transaction */ + +/* Gesture IDs */ +#define GES_ID_DBLTAP 0x01 /* Double Tap */ +#define GES_ID_O 0x02 /* 'O' */ +#define GES_ID_C 0x03 /* 'C' */ +#define GES_ID_M 0x04 /* 'M' */ +#define GES_ID_W 0x05 /* 'W' */ +#define GES_ID_E 0x06 /* 'e' */ +#define GES_ID_HFLIP_L2R 0x07 /* Left to right line */ +#define GES_ID_HFLIP_R2L 0x08 /* Right to left line */ +#define GES_ID_VFLIP_T2D 0x09 /* Top to bottom line */ +#define GES_ID_VFLIP_D2T 0x0A /* Bottom to Top line */ +#define GES_ID_L 0x0B /* 'L' */ +#define GES_ID_F 0x0C /* 'F' */ +#define GES_ID_V 0x0D /* 'V' */ +#define GES_ID_AT 0x0E /* '@' */ +#define GES_ID_S 0x0F /* 'S' */ +#define GES_ID_Z 0x10 /* 'Z' */ +#define GES_ID_CUST1 0x11 /* Custom gesture 1 */ +#define GES_ID_CUST2 0x12 /* Custom gesture 2 */ +#define GES_ID_CUST3 0x13 /* Custom gesture 3 */ +#define GES_ID_CUST4 0x14 /* Custom gesture 4 */ +#define GES_ID_CUST5 0x15 /* Custom gesture 5 */ +#define GES_ID_LEFTBRACE 0x20 /* '<' */ +#define GES_ID_RIGHTBRACE 0x21 /* '>' */ + +#define GESTURE_CUSTOM_OFFSET GES_ID_CUST1 + +/* Command sub-type */ +#define GESTURE_ENABLE 0x01 +#define GESTURE_DISABLE 0x02 +#define GESTURE_ENB_CHECK 0x03 +#define GESTURE_START_ADD 0x10 +#define GESTURE_DATA_ADD 0x11 +#define GESTURE_FINISH_ADD 0x12 +#define GETURE_REMOVE_CUSTOM 0x13 +#define GESTURE_CHECK_CUSTOM 0x14 + +/* Event sub-type */ +#define EVENT_TYPE_ENB 0x04 +#define EVENT_TYPE_CHECK_ENB 0x03 +#define EVENT_TYPE_GESTURE_DTC1 0x01 +#define EVENT_TYPE_GESTURE_DTC2 0x02 + +int disableGesture(u8 *mask, int size); +int enableGesture(u8 *mask, int size); +int startAddCustomGesture(u8 gestureID); +int finishAddCustomGesture(u8 gestureID); +int loadCustomGesture(u8 *template, u8 gestureID); +int reloadCustomGesture(void); +int enterGestureMode(int reload); diff --git a/drivers/input/touchscreen/st/fts_lib/ftsHardware.h b/drivers/input/touchscreen/st/fts_lib/ftsHardware.h new file mode 100644 index 000000000000..933059e671b4 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsHardware.h @@ -0,0 +1,177 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* HW related data * +* * +************************************************************************** +************************************************************************** + +*/ + +#define FTM3_CHIP + +/* DUMMY BYTES DATA */ +#define DUMMY_HW_REG 1 +#define DUMMY_FRAMEBUFFER 1 +#define DUMMY_MEMORY 1 + +/* DIGITAL CHIP INFO */ +#ifdef FTM3_CHIP +#define DCHIP_ID_0 0x39 +#define DCHIP_ID_1 0x6C +#else +#define DCHIP_ID_0 0x36 +#define DCHIP_ID_1 0x70 +#endif + +#ifdef FTM3_CHIP +#define DCHIP_ID_ADDR 0x0007 +#define DCHIP_FW_VER_ADDR 0x000A +#else +#define DCHIP_ID_ADDR 0x0004 +#define DCHIP_FW_VER_ADDR 0x000B +#endif + +#define DCHIP_FW_VER_BYTE 2 + +/* CHUNKS */ +#define READ_CHUNK (2*1024) +#define WRITE_CHUNK (2*1024) +#define MEMORY_CHUNK (2*1024) + +/* PROTOCOL INFO */ +#ifdef FTM3_CHIP +#define I2C_SAD 0x49 +#else +#define I2C_SAD 0x48 +#endif + +#define I2C_INTERFACE /* comment if the chip use SPI */ +#define ICR_ADDR 0x0024 +#define ICR_SPI_VALUE 0x02 + +/* SYSTEM RESET INFO */ +#ifdef FTM3_CHIP +#define SYSTEM_RESET_ADDRESS 0x0023 +#define SYSTEM_RESET_VALUE 0x01 +#else +#define SYSTEM_RESET_ADDRESS 0x0028 +#define SYSTEM_RESET_VALUE 0x80 +#endif + +/* INTERRUPT INFO */ +#ifdef FTM3_CHIP +#define IER_ADDR 0x001C +#else +#define IER_ADDR 0x002C +#endif + +#define IER_ENABLE 0x41 +#define IER_DISABLE 0x00 + +/* FLASH COMMAND */ + +#define FLASH_CMD_UNLOCK 0xF7 + +#ifdef FTM3_CHIP +#define FLASH_CMD_WRITE_LOWER_64 0xF0 +#define FLASH_CMD_WRITE_UPPER_64 0xF1 +#define FLASH_CMD_BURN 0xF2 +#define FLASH_CMD_ERASE 0xF3 +#define FLASH_CMD_READSTATUS 0xF4 +#else +#define FLASH_CMD_WRITE_64K 0xF8 +#define FLASH_CMD_READ_REGISTER 0xF9 +#define FLASH_CMD_WRITE_REGISTER 0xFA +#endif + +/* FLASH UNLOCK PARAMETER */ +#define FLASH_UNLOCK_CODE0 0x74 +#define FLASH_UNLOCK_CODE1 0x45 + +#ifndef FTM3_CHIP +/* FLASH ERASE and DMA PARAMETER */ +#define FLASH_ERASE_UNLOCK_CODE0 0x72 +#define FLASH_ERASE_UNLOCK_CODE1 0x03 +#define FLASH_ERASE_UNLOCK_CODE2 0x02 +#define FLASH_ERASE_CODE0 0x02 +#define FLASH_ERASE_CODE1 0xC0 +#define FLASH_DMA_CODE0 0x05 +#define FLASH_DMA_CODE1 0xC0 +#define FLASH_DMA_CONFIG 0x06 +#endif + +/* FLASH ADDRESS */ +#ifdef FTM3_CHIP +#define FLASH_ADDR_SWITCH_CMD 0x00010000 +#define FLASH_ADDR_CODE 0x00000000 +#define FLASH_ADDR_CONFIG 0x0001E800 +#define FLASH_ADDR_CX 0x0001F000 +#else +#define ADDR_WARM_BOOT 0x001E +#define WARM_BOOT_VALUE 0x38 +#define FLASH_ADDR_CODE 0x00000000 +#define FLASH_ADDR_CONFIG 0x0000FC00 +#endif + +/* CRC ADDR */ +#ifdef FTM3_CHIP +#define ADDR_CRC_BYTE0 0x00 +#define ADDR_CRC_BYTE1 0x86 +#define CRC_MASK 0x02 +#else +#define ADDR_CRC_BYTE0 0x00 +#define ADDR_CRC_BYTE1 0x74 +#define CRC_MASK 0x03 +#endif + +/* SIZES FW, CODE, CONFIG, MEMH */ +#ifdef FTM3_CHIP +#define FW_HEADER_SIZE 32 +#define FW_SIZE (int)(128*1024) +#define FW_CODE_SIZE (int)(122*1024) +#define FW_CONFIG_SIZE (int)(2*1024) +#define FW_CX_SIZE (int)(FW_SIZE-FW_CODE_SIZE-FW_CONFIG_SIZE) +#define FW_VER_MEMH_BYTE1 193 +#define FW_VER_MEMH_BYTE0 192 +#define FW_OFF_CONFID_MEMH_BYTE1 2 +#define FW_OFF_CONFID_MEMH_BYTE0 1 +#define FW_BIN_VER_OFFSET 4 +#define FW_BIN_CONFIG_VER_OFFSET (FW_HEADER_SIZE+FW_CODE_SIZE+1) +#else +#define FW_HEADER_SIZE 64 +#define FW_HEADER_SIGNATURE 0xAA55AA55 +#define FW_FTB_VER 0x00000001 +#define FW_BYTES_ALIGN 4 +#define FW_BIN_VER_OFFSET 16 +#define FW_BIN_CONFIG_VER_OFFSET 20 +#endif + +/* FIFO */ +#define FIFO_EVENT_SIZE 8 +#ifdef FTM3_CHIP +#define FIFO_DEPTH 32 +#else +#define FIFO_DEPTH 64 +#endif + +#define FIFO_CMD_READONE 0x85 +#define FIFO_CMD_READALL 0x86 +#define FIFO_CMD_LAST 0x87 +#define FIFO_CMD_FLUSH 0xA1 + +/* CONSTANT TOTAL CX */ +#define CX1_WEIGHT 4 +#define CX2_WEIGHT 1 + +/* OP CODES FOR MEMORY (based on protocol) */ + +#define FTS_CMD_HW_REG_R 0xB6 +#define FTS_CMD_HW_REG_W 0xB6 +#define FTS_CMD_FRAMEBUFFER_R 0xD0 +#define FTS_CMD_FRAMEBUFFER_W 0xD0 diff --git a/drivers/input/touchscreen/st/fts_lib/ftsIO.c b/drivers/input/touchscreen/st/fts_lib/ftsIO.c new file mode 100644 index 000000000000..96ca8cfecd8a --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsIO.c @@ -0,0 +1,403 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* I2C/SPI Communication * +* * +************************************************************************** +************************************************************************** + +*/ + +#include "ftsSoftware.h" +#include "ftsCrossCompile.h" + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <stdarg.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/serio.h> +#include <linux/init.h> +#include <linux/pm.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/power_supply.h> +#include <linux/firmware.h> +#include <linux/regulator/consumer.h> +#include <linux/of_gpio.h> +/* #include <linux/sec_sysfs.h> */ +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/spi/spidev.h> +static struct i2c_client *client; +static u16 I2CSAD; + +#include "ftsError.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsTool.h" + +static char tag[8] = "[ FTS ]\0"; + +int openChannel(struct i2c_client *clt) +{ + client = clt; + I2CSAD = clt->addr; + logError(1, "%s openChannel: SAD: %02X\n", tag, I2CSAD); + return OK; +} + +struct device *getDev(void) +{ + if (client != NULL) + return &(client->dev); + else + return NULL; +} + +struct i2c_client *getClient(void) +{ + if (client != NULL) + return client; + else + return NULL; +} + +int fts_readCmd(u8 *cmd, int cmdLength, u8 *outBuf, int byteToRead) +{ + int ret = -1; + int retry = 0; + struct i2c_msg I2CMsg[2]; + + /* write msg */ + I2CMsg[0].addr = (__u16)I2CSAD; + I2CMsg[0].flags = (__u16)0; + I2CMsg[0].len = (__u16)cmdLength; + I2CMsg[0].buf = (__u8 *)cmd; + + /* read msg */ + I2CMsg[1].addr = (__u16)I2CSAD; + I2CMsg[1].flags = I2C_M_RD; + I2CMsg[1].len = byteToRead; + I2CMsg[1].buf = (__u8 *)outBuf; + + if (client == NULL) + return ERROR_I2C_O; + while (retry < I2C_RETRY && ret < OK) { + ret = i2c_transfer(client->adapter, I2CMsg, 2); + if (ret >= OK) + break; + retry++; + msleep(I2C_WAIT_BEFORE_RETRY); + } + if (ret < 0) { + logError(1, "%s fts_readCmd: ERROR %02X\n", tag, ERROR_I2C_R); + return ERROR_I2C_R; + } + return OK; +} + +int fts_writeCmd(u8 *cmd, int cmdLength) +{ + int ret = -1; + int retry = 0; + struct i2c_msg I2CMsg[2]; + + I2CMsg[0].addr = (__u16)I2CSAD; + I2CMsg[0].flags = (__u16)0; + I2CMsg[0].len = (__u16)cmdLength; + I2CMsg[0].buf = (__u8 *)cmd; + + if (client == NULL) +return ERROR_I2C_O; + while (retry < I2C_RETRY && ret < OK) { + ret = i2c_transfer(client->adapter, I2CMsg, 1); + if (ret >= OK) + break; + retry++; + msleep(I2C_WAIT_BEFORE_RETRY); + /* logError(1, "%s fts_writeCmd: attempt %d\n", tag, retry); */ + } + if (ret < 0) { + logError(1, "%s fts_writeCmd: ERROR %02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + return OK; +} + +int fts_writeFwCmd(u8 *cmd, int cmdLength) +{ + int ret = -1; + int ret2 = -1; + int retry = 0; + struct i2c_msg I2CMsg[2]; + + I2CMsg[0].addr = (__u16)I2CSAD; + I2CMsg[0].flags = (__u16)0; + I2CMsg[0].len = (__u16)cmdLength; + I2CMsg[0].buf = (__u8 *)cmd; + + if (client == NULL) +return ERROR_I2C_O; + while (retry < I2C_RETRY && (ret < OK || ret2 < OK)) { + ret = i2c_transfer(client->adapter, I2CMsg, 1); + retry++; + if (ret >= 0) { + ret2 = checkEcho(cmd, cmdLength); + break; + } + msleep(I2C_WAIT_BEFORE_RETRY); + /* logError(1, "%s fts_writeCmd: attempt %d\n", tag, retry); */ + } + if (ret < 0) { + logError(1, "%s fts_writeFwCmd: ERROR %02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + if (ret2 < OK) { + logError(1, "%s fts_writeFwCmd: check echo ERROR %02X\n", tag, ret2); + return (ret|ERROR_I2C_W); + } + return OK; +} + +int writeReadCmd(u8 *writeCmd1, int writeCmdLength, u8 *readCmd1, + int readCmdLength, u8 *outBuf, int byteToRead) +{ + int ret = -1; + int retry = 0; + struct i2c_msg I2CMsg[3]; + + /* write msg */ + I2CMsg[0].addr = (__u16)I2CSAD; + I2CMsg[0].flags = (__u16)0; + I2CMsg[0].len = (__u16)writeCmdLength; + I2CMsg[0].buf = (__u8 *)writeCmd1; + + /* write msg */ + I2CMsg[1].addr = (__u16)I2CSAD; + I2CMsg[1].flags = (__u16)0; + I2CMsg[1].len = (__u16)readCmdLength; + I2CMsg[1].buf = (__u8 *)readCmd1; + + /* read msg */ + I2CMsg[2].addr = (__u16)I2CSAD; + I2CMsg[2].flags = I2C_M_RD; + I2CMsg[2].len = byteToRead; + I2CMsg[2].buf = (__u8 *)outBuf; + + if (client == NULL) + return ERROR_I2C_O; + while (retry < I2C_RETRY && ret < OK) { + ret = i2c_transfer(client->adapter, I2CMsg, 3); + if (ret >= OK) + break; + retry++; + msleep(I2C_WAIT_BEFORE_RETRY); + } + + if (ret < 0) { + logError(1, "%s writeReadCmd: ERROR %02X\n", tag, ERROR_I2C_WR); + return ERROR_I2C_WR; + } + return OK; + +} + +int readCmdU16(u8 cmd, u16 address, u8 *outBuf, int byteToRead, int hasDummyByte) +{ + + int remaining = byteToRead; + int toRead = 0; + u8 rCmd[3] = { cmd, 0x00, 0x00 }; + + u8 *buff = (u8 *)kmalloc((READ_CHUNK + 1)*sizeof(u8), GFP_KERNEL); + if (buff == NULL) { + logError(1, "%s readCmdU16: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + while (remaining > 0) { + if (remaining >= READ_CHUNK) { + toRead = READ_CHUNK; + remaining -= READ_CHUNK; + } else { + toRead = remaining; + remaining = 0; + } + + rCmd[1] = (u8)((address & 0xFF00) >> 8); + rCmd[2] = (u8)(address & 0xFF); + + if (hasDummyByte) { + if (fts_readCmd(rCmd, 3, buff, toRead + 1) < 0) { + logError(1, "%s readCmdU16: ERROR %02X\n", tag, ERROR_I2C_R); + return ERROR_I2C_R; + } + memcpy(outBuf, buff + 1, toRead); + } else { + if (fts_readCmd(rCmd, 3, buff, toRead) < 0) + return ERROR_I2C_R; + memcpy(outBuf, buff, toRead); + } + + address += toRead; + + outBuf += toRead; + + } + kfree(buff); + + return OK; +} + +int writeCmdU16(u8 WriteCmd, u16 address, u8 *dataToWrite, int byteToWrite) +{ + + int remaining = byteToWrite; + int toWrite = 0; + + u8 *buff = (u8 *)kmalloc((WRITE_CHUNK + 3)*sizeof(u8), GFP_KERNEL); + if (buff == NULL) { + logError(1, "%s writeCmdU16: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + buff[0] = WriteCmd; + + while (remaining > 0) { + if (remaining >= WRITE_CHUNK) { + toWrite = WRITE_CHUNK; + remaining -= WRITE_CHUNK; + } else { + toWrite = remaining; + remaining = 0; + } + + buff[1] = (u8)((address & 0xFF00) >> 8); + buff[2] = (u8)(address & 0xFF); + memcpy(buff + 3, dataToWrite, toWrite); + if (fts_writeCmd(buff, 3 + toWrite) < 0) { + logError(1, "%s writeCmdU16: ERROR %02\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + address += toWrite; + dataToWrite += toWrite; + + } + + return OK; +} + +int writeCmdU32(u8 writeCmd1, u8 writeCmd2, u32 address, u8 *dataToWrite, int byteToWrite) +{ + + int remaining = byteToWrite; + int toWrite = 0; + + u8 buff1[3] = { writeCmd1, 0x00, 0x00 }; + u8 *buff2 = (u8 *)kmalloc((WRITE_CHUNK + 3)*sizeof(u8), GFP_KERNEL); + if (buff2 == NULL) { + logError(1, "%s writeCmdU32: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + buff2[0] = writeCmd2; + + while (remaining > 0) { + if (remaining >= WRITE_CHUNK) { + toWrite = WRITE_CHUNK; + remaining -= WRITE_CHUNK; + } else { + toWrite = remaining; + remaining = 0; + } + + buff1[1] = (u8)((address & 0xFF000000) >> 24); + buff1[2] = (u8)((address & 0x00FF0000) >> 16); + buff2[1] = (u8)((address & 0x0000FF00) >> 8); + buff2[2] = (u8)(address & 0xFF); + memcpy(buff2 + 3, dataToWrite, toWrite); + + if (fts_writeCmd(buff1, 3) < 0) { + logError(1, "%s writeCmdU32: ERROR %02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + if (fts_writeCmd(buff2, 3 + toWrite) < 0) { + logError(1, "%s writeCmdU32: ERROR %02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + + address += toWrite; + dataToWrite += toWrite; + + } + + return OK; +} + +int writeReadCmdU32(u8 wCmd, u8 rCmd, u32 address, u8 *outBuf, int byteToRead, int hasDummyByte) +{ + + int remaining = byteToRead; + int toRead = 0; + u8 reaCmd[3]; + u8 wriCmd[3]; + + u8 *buff = (u8 *)kmalloc((READ_CHUNK + 1)*sizeof(u8), GFP_KERNEL); + if (buff == NULL) { + logError(1, "%s writereadCmd32: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + reaCmd[0] = rCmd; + wriCmd[0] = wCmd; + + while (remaining > 0) { + if (remaining >= READ_CHUNK) { + toRead = READ_CHUNK; + remaining -= READ_CHUNK; + } else { + toRead = remaining; + remaining = 0; + } + + wriCmd[1] = (u8)((address & 0xFF000000) >> 24); + wriCmd[2] = (u8)((address & 0x00FF0000) >> 16); + + reaCmd[1] = (u8)((address & 0x0000FF00) >> 8); + reaCmd[2] = (u8)(address & 0x000000FF); + + if (hasDummyByte) { + if (writeReadCmd(wriCmd, 3, reaCmd, 3, buff, toRead + 1) < 0) { + logError(1, "%s writeCmdU32: ERROR %02X\n", tag, ERROR_I2C_WR); + return ERROR_I2C_WR; + } + memcpy(outBuf, buff + 1, toRead); + } else { + if (writeReadCmd(wriCmd, 3, reaCmd, 3, buff, toRead) < 0) + return ERROR_I2C_WR; + memcpy(outBuf, buff, toRead); + } + + address += toRead; + + outBuf += toRead; + + } + + return OK; +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsIO.h b/drivers/input/touchscreen/st/fts_lib/ftsIO.h new file mode 100644 index 000000000000..7bdeda2f9a2d --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsIO.h @@ -0,0 +1,35 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* I2C/SPI Communication * +* * +************************************************************************** +************************************************************************** + +*/ + +#include "ftsSoftware.h" +#include "ftsCrossCompile.h" + +#include <linux/i2c.h> +#include <linux/i2c-dev.h> + +#define I2C_RETRY 3 /* number */ +#define I2C_WAIT_BEFORE_RETRY 10 /* ms */ + +int openChannel(struct i2c_client *clt); +struct device *getDev(void); +struct i2c_client *getClient(void); +int fts_readCmd(u8 *cmd, int cmdLenght, u8 *outBuf, int byteToRead); +int fts_writeCmd(u8 *cmd, int cmdLenght); +int fts_writeFwCmd(u8 *cmd, int cmdLenght); +int writeReadCmd(u8 *writeCmd, int writeCmdLenght, u8 *readCmd, int readCmdLenght, u8 *outBuf, int byteToRead); +int readCmdU16(u8 cmd, u16 address, u8 *outBuf, int byteToRead, int hasDummyByte); +int writeCmdU16(u8 WriteCmd, u16 address, u8 *dataToWrite, int byteToWrite); +int writeCmdU32(u8 writeCmd1, u8 writeCmd2, u32 address, u8 *dataToWrite, int byteToWrite); +int writeReadCmdU32(u8 wCmd, u8 rCmd, u32 address, u8 *outBuf, int byteToRead, int hasDummyByte); diff --git a/drivers/input/touchscreen/st/fts_lib/ftsSoftware.h b/drivers/input/touchscreen/st/fts_lib/ftsSoftware.h new file mode 100644 index 000000000000..c8bbc8e18d28 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsSoftware.h @@ -0,0 +1,131 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FW related data * +* * +************************************************************************** +************************************************************************** + +*/ + +#include "ftsHardware.h" + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; + +#define ECHO_ENABLED 0x00000001 + +/* chipInfo ftsInfo; */ + +/* FTS FW COMAND */ +#define FTS_CMD_MS_MT_SENSE_OFF 0x92 +#define FTS_CMD_MS_MT_SENSE_ON 0x93 +#define FTS_CMD_SS_HOVER_OFF 0x94 +#define FTS_CMD_SS_HOVER_ON 0x95 +#define FTS_CMD_LP_TIMER_CALIB 0x97 +#define FTS_CMD_MS_KEY_OFF 0x9A +#define FTS_CMD_MS_KEY_ON 0x9B +#define FTS_CMD_MS_COMP_TUNING 0xA3 +#define FTS_CMD_SS_COMP_TUNING 0xA4 +#define FTS_CMD_FULL_INITIALIZATION 0xA5 +#define FTS_CMD_ITO_CHECK 0xA7 +#define FTS_CMD_RELEASE_INFO 0xAA +#define FTS_CMD_GESTURE_MODE 0xAD +#define FTS_CMD_REQU_FW_CONF 0xB2 +#define FTS_CMD_REQU_FRAME_DATA 0xB7 +#define FTS_CMD_REQU_COMP_DATA 0xB8 +#define FTS_CMD_WRITE_MP_FLAG 0xC0 +#define FTS_CMD_FEATURE_ENABLE 0xC1 +#define FTS_CMD_FEATURE_DISABLE 0xC2 +#define FTS_CMD_GESTURE_CMD 0xC3 +#define FTS_CMD_SAVE_CX_TUNING 0xFC + +/* Event ID */ +#define EVENTID_NO_EVENT 0x00 +#define EVENTID_ERROR_EVENT 0x0F +#define EVENTID_CONTROL_READY 0x10 +#define EVENTID_FW_CONFIGURATION 0x12 +#define EVENTID_COMP_DATA_READ 0x13 +#define EVENTID_STATUS_UPDATE 0x16 +#define EVENTID_RELEASE_INFO 0x1C +#define EVENTID_ENTER_POINTER 0x03 +#define EVENTID_LEAVE_POINTER 0x04 +#define EVENTID_MOTION_POINTER 0x05 +#define EVENTID_HOVER_ENTER_POINTER 0x07 +#define EVENTID_HOVER_LEAVE_POINTER 0x08 +#define EVENTID_HOVER_MOTION_POINTER 0x09 +#define EVENTID_PROXIMITY_ENTER 0x0B +#define EVENTID_PROXIMITY_LEAVE 0x0C +#define EVENTID_KEY_STATUS 0x0E +#define EVENTID_GESTURE 0x22 +#define EVENTID_FRAME_DATA_READ 0x25 +#define EVENTID_ECHO 0xEC +#define EVENTID_LAST (EVENTID_FRAME_DATA_READ+1) + +/* EVENT TYPE */ +#define EVENT_TYPE_MS_TUNING_CMPL 0x01 +#define EVENT_TYPE_SS_TUNING_CMPL 0x02 +#define EVENT_TYPE_COMP_DATA_SAVED 0x04 +#define EVENT_TYPE_ITO 0x05 +#define EVENT_TYPE_FULL_INITIALIZATION 0x07 +#define EVENT_TYPE_LPTIMER_TUNING_CMPL 0x20 +#define EVENT_TYPE_ESD_ERROR 0x0A +#define EVENT_TYPE_WATCHDOG_ERROR 0x01 + +/* CONFIG ID INFO */ +#define CONFIG_ID_ADDR 0x0001 +#define CONFIG_ID_BYTE 2 + +/* ADDRESS OFFSET IN SYSINFO */ +#define ADDR_RAW_TOUCH 0x0000 +#define ADDR_FILTER_TOUCH 0x0002 +#define ADDR_NORM_TOUCH 0x0004 +#define ADDR_CALIB_TOUCH 0x0006 +#define ADDR_RAW_HOVER_FORCE 0x000A +#define ADDR_RAW_HOVER_SENSE 0x000C +#define ADDR_FILTER_HOVER_FORCE 0x000E +#define ADDR_FILTER_HOVER_SENSE 0x0010 +#define ADDR_NORM_HOVER_FORCE 0x0012 +#define ADDR_NORM_HOVER_SENSE 0x0014 +#define ADDR_CALIB_HOVER_FORCE 0x0016 +#define ADDR_CALIB_HOVER_SENSE 0x0018 +#define ADDR_RAW_PRX_FORCE 0x001A +#define ADDR_RAW_PRX_SENSE 0x001C +#define ADDR_FILTER_PRX_FORCE 0x001E +#define ADDR_FILTER_PRX_SENSE 0x0020 +#define ADDR_NORM_PRX_FORCE 0x0022 +#define ADDR_NORM_PRX_SENSE 0x0024 +#define ADDR_CALIB_PRX_FORCE 0x0026 +#define ADDR_CALIB_PRX_SENSE 0x0028 +#define ADDR_RAW_MS_KEY 0x0032 +#define ADDR_COMP_DATA 0x0050 +#define ADDR_FRAMEBUFFER_DATA 0x8000 + +/* ADDRESS FW REGISTER */ +#define ADDR_SENSE_LEN 0x0014 +#define ADDR_FORCE_LEN 0x0015 +#define ADDR_MS_TUNING_VER 0x0729 +#define ADDR_SS_TUNING_VER 0x074E + +/* B2 INFO */ +#define B2_DATA_BYTES 4 +#define B2_CHUNK ((FIFO_DEPTH/2)*B2_DATA_BYTES) /* number of bytes */ + +/* FEATURES */ +#define FEAT_GESTURE 0x00 +#define FEAT_GLOVE 0x01 +#define FEAT_STYLUS 0x02 +#define FEAT_COVER 0x04 +#define FEAT_CHARGER 0x08 +#define FEAT_VR 0x10 +#define FEAT_EDGE_REJECTION 0x20 + +/* MP_FLAG_VALUE */ +#define INIT_MP 0xA5A5A501 +#define INIT_FIELD 0xA5A5A502 diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTest.c b/drivers/input/touchscreen/st/fts_lib/ftsTest.c new file mode 100644 index 000000000000..68bd04eff316 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsTest.c @@ -0,0 +1,2324 @@ +/* + + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS API for MP test * + * * + ************************************************************************** + ************************************************************************** + + */ + +#include "ftsCrossCompile.h" +#include "ftsCompensation.h" +#include "ftsError.h" +#include "ftsFrame.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTest.h" +#include "ftsTime.h" +#include "ftsTool.h" +#include "../fts.h" + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <stdarg.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/serio.h> +#include <linux/time.h> +#include <linux/pm.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/power_supply.h> +#include <linux/firmware.h> +#include <linux/regulator/consumer.h> +#include <linux/of_gpio.h> +/* #include <linux/sec_sysfs.h> */ + +#ifdef LIMITS_H_FILE +#include <../fts_limits.h> +#endif + +/* static char tag[8] = "[ FTS ]\0"; */ + +int computeAdjHoriz(u8 *data, int row, int column, u8 **result) +{ + int i, j; + int size = row * (column - 1); + + if (column < 2) { + logError(1, "%s computeAdjHoriz: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *result = (u8 *) kmalloc(size * sizeof (u8), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s computeAdjHoriz: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 0; i < row; i++) { + for (j = 1; j < column; j++) { + *(*result + (i * (column - 1) + (j - 1))) = abs(data[i * column + j] - data[i * column + (j - 1)]); + } + } + + return OK; + +} + +int computeAdjHorizTotal(u16 *data, int row, int column, u16 **result) +{ + int i, j; + int size = row * (column - 1); + + if (column < 2) { + logError(1, "%s computeAdjHorizTotal: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *result = (u16 *) kmalloc(size * sizeof (u16), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s computeAdjHorizTotal: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 0; i < row; i++) { + for (j = 1; j < column; j++) { + *(*result + (i * (column - 1) + (j - 1))) = abs(data[i * column + j] - data[i * column + (j - 1)]); + } + } + + return OK; + +} + +int computeAdjVert(u8 *data, int row, int column, u8 **result) +{ + int i, j; + int size = (row - 1)*(column); + + if (row < 2) { + logError(1, "%s computeAdjVert: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *result = (u8 *) kmalloc(size * sizeof (u8), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s computeAdjVert: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 1; i < row; i++) { + for (j = 0; j < column; j++) { + *(*result + ((i - 1) * column + j)) = abs(data[i * column + j] - data[(i - 1) * column + j]); + } + } + + return OK; +} + +int computeAdjVertTotal(u16 *data, int row, int column, u16 **result) +{ + int i, j; + int size = (row - 1)*(column); + + if (row < 2) { + logError(1, "%s computeAdjVertTotal: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *result = (u16 *) kmalloc(size * sizeof (u16), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s computeAdjVertTotal: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 1; i < row; i++) { + for (j = 0; j < column; j++) { + *(*result + ((i - 1) * column + j)) = abs(data[i * column + j] - data[(i - 1) * column + j]); + } + } + + return OK; +} + +int computeTotal(u8 *data, u8 main, int row, int column, int m, int n, u16 **result) +{ + int i, j; + int size = (row)*(column); + + *result = (u16 *) kmalloc(size * sizeof (u16), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s computeTotal : ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + *(*result + (i * column + j)) = m * main + n * data[i * column + j]; + } + } + + return OK; +} + +int checkLimitsMinMax(short *data, int row, int column, int min, int max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] < min || data[i * column + j] > max) { + logError(1, "%s checkLimitsMinMax: Node[%d,%d] = %d exceed limit [%d, %d]\n", tag, i, j, data[i * column + j], min, max); + count++; + } + } + } + + return count; /* if count is 0 = OK, test completed successfully */ +} + +int checkLimitsGap(short *data, int row, int column, int threshold) +{ + int i, j; + int min_node; + int max_node; + + if (row == 0 || column == 0) { + logError(1, "%s checkLimitsGap: invalid number of rows = %d or columns = %d ERROR %02\n", tag, row, column, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + min_node = data[0]; + max_node = data[0]; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] < min_node) { + min_node = data[i * column + j]; + } else { + if (data[i * column + j] > max_node) + max_node = data[i * column + j]; + } + } + } + + if (max_node - min_node > threshold) { + logError(1, "%s checkLimitsGap: GAP = %d exceed limit %d\n", tag, max_node - min_node, threshold); + return ERROR_TEST_CHECK_FAIL; + } else + return OK; + +} + +int checkLimitsMap(u8 *data, int row, int column, int *min, int *max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] < min[i * column + j] || data[i * column + j] > max[i * column + j]) { + logError(1, "%s checkLimitsMap: Node[%d,%d] = %d exceed limit [%d, %d]\n", tag, i, j, data[i * column + j], min[i * column + j], max[i * column + j]); + count++; + } + } + } + + return count; /* if count is 0 = OK, test completed successfully */ +} + +int checkLimitsMapTotal(u16 *data, int row, int column, int *min, int *max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] < min[i * column + j] || data[i * column + j] > max[i * column + j]) { + logError(1, "%s checkLimitsMapTotal: Node[%d,%d] = %d exceed limit [%d, %d]\n", tag, i, j, data[i * column + j], min[i * column + j], max[i * column + j]); + count++; + } + } + } + + return count; /* if count is 0 = OK, test completed successfully */ +} + +int checkLimitsMapAdj(u8 *data, int row, int column, int *max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] > max[i * column + j]) { + logError(1, "%s checkLimitsMapAdj: Node[%d,%d] = %d exceed limit > %d\n", tag, i, j, data[i * column + j], max[i * column + j]); + count++; + } + } + } + + return count; /* if count is 0 = OK, test completed successfully */ +} + +int checkLimitsMapAdjTotal(u16 *data, int row, int column, int *max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] > max[i * column + j]) { + logError(1, "%s checkLimitsMapAdjTotal: Node[%d,%d] = %d exceed limit > %d\n", tag, i, j, data[i * column + j], max[i * column + j]); + count++; + } + } + } + + return count; /* if count is 0 = OK, test completed successfully */ +} + +int production_test_ito(void) +{ + int res = OK; + u8 cmd; + u8 readData[FIFO_EVENT_SIZE]; + int eventToSearch[2] = {EVENTID_ERROR_EVENT, EVENT_TYPE_ITO}; /* look for ito event */ + + logError(0, "%s ITO Production test is starting...\n", tag); + + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s production_test_ito: ERROR %02X\n", tag, ERROR_PROD_TEST_ITO); + return (res | ERROR_PROD_TEST_ITO); + } + + cmd = FTS_CMD_ITO_CHECK; + + logError(0, "%s ITO Check command sent...\n", tag); + if (fts_writeFwCmd(&cmd, 1) < 0) { + logError(1, "%s production_test_ito: ERROR %02X\n", tag, (ERROR_I2C_W | ERROR_PROD_TEST_ITO)); + return (ERROR_I2C_W | ERROR_PROD_TEST_ITO); + } + + logError(0, "%s Looking for ITO Event...\n", tag); + res = pollForEvent(eventToSearch, 2, readData, TIMEOUT_ITO_TEST_RESULT); + if (res < 0) { + logError(1, "%s production_test_ito: ITO Production test failed... ERROR %02X\n", tag, ERROR_PROD_TEST_ITO); + return (res | ERROR_PROD_TEST_ITO); + } + + if (readData[2] != 0x00 || readData[3] != 0x00) { + logError(0, "%s ITO Production testes finished!.................FAILED ERROR %02X\n", tag, (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_ITO)); + res = (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_ITO); + } else { + logError(0, "%s ITO Production test finished!.................OK\n", tag); + res = OK; + } + + res |= fts_system_reset(); + if (res < 0) { + logError(1, "%s production_test_ito: ERROR %02X\n", tag, ERROR_PROD_TEST_ITO); + res = (res | ERROR_PROD_TEST_ITO); + } + return res; +} + +int production_test_initialization(void) +{ + int res; + u8 cmd; + u8 readData[FIFO_EVENT_SIZE]; + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, EVENT_TYPE_FULL_INITIALIZATION}; + + logError(0, "%s INITIALIZATION Production test is starting...\n", tag); + + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s production_test_initialization: ERROR %02X\n", tag, ERROR_PROD_TEST_INITIALIZATION); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + + logError(0, "%s INITIALIZATION command sent...\n", tag); + cmd = FTS_CMD_FULL_INITIALIZATION; + if (fts_writeFwCmd(&cmd, 1) < 0) { + logError(1, "%s production_test_initialization: ERROR %02X\n", tag, (ERROR_I2C_W | ERROR_PROD_TEST_INITIALIZATION)); + return (ERROR_I2C_W | ERROR_PROD_TEST_INITIALIZATION); + } + + logError(0, "%s Looking for INITIALIZATION Event...\n", tag); + res = pollForEvent(eventToSearch, 2, readData, TIMEOUT_INITIALIZATION_TEST_RESULT); + if (res < 0) { + logError(1, "%s production_test_initialization: INITIALIZATION Production test failed... ERROR %02X\n", tag, ERROR_PROD_TEST_INITIALIZATION); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + + if (readData[2] != 0x00) { + logError(0, "%s INITIALIZATION Production testes finished!.................FAILED ERROR %02X\n", tag, (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_INITIALIZATION)); + res = (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_INITIALIZATION); + } else { + logError(0, "%s INITIALIZATION Production test.................OK\n", tag); + res = OK; + } + + logError(0, "%s Refresh Chip Info...\n", tag); + res |= readChipInfo(1); + /* need to update the chipInfo in order to refresh the tuning_versione */ + + if (res < 0) { + logError(1, "%s production_test_initialization: read chip info ERROR %02X\n", tag, ERROR_PROD_TEST_INITIALIZATION); + res = (res | ERROR_PROD_TEST_INITIALIZATION); + } + + return res; + +} + +int ms_compensation_tuning(void) +{ + int res; + u8 cmd; + u8 readData[FIFO_EVENT_SIZE]; + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, EVENT_TYPE_MS_TUNING_CMPL}; + + logError(0, "%s MS INITIALIZATION command sent...\n", tag); + cmd = FTS_CMD_MS_COMP_TUNING; + if (fts_writeFwCmd(&cmd, 1) < 0) { + logError(1, "%s ms_compensation_tuning 2: ERROR %02X\n", tag, (ERROR_I2C_W | ERROR_MS_TUNING)); + return (ERROR_I2C_W | ERROR_MS_TUNING); + } + + logError(0, "%s Looking for MS INITIALIZATION Event...\n", tag); + res = pollForEvent(eventToSearch, 2, readData, TIMEOUT_INITIALIZATION_TEST_RESULT); + if (res < 0) { + logError(1, "%s ms_compensation_tuning: MS INITIALIZATION Production test failed... ERROR %02X\n", tag, ERROR_MS_TUNING); + return (res | ERROR_MS_TUNING); + } + + if (readData[2] != 0x00 || readData[3] != 0x00) { + logError(0, "%s MS INITIALIZATION Production test finished!.................FAILED ERROR %02X\n", tag, ERROR_MS_TUNING); + res = ERROR_MS_TUNING; + } else { + logError(0, "%s MS INITIALIZATION Production test finished!.................OK\n", tag); + res = OK; + } + + return res; +} + +int ss_compensation_tuning(void) +{ + int res; + u8 cmd; + u8 readData[FIFO_EVENT_SIZE]; + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, EVENT_TYPE_SS_TUNING_CMPL}; + + logError(0, "%s SS INITIALIZATION command sent...\n", tag); + cmd = FTS_CMD_SS_COMP_TUNING; + if (fts_writeFwCmd(&cmd, 1) < 0) { + logError(1, "%s ss_compensation_tuning 2: ERROR %02X\n", tag, (ERROR_I2C_W | ERROR_SS_TUNING)); + return (ERROR_I2C_W | ERROR_SS_TUNING); + } + + logError(0, "%s Looking for SS INITIALIZATION Event...\n", tag); + res = pollForEvent(eventToSearch, 2, readData, TIMEOUT_INITIALIZATION_TEST_RESULT); + if (res < 0) { + logError(1, "%s ms_compensation_tuning: SS INITIALIZATION Production test failed... ERROR %02X\n", tag, ERROR_SS_TUNING); + return (res | ERROR_SS_TUNING); + } + + if (readData[2] != 0x00 || readData[3] != 0x00) { + logError(0, "%s SS INITIALIZATION Production test finished!.................FAILED ERROR %02X\n", tag, ERROR_SS_TUNING); + res = ERROR_SS_TUNING; + } else { + logError(0, "%s SS INITIALIZATION Production test finished!.................OK\n", tag); + res = OK; + } + + return res; +} + +int lp_timer_calibration(void) +{ + int res; + u8 cmd; + u8 readData[FIFO_EVENT_SIZE]; + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, EVENT_TYPE_LPTIMER_TUNING_CMPL}; + + logError(0, "%s LP TIMER CALIBRATION command sent...\n", tag); + cmd = FTS_CMD_LP_TIMER_CALIB; + if (fts_writeFwCmd(&cmd, 1) < 0) { + logError(1, "%s lp_timer_calibration 2: ERROR %02X\n", tag, (ERROR_I2C_W | ERROR_LP_TIMER_TUNING)); + return (ERROR_I2C_W | ERROR_LP_TIMER_TUNING); + } + + logError(0, "%s Looking for LP TIMER CALIBRATION Event...\n", tag); + res = pollForEvent(eventToSearch, 2, readData, TIMEOUT_INITIALIZATION_TEST_RESULT); + if (res < 0) { + logError(1, "%s lp_timer_calibration: LP TIMER CALIBRATION Production test failed... ERROR %02X\n", tag, ERROR_LP_TIMER_TUNING); + return (res | ERROR_LP_TIMER_TUNING); + } + + if (readData[2] != 0x00 || readData[3] != 0x01) { + logError(0, "%s LP TIMER CALIBRATION Production test finished!.................FAILED ERROR %02X\n", tag, ERROR_LP_TIMER_TUNING); + res = ERROR_LP_TIMER_TUNING; + } else { + logError(0, "%s LP TIMER CALIBRATION Production test finished!.................OK\n", tag); + res = OK; + } + + return res; +} + +int save_cx_tuning(void) +{ + int res; + u8 cmd; + u8 readData[FIFO_EVENT_SIZE]; + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, EVENT_TYPE_COMP_DATA_SAVED}; + + logError(0, "%s SAVE CX command sent...\n", tag); + cmd = FTS_CMD_SAVE_CX_TUNING; + if (fts_writeCmd(&cmd, 1) < 0) { + logError(1, "%s save_cx_tuning 2: ERROR %02X\n", tag, (ERROR_I2C_W | ERROR_SAVE_CX_TUNING)); + return (ERROR_I2C_W | ERROR_SAVE_CX_TUNING); + } + + logError(0, "%s Looking for SAVE CX Event...\n", tag); + res = pollForEvent(eventToSearch, 2, readData, TIMEOUT_INITIALIZATION_TEST_RESULT); + if (res < 0) { + logError(1, "%s save_cx_tuning: SAVE CX failed... ERROR %02X\n", tag, ERROR_SAVE_CX_TUNING); + return (res | ERROR_SAVE_CX_TUNING); + } + + if (readData[2] != 0x00 || readData[3] != 0x00) { + logError(0, "%s SAVE CX finished!.................FAILED ERROR %02X\n", tag, ERROR_SAVE_CX_TUNING); + res = ERROR_SAVE_CX_TUNING; + } else { + logError(0, "%s SAVE CX finished!.................OK\n", tag); + res = OK; + } + + return res; +} + +int production_test_splited_initialization(int saveToFlash) +{ + int res; + + logError(0, "%s Splitted Initialization test is starting...\n", tag); + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s production_test_initialization: ERROR %02X\n", tag, ERROR_PROD_TEST_INITIALIZATION); + logError(0, "%s LP INITIALIZATION TEST:\n", tag); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + + logError(0, "%s MS INITIALIZATION TEST:\n", tag); + res = ms_compensation_tuning(); + if (res < 0) { + logError(0, "%s production_test_splited_initialization: MS INITIALIZATION TEST FAILED! ERROR %02X\n", tag, ERROR_PROD_TEST_INITIALIZATION); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + logError(0, "%s MS INITIALIZATION TEST OK!\n", tag); + + logError(0, "%s\n", tag); + + logError(0, "%s SS INITIALIZATION TEST:\n", tag); + res = ss_compensation_tuning(); + if (res < 0) { + logError(0, "%s production_test_splited_initialization: SS INITIALIZATION TEST FAILED! ERROR %02X\n", tag, ERROR_PROD_TEST_INITIALIZATION); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + logError(0, "%s SS INITIALIZATION TEST OK!\n", tag); + + logError(0, "%s\n", tag); + + logError(0, "%s LP INITIALIZATION TEST:\n", tag); + res = lp_timer_calibration(); + if (res < 0) { + logError(0, "%s production_test_splited_initialization: LP INITIALIZATION TEST FAILED! ERROR %02X\n", tag, ERROR_PROD_TEST_INITIALIZATION); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + logError(0, "%s LP INITIALIZATION TEST OK!\n", tag); + if (saveToFlash) { + logError(0, "%s\n", tag); + logError(0, "%s SAVE CX TEST:\n", tag); + res = save_cx_tuning(); + if (res < 0) { + logError(0, "%s production_test_splited_initialization: SAVE CX TEST FAILED! ERROR %02X\n", tag, res); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + logError(0, "%s SAVE CX TEST OK!\n", tag); + } + logError(0, "%s Refresh Chip Info...\n", tag); + res |= readChipInfo(1); + if (res < 0) { + logError(1, "%s production_test_initialization: read chip info ERROR %02X\n", tag, ERROR_PROD_TEST_INITIALIZATION); + res = (res | ERROR_PROD_TEST_INITIALIZATION); + } else + logError(0, "%s Splited Initialization test finished!.................OK\n", tag); + return res; +} + +int production_test_main(char *pathThresholds, int stop_on_fail, + int saveInit, TestToDo *todo, u32 signature) +{ + int res, ret; + + logError(0, "%s MAIN Production test is starting...\n", tag); + + logError(0, "%s\n", tag); + + logError(0, "%s ITO TEST:\n", tag); + res = production_test_ito(); + if (res < 0) { + logError(0, "%s Error during ITO TEST! ERROR %02X\n", tag, (u8) res); + goto END; /* in case of ITO TEST failure is no sense keep going */ + } else { + logError(0, "%s ITO TEST OK!\n", tag); + } + + logError(0, "%s\n", tag); + + logError(0, "%s INITIALIZATION TEST :\n", tag); + if (saveInit == 1) { + res = production_test_initialization(); + if (res < 0) { + logError(0, "%s Error during INITIALIZATION TEST! ERROR %02X\n", tag, (u8) res); + if (stop_on_fail) + goto END; + } else { + logError(0, "%s INITIALIZATION TEST OK!\n", tag); + } + } else + logError(0, "%s INITIALIZATION TEST :................. SKIPPED\n", tag); + + logError(0, "%s\n", tag); + + if (saveInit == 1) { + logError(0, "%s Cleaning up...\n", tag); + ret = cleanUp(0); + if (ret < 0) { + logError(1, "%s production_test_main: clean up ERROR %02X\n", tag, ret); + res |= ret; + if (stop_on_fail) + goto END; + } + logError(0, "%s\n", tag); + } + + logError(0, "%s PRODUCTION DATA TEST:\n", tag); + ret = production_test_data(pathThresholds, stop_on_fail, todo); + if (ret < 0) { + logError(0, "%s Error during PRODUCTION DATA TEST! ERROR %02X\n", tag, ret); + } else { + logError(0, "%s PRODUCTION DATA TEST OK!\n", tag); + } + + res |= ret; + /* the OR is important because if the data test is OK but the inizialization test fail, + * the main production test result should = FAIL + */ + + if (ret == OK && saveInit == 1) { + logError(0, "%s SAVE FLAG:\n", tag); + ret = save_mp_flag(signature); + if (ret < OK) + logError(0, "%s SAVE FLAG:................. FAIL! ERROR %08X\n", tag, ret); + else + logError(0, "%s SAVE FLAG:................. OK!\n", tag); + res |= ret; + } + + logError(0, "%s\n", tag); +END: + if (res < 0) { + logError(0, "%s MAIN Production test finished.................FAILED\n", tag); + return res; + } + logError(0, "%s MAIN Production test finished.................OK\n", tag); + return OK; +} + +int production_test_ms_raw(char *path_limits, int stop_on_fail, TestToDo *todo) +{ + + int ret, count_fail = 0; + MutualSenseFrame msRawFrame; + + int *thresholds = NULL; + int trows, tcolumns; + + /*********************** Mutual Sense Test **********************/ + logError(0, "%s\n", tag); + logError(0, "%s MS RAW DATA TEST is starting...\n", tag); + if (todo->MutualRaw == 1 || todo->MutualRawGap == 1) { + + ret = getMSFrame2(MS_TOUCH_ACTIVE, &msRawFrame); + if (ret < 0) { + logError(1, "%s production_test_data: getMSFrame failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s MS RAW MIN MAX TEST:\n", tag); + if (todo->MutualRaw == 1) { + ret = parseProductionTestLimits(path_limits, MS_RAW_MIN_MAX, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_RAW_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMinMax(msRawFrame.node_data, msRawFrame.header.force_node, msRawFrame.header.sense_node, thresholds[0], thresholds[1]); + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMinMax MS RAW failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s MS RAW MIN MAX TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail == 1) + goto ERROR; + } + kfree(thresholds); + logError(0, "%s MS RAW MIN MAX TEST:.................OK\n", tag); + } else + logError(0, "%s MS RAW MIN MAX TEST:.................SKIPPED\n", tag); + + logError(0, "%s\n", tag); + logError(0, "%s MS RAW GAP TEST:\n", tag); + if (todo->MutualRawGap == 1) { + ret = parseProductionTestLimits(path_limits, MS_RAW_GAP, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_RAW_GAP failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsGap(msRawFrame.node_data, msRawFrame.header.force_node, msRawFrame.header.sense_node, thresholds[0]); + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsGap MS RAW failed... ERROR = %02X\n", tag, ret); + count_fail += 1; + if (stop_on_fail == 1) + goto ERROR; + + } else + logError(0, "%s MS RAW GAP TEST:.................OK\n\n", tag); + kfree(thresholds); + } else + logError(0, "%s MS RAW GAP TEST:.................SKIPPED\n", tag); + + kfree(msRawFrame.node_data); + } else + logError(0, "%s MS RAW FRAME TEST:.................SKIPPED\n", tag); + + logError(0, "%s\n", tag); + logError(0, "%s MS KEY RAW TEST:\n", tag); + if (todo->MutualKeyRaw == 1) { + ret = production_test_ms_key_raw(path_limits); + if (ret < 0) { + logError(1, "%s production_test_data: production_test_ms_key_raw failed... ERROR = %02X\n", tag, ret); + count_fail += 1; + } + } else + logError(0, "%s MS KEY RAW TEST:.................SKIPPED\n", tag); + +ERROR: + logError(0, "%s\n", tag); + if (count_fail == 0) { + logError(0, "%s MS RAW DATA TEST finished!.................OK\n", tag); + return OK; + } + print_frame_short("MS Raw frame =", array1dTo2d_short(msRawFrame.node_data, msRawFrame.node_data_size, msRawFrame.header.sense_node), msRawFrame.header.force_node, msRawFrame.header.sense_node); + logError(0, "%s MS RAW DATA TEST:.................FAIL fails_count = %d\n\n", tag, count_fail); + return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); +} + +int production_test_ms_key_raw(char *path_limits) +{ + + int ret; + MutualSenseFrame msRawFrame; + + int *thresholds = NULL; + int trows, tcolumns; + + /******************************* Mutual Sense Test *******************************/ + logError(0, "%s MS KEY RAW DATA TEST is starting...\n", tag); + + ret = getMSFrame2(MS_KEY, &msRawFrame); + if (ret < 0) { + logError(1, "%s production_test_data: getMSKeyFrame failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, MS_KEY_RAW_MIN_MAX, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_KEY_RAW_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMinMax(msRawFrame.node_data, msRawFrame.header.force_node, msRawFrame.header.sense_node, thresholds[0], thresholds[1]); + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMinMax MS KEY RAW failed... ERROR COUNT = %d\n", tag, ret); + goto ERROR; + } else + logError(0, "%s MS KEY RAW TEST:.................OK\n\n", tag); + + kfree(thresholds); + + kfree(msRawFrame.node_data); + + return OK; + +ERROR: + print_frame_short("MS Key Raw frame =", array1dTo2d_short(msRawFrame.node_data, msRawFrame.node_data_size, msRawFrame.header.sense_node), msRawFrame.header.force_node, msRawFrame.header.sense_node); + logError(0, "%s MS KEY RAW TEST:.................FAIL\n\n", tag); + return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); + +} + +int production_test_ms_cx(char *path_limits, int stop_on_fail, TestToDo *todo) +{ + + int ret; + int count_fail = 0; + + int *thresholds = NULL; + int *thresholds_min = NULL; + int *thresholds_max = NULL; + int trows, tcolumns; + + MutualSenseData msCompData; + + u8 *adjhor = NULL; + + u8 *adjvert = NULL; + + u16 container; + u16 *total_cx = NULL; + u16 *total_adjhor = NULL; + u16 *total_adjvert = NULL; + + /* MS CX TEST */ + logError(0, "%s\n", tag); + logError(0, "%s MS CX Testes are starting...\n", tag); + + ret = readMutualSenseCompensationData(MS_TOUCH_ACTIVE, &msCompData); /* read MS compensation data */ + if (ret < 0) { + logError(1, "%s production_test_data: readMutualSenseCompensationData failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s MS CX1 TEST:\n", tag); + if (todo->MutualCx1 == 1) { + + ret = parseProductionTestLimits(path_limits, MS_CX1_MIN_MAX, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_CX1_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + container = (u16) msCompData.cx1; + ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], thresholds[1]); /* check the limits */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMinMax MS CX1 failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s MS CX1 TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS CX1 TEST:.................OK\n\n", tag); + } else + logError(0, "%s MS CX1 TEST:.................SKIPPED\n\n", tag); + + kfree(thresholds); + + logError(0, "%s MS CX2 MIN MAX TEST:\n", tag); + if (todo->MutualCx2 == 1) { + ret = parseProductionTestLimits(path_limits, MS_CX2_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load min thresholds */ + if (ret < 0 || (trows != msCompData.header.force_node || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_CX2_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, MS_CX2_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load max thresholds */ + if (ret < 0 || (trows != msCompData.header.force_node || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_CX2_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMap(msCompData.node_data, msCompData.header.force_node, msCompData.header.sense_node, thresholds_min, thresholds_max); /* check the limits */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap MS CX2 MIN MAX failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s MS CX2 MIN MAX TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS CX2 MIN MAX TEST:.................OK\n\n", tag); + + kfree(thresholds_min); + kfree(thresholds_max); + } else + logError(0, "%s MS CX2 MIN MAX TEST:.................SKIPPED\n\n", tag); + + logError(0, "%s MS CX2 ADJ TEST:\n", tag); + if (todo->MutualCx2Adj == 1) { + /* MS CX2 ADJ HORIZ */ + logError(0, "%s MS CX2 ADJ HORIZ TEST:\n", tag); + + ret = computeAdjHoriz(msCompData.node_data, msCompData.header.force_node, msCompData.header.sense_node, &adjhor); + if (ret < 0) { + logError(1, "%s production_test_data: computeAdjHoriz failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s MS CX2 ADJ HORIZ computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, MS_CX2_ADJH_MAP_MAX, &thresholds_max, &trows, &tcolumns); + if (ret < 0 || (trows != msCompData.header.force_node || tcolumns != msCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_CX2_ADJH_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapAdj(adjhor, msCompData.header.force_node, msCompData.header.sense_node - 1, thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMapAdj CX2 ADJH failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s MS CX2 ADJ HORIZ TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS CX2 ADJ HORIZ TEST:.................OK\n\n", tag); + + kfree(thresholds_max); + kfree(adjhor); + + /* MS CX2 ADJ VERT */ + logError(0, "%s MS CX2 ADJ VERT TEST:\n", tag); + + ret = computeAdjVert(msCompData.node_data, msCompData.header.force_node, msCompData.header.sense_node, &adjvert); + if (ret < 0) { + logError(1, "%s production_test_data: computeAdjVert failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s MS CX2 ADJ VERT computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, MS_CX2_ADJV_MAP_MAX, &thresholds_max, &trows, &tcolumns); + if (ret < 0 || (trows != msCompData.header.force_node - 1 || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_CX2_ADJV_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapAdj(adjvert, msCompData.header.force_node - 1, msCompData.header.sense_node - 1, thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMapAdj CX2 ADJV failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s MS CX2 ADJ HORIZ TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS CX2 ADJ VERT TEST:.................OK\n\n", tag); + + kfree(thresholds_max); + kfree(adjvert); + } else + logError(0, "%s MS CX2 ADJ TEST:.................SKIPPED\n\n", tag); + + /* START OF TOTAL CHECK */ + logError(0, "%s MS TOTAL CX TEST:\n", tag); + + if (todo->MutualCxTotal == 1 || todo->MutualCxTotalAdj == 1) { + ret = computeTotal(msCompData.node_data, msCompData.cx1, msCompData.header.force_node, msCompData.header.sense_node, CX1_WEIGHT, CX2_WEIGHT, &total_cx); + if (ret < 0) { + logError(1, "%s production_test_data: computeTotalCx failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s MS TOTAL CX MIN MAX TEST:\n", tag); + if (todo->MutualCxTotal == 1) { + ret = parseProductionTestLimits(path_limits, MS_TOTAL_CX_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load min thresholds */ + if (ret < 0 || (trows != msCompData.header.force_node || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_TOTAL_CX_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, MS_TOTAL_CX_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load max thresholds */ + if (ret < 0 || (trows != msCompData.header.force_node || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_TOTAL_CX_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapTotal(total_cx, msCompData.header.force_node, msCompData.header.sense_node, thresholds_min, thresholds_max); /* check the limits */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap MS TOTAL CX TEST failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s MS TOTAL CX MIN MAX TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS TOTAL CX MIN MAX TEST:.................OK\n\n", tag); + + kfree(thresholds_min); + kfree(thresholds_max); + } else + logError(0, "%s MS TOTAL CX MIN MAX TEST:.................SKIPPED\n\n", tag); + + logError(0, "%s MS TOTAL CX ADJ TEST:\n", tag); + if (todo->MutualCxTotalAdj == 1) { + /* MS TOTAL CX ADJ HORIZ */ + logError(0, "%s MS TOTAL CX ADJ HORIZ TEST:\n", tag); + + /* thresholds_max = NULL; */ + ret = computeAdjHorizTotal(total_cx, msCompData.header.force_node, msCompData.header.sense_node, &total_adjhor); + if (ret < 0) { + logError(1, "%s production_test_data: computeAdjHoriz failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s MS TOTAL CX ADJ HORIZ computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, MS_TOTAL_CX_ADJH_MAP_MAX, &thresholds_max, &trows, &tcolumns); + if (ret < 0 || (trows != msCompData.header.force_node || tcolumns != msCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_TOTAL_CX_ADJH_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapAdjTotal(total_adjhor, msCompData.header.force_node, msCompData.header.sense_node - 1, thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMapAdj MS TOTAL CX ADJH failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s MS TOTAL CX ADJ HORIZ TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS TOTAL CX ADJ HORIZ TEST:.................OK\n\n", tag); + + kfree(thresholds_max); + kfree(total_adjhor); + + /* MS TOTAL CX ADJ VERT */ + logError(0, "%s MS TOTAL CX ADJ VERT TEST:\n", tag); + + ret = computeAdjVertTotal(total_cx, msCompData.header.force_node, msCompData.header.sense_node, &total_adjvert); + if (ret < 0) { + logError(1, "%s production_test_data: computeAdjVert failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s MS TOTAL CX ADJ VERT computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, MS_TOTAL_CX_ADJV_MAP_MAX, &thresholds_max, &trows, &tcolumns); + if (ret < 0 || (trows != msCompData.header.force_node - 1 || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_TOTAL_CX_ADJV_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapAdjTotal(total_adjvert, msCompData.header.force_node - 1, msCompData.header.sense_node - 1, thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMapAdj MS TOTAL CX ADJV failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s MS TOTAL CX ADJ HORIZ TEST:.................FAIL\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS TOTAL CX ADJ VERT TEST:.................OK\n", tag); + + kfree(thresholds_max); + kfree(total_adjvert); + } else + logError(0, "%s MS TOTAL CX ADJ TEST:.................SKIPPED\n", tag); + + kfree(total_cx); + } else + logError(0, "%s MS TOTAL CX TEST:.................SKIPPED\n", tag); + + kfree(msCompData.node_data); + + if ((todo->MutualKeyCx1 | todo->MutualKeyCx2 | todo->MutualKeyCxTotal) == 1) { + ret = production_test_ms_key_cx(path_limits, stop_on_fail, todo); + if (ret < 0) { + count_fail += 1; + logError(1, "%s production_test_data: production_test_ms_key_cx failed... ERROR = %02X\n", tag, ret); + logError(0, "%s MS CX testes finished!.................FAILED fails_count = %d\n\n", tag, count_fail); + return ret; + } + } else + logError(0, "%s MS KEY CX TEST:.................SKIPPED\n", tag); + +ERROR: + logError(0, "%s\n", tag); + if (count_fail == 0) { + logError(0, "%s MS CX testes finished!.................OK\n", tag); + return OK; + } + print_frame_u8("MS Init Data (Cx2) =", array1dTo2d_u8(msCompData.node_data, msCompData.node_data_size, msCompData.header.sense_node), msCompData.header.force_node, msCompData.header.sense_node); + logError(0, "%s MS CX testes finished!.................FAILED fails_count = %d\n\n", tag, count_fail); + return (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA); + +} + +int production_test_ms_key_cx(char *path_limits, int stop_on_fail, TestToDo *todo) +{ + + int ret; + int count_fail = 0; + int num_keys = 0; + + int *thresholds = NULL; + int *thresholds_min = NULL; + int *thresholds_max = NULL; + int trows, tcolumns; + + MutualSenseData msCompData; + + u16 container; + u16 *total_cx = NULL; + + /* MS CX TEST */ + logError(0, "%s MS KEY CX Testes are starting...\n", tag); + + ret = readMutualSenseCompensationData(MS_KEY, &msCompData); /* read MS compensation data */ + if (ret < 0) { + logError(1, "%s production_test_data: readMutualSenseCompensationData failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + if (msCompData.header.force_node > msCompData.header.sense_node) +/* the meaningful data are only in the first row, the other rows are only a copy of the first one */ + num_keys = msCompData.header.force_node; + else + num_keys = msCompData.header.sense_node; + + logError(0, "%s MS KEY CX1 TEST:\n", tag); + if (todo->MutualKeyCx1 == 1) { + + ret = parseProductionTestLimits(path_limits, MS_KEY_CX1_MIN_MAX, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_KEY_CX1_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + container = (u16) msCompData.cx1; + ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], thresholds[1]); /* check the limits */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMinMax MS CX1 failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s MS KEY CX1 TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS KEY CX1 TEST:.................OK\n\n", tag); + } else + logError(0, "%s MS KEY CX1 TEST:.................SKIPPED\n\n", tag); + + kfree(thresholds); + + logError(0, "%s MS KEY CX2 TEST:\n", tag); + if (todo->MutualKeyCx2 == 1) { + ret = parseProductionTestLimits(path_limits, MS_KEY_CX2_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load min thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != num_keys)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_KEY_CX2_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, MS_KEY_CX2_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != num_keys)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_KEY_CX2_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMap(msCompData.node_data, 1, num_keys, thresholds_min, thresholds_max); /* check the limits */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap MS KEY CX2 failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s MS KEY CX2 TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS KEY CX2 TEST:.................OK\n\n", tag); + + kfree(thresholds_min); + kfree(thresholds_max); + } else + logError(0, "%s MS CX2 TEST:.................SKIPPED\n\n", tag); + + /* START OF TOTAL CHECK */ + logError(0, "%s MS KEY TOTAL CX TEST:\n", tag); + + if (todo->MutualKeyCxTotal == 1) { + ret = computeTotal(msCompData.node_data, msCompData.cx1, 1, num_keys, CX1_WEIGHT, CX2_WEIGHT, &total_cx); + if (ret < 0) { + logError(1, "%s production_test_data: computeTotalCx failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, MS_KEY_TOTAL_CX_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load min thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != num_keys)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_KEY_TOTAL_CX_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, MS_KEY_TOTAL_CX_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != num_keys)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_KEY_TOTAL_CX_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapTotal(total_cx, 1, num_keys, thresholds_min, thresholds_max); /* check the limits */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap MS TOTAL KEY CX TEST failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s MS KEY TOTAL CX TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS KEY TOTAL CX TEST:.................OK\n\n", tag); + + kfree(thresholds_min); + kfree(thresholds_max); + + kfree(total_cx); + } else + logError(0, "%s MS KEY TOTAL CX TEST:.................SKIPPED\n", tag); + + kfree(msCompData.node_data); +ERROR: + logError(0, "%s\n", tag); + if (count_fail == 0) { + logError(0, "%s MS KEY CX testes finished!.................OK\n", tag); + return OK; + } + print_frame_u8("MS Key Init Data (Cx2) =", array1dTo2d_u8(msCompData.node_data, msCompData.node_data_size, msCompData.header.sense_node), 1, msCompData.header.sense_node); + logError(0, "%s MS Key CX testes finished!.................FAILED fails_count = %d\n\n", tag, count_fail); + return (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA); +} + +int production_test_ss_raw(char *path_limits, int stop_on_fail, TestToDo *todo) +{ + int ret; + int count_fail = 0; + int rows, columns; + + /* short *ssRawFrame = NULL; */ + SelfSenseFrame ssRawFrame; + + int *thresholds = NULL; + int trows, tcolumns; + + /* MS SS TEST */ + logError(0, "%s\n", tag); + logError(0, "%s SS RAW Testes are starting...\n", tag); + + /******************************* Self Sense Test *******************************/ + + logError(0, "%s Getting SS Frame...\n", tag); + ret = getSSFrame2(SS_TOUCH, &ssRawFrame); + if (ret < 0) { + logError(1, "%s production_test_data: getSSFrame failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + /* SS RAW (PROXIMITY) FORCE TEST */ + logError(0, "%s SS RAW (PROXIMITY) FORCE TEST:\n", tag); + + if (todo->SelfForceRaw == 1 || todo->SelfForceRawGap == 1) { + + columns = 1; + /* there are no data for the sense channels due to the fact that the force frame is analized */ + rows = ssRawFrame.header.force_node; + + logError(0, "%s SS RAW (PROXIMITY) FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceRaw == 1) { + + ret = parseProductionTestLimits(path_limits, SS_RAW_FORCE_MIN_MAX, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_RAW_FORCE_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMinMax(ssRawFrame.force_data, rows, columns, thresholds[0], thresholds[1]); + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMinMax SS RAW (PROXIMITY) FORCE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS RAW (PROXIMITY) FORCE MIN MAX TEST:.................FAIL\n\n", tag); + count_fail += 1; + print_frame_short("SS Raw force frame =", array1dTo2d_short(ssRawFrame.force_data, rows*columns, columns), rows, columns); + if (stop_on_fail) + return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); + } else + logError(0, "%s SS RAW (PROXIMITY) FORCE MIN MAX TEST:.................OK\n\n", tag); + + kfree(thresholds); + } else + logError(0, "%s SS RAW (PROXIMITY) FORCE MIN MAX TEST:.................SKIPPED\n\n", tag); + + logError(0, "%s\n", tag); + logError(0, "%s SS RAW (PROXIMITY) FORCE GAP TEST:\n", tag); + if (todo->SelfForceRawGap == 1) { + + ret = parseProductionTestLimits(path_limits, SS_RAW_FORCE_GAP, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_RAW_FORCE_GAP failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsGap(ssRawFrame.force_data, rows, columns, thresholds[0]); + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsGap SS RAW (PROXIMITY) FORCE GAP failed... ERROR = %02X\n", tag, ret); + logError(0, "%s SS RAW (PROXIMITY) FORCE GAP TEST:.................FAIL\n\n", tag); + count_fail += 1; + print_frame_short("SS Raw force frame =", array1dTo2d_short(ssRawFrame.force_data, rows*columns, columns), rows, columns); + if (stop_on_fail) + return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); + } else + logError(0, "%s SS RAW (PROXIMITY) FORCE GAP TEST:.................OK\n\n", tag); + + kfree(thresholds); + } else + logError(0, "%s SS RAW (PROXIMITY) FORCE GAP TEST:.................SKIPPED\n\n", tag); + + kfree(ssRawFrame.force_data); + } else + logError(0, "%s SS RAW (PROXIMITY) FORCE TEST:.................SKIPPED\n\n", tag); + + logError(0, "%s\n", tag); + /* SS RAW (PROXIMITY) SENSE TEST */ + logError(0, "%s SS RAW (PROXIMITY) SENSE TEST:\n", tag); + + if (todo->SelfSenseRaw == 1 || todo->SelfSenseRawGap == 1) { + columns = ssRawFrame.header.sense_node; + rows = 1; /* there are no data for the force channels due to the fact that the sense frame is analized */ + + logError(0, "%s SS RAW (PROXIMITY) SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseRaw == 1) { + ret = parseProductionTestLimits(path_limits, SS_RAW_SENSE_MIN_MAX, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_RAW_SENSE_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMinMax(ssRawFrame.sense_data, rows, columns, thresholds[0], thresholds[1]); + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMinMax SS RAW (PROXIMITY) SENSE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS RAW (PROXIMITY) SENSE MIN MAX TEST:.................FAIL\n", tag); + count_fail += 1; + print_frame_short("SS Raw sense frame =", array1dTo2d_short(ssRawFrame.sense_data, rows*columns, columns), rows, columns); + if (stop_on_fail) + return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); + } else + logError(0, "%s SS RAW (PROXIMITY) SENSE MIN MAX TEST:.................OK\n", tag); + + kfree(thresholds); + } else + logError(0, "%s SS RAW (PROXIMITY) SENSE MIN MAX TEST:.................SKIPPED\n", tag); + + logError(0, "%s\n", tag); + logError(0, "%s SS RAW (PROXIMITY) SENSE GAP TEST:\n", tag); + if (todo->SelfSenseRawGap == 1) { + ret = parseProductionTestLimits(path_limits, SS_RAW_SENSE_GAP, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_RAW_SENSE_GAP failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsGap(ssRawFrame.sense_data, rows, columns, thresholds[0]); + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsGap SS RAW (PROXIMITY) SENSE GAP failed... ERROR = %02X\n", tag, ret); + logError(0, "%s SS RAW (PROXIMITY) SENSE GAP TEST:.................FAIL\n", tag); + count_fail += 1; + print_frame_short("SS Raw sense frame =", array1dTo2d_short(ssRawFrame.sense_data, rows*columns, columns), rows, columns); + if (stop_on_fail) + return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); + } else + logError(0, "%s SS RAW (PROXIMITY) SENSE GAP TEST:.................OK\n", tag); + + kfree(thresholds); + } else + logError(0, "%s SS RAW (PROXIMITY) SENSE GAP TEST:.................SKIPPED\n", tag); + + kfree(ssRawFrame.sense_data); + } + + logError(0, "%s\n", tag); + if (count_fail == 0) { + logError(0, "%s SS RAW testes finished!.................OK\n\n", tag); + return OK; + } + logError(0, "%s SS RAW testes finished!.................FAILED fails_count = %d\n\n", tag, count_fail); + return (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA); +} + +int production_test_ss_ix_cx(char *path_limits, int stop_on_fail, TestToDo *todo) +{ + + int ret; + int count_fail = 0; + + int *thresholds = NULL; + int trows, tcolumns; + int *thresholds_min = NULL; + int *thresholds_max = NULL; + + SelfSenseData ssCompData; + + u8 *adjhor = NULL; + u8 *adjvert = NULL; + + u16 container; + int *ix1_w, *ix2_w; + u16 *total_ix = NULL; + u16 *total_cx = NULL; + + u16 *total_adjhor = NULL; + u16 *total_adjvert = NULL; + + logError(0, "%s\n", tag); + logError(0, "%s SS IX CX testes are starting...\n", tag); + ret = readSelfSenseCompensationData(SS_TOUCH, &ssCompData); /* read the SS compensation data */ + if (ret < 0) { + logError(1, "%s production_test_data: readSelfSenseCompensationData failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + /*************************** SS FORCE IX *************************************/ + /* SS IX1 FORCE TEST */ + logError(0, "%s SS IX1 FORCE TEST:\n", tag); + if (todo->SelfForceIx1 == 1) { + + ret = parseProductionTestLimits(path_limits, SS_IX1_FORCE_MIN_MAX, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX1_FORCE_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + container = (u16) ssCompData.f_ix1; + ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], thresholds[1]); /* check the limits */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMinMax SS IX1 FORCE TEST failed... ERROR COUNT = %d\n", tag, ret); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS IX1 FORCE TEST:.................OK\n\n", tag); + } else + logError(0, "%s SS IX1 FORCE TEST:.................SKIPPED\n\n", tag); + + kfree(thresholds); + /* SS IX2 FORCE TEST */ + logError(0, "%s SS IX2 FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceIx2 == 1) { + ret = parseProductionTestLimits(path_limits, SS_IX2_FORCE_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load the min thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX2_FORCE_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, SS_IX2_FORCE_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX2_FORCE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMap(ssCompData.ix2_fm, ssCompData.header.force_node, 1, thresholds_min, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap SS IX2 FORCE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS IX2 FORCE MIN MAX TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS IX2 FORCE MIN MAX TEST:.................OK\n\n", tag); + + kfree(thresholds_min); + kfree(thresholds_max); + } else + logError(0, "%s SS IX2 FORCE MIN MAX TEST:.................SKIPPED\n\n", tag); + + logError(0, "%s SS IX2 FORCE ADJ TEST:\n", tag); + if (todo->SelfForceIx2Adj == 1) { + /* SS IX2 FORCE ADJV TEST */ + logError(0, "%s SS IX2 FORCE ADJVERT TEST:\n", tag); + ret = computeAdjVert(ssCompData.ix2_fm, ssCompData.header.force_node, 1, &adjvert); + if (ret < 0) { + logError(1, "%s production_test_data: computeAdjVert SS IX2 FORCE ADJV failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s SS IX2 FORCE ADJV computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, SS_IX2_FORCE_ADJV_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node - 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX2_FORCE_ADJV_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapAdj(adjvert, ssCompData.header.force_node - 1, 1, thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap SS IX2 FORCE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS IX2 FORCE ADJV TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS IX2 FORCE ADJV TEST:.................OK\n\n", tag); + + kfree(thresholds_max); + kfree(adjvert); + + } else + logError(0, "%s SS IX2 FORCE ADJ TEST:.................SKIPPED\n\n", tag); + + /* SS TOTAL FORCE IX */ + logError(0, "%s SS TOTAL IX FORCE TEST:\n", tag); + if (todo->SelfForceIxTotal == 1 || todo->SelfForceIxTotalAdj == 1) { + logError(0, "%s Reading TOTAL IX FORCE Weights...\n", tag); + ret = parseProductionTestLimits(path_limits, SS_IX1_FORCE_W, &ix1_w, &trows, &tcolumns); /* load the IX1 weight */ + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX1_FORCE_W failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, SS_IX2_FORCE_W, &ix2_w, &trows, &tcolumns); /* load the IX2 weight */ + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX1_FORCE_W failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s Weights: IX1_W = %d IX2_W = %d\n", tag, *ix1_w, *ix2_w); + + ret = computeTotal(ssCompData.ix2_fm, ssCompData.f_ix1, ssCompData.header.force_node, 1, *ix1_w, *ix2_w, &total_ix); + if (ret < 0) { + logError(1, "%s production_test_data: computeTotal Ix Force failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s SS TOTAL IX FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceIxTotal == 1) { + ret = parseProductionTestLimits(path_limits, SS_TOTAL_IX_FORCE_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load the min thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_FORCE_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, SS_TOTAL_IX_FORCE_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_FORCE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapTotal(total_ix, ssCompData.header.force_node, 1, thresholds_min, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap SS TOTAL IX FORCE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS TOTAL IX FORCE MIN MAX TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS TOTAL IX FORCE MIN MAX TEST:.................OK\n\n", tag); + + kfree(thresholds_min); + kfree(thresholds_max); + } else + logError(0, "%s SS TOTAL IX FORCE MIN MAX TEST:.................SKIPPED\n", tag); + + logError(0, "%s SS TOTAL IX FORCE ADJ TEST:\n", tag); + if (todo->SelfForceIxTotalAdj == 1) { + /* SS TOTAL IX FORCE ADJV TEST */ + logError(0, "%s SS TOTAL IX FORCE ADJVERT TEST:\n", tag); + ret = computeAdjVertTotal(total_ix, ssCompData.header.force_node, 1, &total_adjvert); + if (ret < 0) { + logError(1, "%s production_test_data: computeAdjVert SS TOTAL IX FORCE ADJV failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s SS TOTAL IX FORCE ADJV computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, SS_TOTAL_IX_FORCE_ADJV_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node - 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_FORCE_ADJV_MAP_MAX... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapAdjTotal(total_adjvert, ssCompData.header.force_node - 1, 1, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap SS TOTAL IX FORCE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS TOTAL IX FORCE ADJV TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS TOTAL IX FORCE ADJV TEST:.................OK\n\n", tag); + + kfree(thresholds_max); + kfree(total_adjvert); + } else + logError(0, "%s SS TOTAL IX FORCE ADJ TEST:.................SKIPPED\n", tag); + + kfree(total_ix); + } else + logError(0, "%s SS TOTAL IX FORCE TEST:.................SKIPPED\n\n", tag); + + /******************* SS SENSE IX **************************/ + /* SS IX1 SENSE TEST */ + logError(0, "%s SS IX1 SENSE TEST:\n", tag); + if (todo->SelfSenseIx1 == 1) { + + ret = parseProductionTestLimits(path_limits, SS_IX1_SENSE_MIN_MAX, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX1_SENSE_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + container = (u16) ssCompData.s_ix1; + ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], thresholds[1]); /* check the limits */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMinMax SS IX1 SENSE TEST failed... ERROR COUNT = %d\n", tag, ret); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS IX1 SENSE TEST:.................OK\n\n", tag); + } else + logError(0, "%s SS IX1 SENSE TEST:.................SKIPPED\n\n", tag); + + kfree(thresholds); + /* SS IX2 SENSE TEST */ + logError(0, "%s SS IX2 SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseIx2 == 1) { + ret = parseProductionTestLimits(path_limits, SS_IX2_SENSE_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load the min thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX2_SENSE_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, SS_IX2_SENSE_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX2_SENSE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMap(ssCompData.ix2_sn, 1, ssCompData.header.sense_node, thresholds_min, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap SS IX2 SENSE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS IX2 SENSE MIN MAX TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS IX2 SENSE MIN MAX TEST:.................OK\n\n", tag); + + kfree(thresholds_min); + kfree(thresholds_max); + } else + logError(0, "%s SS IX2 SENSE MIN MAX TEST:.................SKIPPED\n\n", tag); + + logError(0, "%s SS IX2 SENSE ADJ TEST:\n", tag); + if (todo->SelfSenseIx2Adj == 1) { + /* SS IX2 SENSE ADJH TEST */ + logError(0, "%s SS IX2 SENSE ADJHORIZ TEST:\n", tag); + ret = computeAdjHoriz(ssCompData.ix2_sn, 1, ssCompData.header.sense_node, &adjhor); + if (ret < 0) { + logError(1, "%s production_test_data: computeAdjHoriz SS IX2 SENSE ADJH failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s SS IX2 SENSE ADJ HORIZ computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, SS_IX2_SENSE_ADJH_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX2_SENSE_ADJH_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapAdj(adjhor, 1, ssCompData.header.sense_node - 1, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMapAdj SS IX2 SENSE ADJH failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS IX2 SENSE ADJH TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS IX2 SENSE ADJH TEST:.................OK\n\n", tag); + + kfree(thresholds_max); + kfree(adjhor); + } else + logError(0, "%s SS IX2 SENSE ADJ TEST:.................SKIPPED\n", tag); + + /* SS TOTAL IX SENSE */ + logError(0, "%s SS TOTAL IX SENSE TEST:\n", tag); + if (todo->SelfSenseIxTotal == 1 || todo->SelfSenseIxTotalAdj == 1) { + logError(0, "%s Reading TOTAL IX SENSE Weights...\n", tag); + ret = parseProductionTestLimits(path_limits, SS_IX1_SENSE_W, &ix1_w, &trows, &tcolumns); /* load the IX1 weight */ + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX1_SENSE_W failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, SS_IX2_SENSE_W, &ix2_w, &trows, &tcolumns); /* load the IX2 weight */ + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX1_SENSE_W failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s Weights: IX1_W = %d IX2_W = %d\n", tag, *ix1_w, *ix2_w); + + ret = computeTotal(ssCompData.ix2_sn, ssCompData.s_ix1, 1, ssCompData.header.sense_node, *ix1_w, *ix2_w, &total_ix); + if (ret < 0) { + logError(1, "%s production_test_data: computeTotal Ix Sense failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s SS TOTAL IX SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseIxTotal == 1) { + ret = parseProductionTestLimits(path_limits, SS_TOTAL_IX_SENSE_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load the min thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_SENSE_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, SS_TOTAL_IX_SENSE_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_SENSE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapTotal(total_ix, 1, ssCompData.header.sense_node, thresholds_min, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap SS TOTAL IX SENSE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS TOTAL IX SENSE MIN MAX TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS TOTAL IX SENSE MIN MAX TEST:.................OK\n\n", tag); + + kfree(thresholds_min); + kfree(thresholds_max); + } else + logError(0, "%s SS TOTAL IX SENSE MIN MAX TEST:.................SKIPPED\n", tag); + + logError(0, "%s SS TOTAL IX SENSE ADJ TEST:\n", tag); + if (todo->SelfSenseIxTotalAdj == 1) { + /* SS TOTAL IX SENSE ADJH TEST */ + logError(0, "%s SS TOTAL IX SENSE ADJHORIZ TEST:\n", tag); + ret = computeAdjHorizTotal(total_ix, 1, ssCompData.header.sense_node, &total_adjhor); + if (ret < 0) { + logError(1, "%s production_test_data: computeAdjHoriz SS TOTAL IX SENSE ADJH failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s SS TOTAL IX SENSE ADJ HORIZ computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, SS_TOTAL_IX_SENSE_ADJH_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_SENSE_ADJH_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapAdjTotal(total_adjhor, 1, ssCompData.header.sense_node - 1, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMapAdj SS TOTAL IX SENSE ADJH failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS TOTAL IX SENSE ADJH TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS TOTAL IX SENSE ADJH TEST:.................OK\n\n", tag); + + kfree(thresholds_max); + kfree(total_adjhor); + } else + logError(0, "%s SS TOTAL IX SENSE ADJ TEST:.................SKIPPED\n", tag); + kfree(total_ix); + } else + logError(0, "%s SS TOTAL IX SENSE TEST:.................SKIPPED\n", tag); + + /*********************** SS SENSE CX ******************************/ + /* SS CX1 FORCE TEST */ + logError(0, "%s SS CX1 FORCE TEST:\n", tag); + if (todo->SelfForceCx1 == 1) { + + ret = parseProductionTestLimits(path_limits, SS_CX1_FORCE_MIN_MAX, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_CX1_FORCE_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + container = (u16) ssCompData.f_cx1; + ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], thresholds[1]); /* check the limits */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMinMax SS CX1 FORCE TEST failed... ERROR COUNT = %d\n", tag, ret); + count_fail += 1; + /* if (stop_on_fail) return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); */ + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS CX1 FORCE TEST:.................OK\n\n", tag); + kfree(thresholds); + } else + logError(0, "%s SS CX1 FORCE TEST:.................SKIPPED\n\n", tag); + + /* SS CX2 FORCE TEST */ + logError(0, "%s SS CX2 FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceCx2 == 1) { + ret = parseProductionTestLimits(path_limits, SS_CX2_FORCE_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load the min thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_CX2_FORCE_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, SS_CX2_FORCE_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_CX2_FORCE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMap(ssCompData.cx2_fm, ssCompData.header.force_node, 1, thresholds_min, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap SS CX2 FORCE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS CX2 FORCE MIN MAX TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS CX2 FORCE MIN MAX TEST:.................OK\n\n", tag); + + kfree(thresholds_min); + kfree(thresholds_max); + } else + logError(0, "%s SS CX2 FORCE MIN MAX TEST:.................SKIPPED\n", tag); + + logError(0, "%s SS CX2 FORCE ADJ TEST:\n", tag); + if (todo->SelfForceCx2Adj == 1) { + /* SS CX2 FORCE ADJV TEST */ + logError(0, "%s SS CX2 FORCE ADJVERT TEST:\n", tag); + ret = computeAdjVert(ssCompData.cx2_fm, ssCompData.header.force_node, + 1, &adjvert); /* comepute the ADJV for CX2 FORCE */ + if (ret < 0) { + logError(1, "%s production_test_data: computeAdjVert SS CX2 FORCE ADJV failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s SS CX2 FORCE ADJV computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, SS_CX2_FORCE_ADJV_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node - 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_CX2_FORCE_ADJV_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapAdj(adjvert, ssCompData.header.force_node - 1, 1, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap SS IX2 FORCE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS CX2 FORCE ADJV TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS CX2 FORCE ADJV TEST:.................OK\n\n", tag); + + kfree(thresholds_max); + kfree(adjvert); + } else + logError(0, "%s SS CX2 FORCE ADJ TEST:.................SKIPPED\n\n", tag); + + /* SS TOTAL CX FORCE */ + logError(0, "%s SS TOTAL CX FORCE TEST:\n", tag); + if (todo->SelfForceCxTotal == 1 || todo->SelfForceCxTotalAdj == 1) { + ret = computeTotal(ssCompData.cx2_fm, ssCompData.f_cx1, ssCompData.header.force_node, 1, CX1_WEIGHT, CX2_WEIGHT, &total_cx); + if (ret < 0) { + logError(1, "%s production_test_data: computeTotal Cx Force failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s SS TOTAL CX FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceCxTotal == 1) { + ret = parseProductionTestLimits(path_limits, SS_TOTAL_CX_FORCE_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load the min thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_FORCE_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, SS_TOTAL_CX_FORCE_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_FORCE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapTotal(total_cx, ssCompData.header.force_node, 1, thresholds_min, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap SS TOTAL FORCE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS TOTAL FORCE MIN MAX TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS TOTAL FORCE MIN MAX TEST:.................OK\n\n", tag); + + kfree(thresholds_min); + kfree(thresholds_max); + } else + logError(0, "%s SS TOTAL CX FORCE MIN MAX TEST:.................SKIPPED\n", tag); + + /* SS TOTAL CX FORCE ADJV TEST */ + logError(0, "%s SS TOTAL CX FORCE ADJ TEST:\n", tag); + if (todo->SelfForceCxTotalAdj == 1) { + logError(0, "%s SS TOTAL CX FORCE ADJVERT TEST:\n", tag); + ret = computeAdjVertTotal(total_cx, ssCompData.header.force_node, 1, &total_adjvert); /* comepute the ADJV for CX2 FORCE */ + if (ret < 0) { + logError(1, "%s production_test_data: computeAdjVert SS TOTAL CX FORCE ADJV failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s SS TOTAL CX FORCE ADJV computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, SS_TOTAL_CX_FORCE_ADJV_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node - 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_FORCE_ADJV_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapAdjTotal(total_adjvert, ssCompData.header.force_node - 1, 1, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap SS TOTAL CX FORCE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS TOTAL CX FORCE ADJV TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS TOTAL CX FORCE ADJV TEST:.................OK\n\n", tag); + + kfree(thresholds_max); + kfree(total_adjvert); + + } else + logError(0, "%s SS TOTAL CX FORCE ADJ TEST:.................SKIPPED\n", tag); + kfree(total_cx); + } else + logError(0, "%s SS TOTAL CX FORCE TEST:.................SKIPPED\n\n", tag); + + /* ********************** SS SENSE CX ************************************/ + /* SS CX1 SENSE TEST */ + logError(0, "%s SS CX1 SENSE TEST:\n", tag); + if (todo->SelfSenseCx1 == 1) { + + ret = parseProductionTestLimits(path_limits, SS_CX1_SENSE_MIN_MAX, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_CX1_SENSE_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + container = (u16) ssCompData.s_cx1; + ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], thresholds[1]); /* check the limits */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMinMax SS CX1 SENSE TEST failed... ERROR COUNT = %d\n", tag, ret); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS CX1 SENSE TEST:.................OK\n\n", tag); + kfree(thresholds); + } else + logError(0, "%s SS CX1 SENSE TEST:.................SKIPPED\n\n", tag); + + /* SS CX2 SENSE TEST */ + logError(0, "%s SS CX2 SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseCx2 == 1) { + ret = parseProductionTestLimits(path_limits, SS_CX2_SENSE_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load the min thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_CX2_SENSE_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, SS_CX2_SENSE_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_CX2_SENSE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMap(ssCompData.cx2_sn, 1, ssCompData.header.sense_node, thresholds_min, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap SS CX2 SENSE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS CX2 SENSE MIN MAX TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS CX2 SENSE MIN MAX TEST:.................OK\n\n", tag); + + kfree(thresholds_min); + kfree(thresholds_max); + } else + logError(0, "%s SS CX2 SENSE MIN MAX TEST:.................SKIPPED\n", tag); + + logError(0, "%s SS CX2 SENSE ADJ TEST:\n", tag); + if (todo->SelfSenseCx2Adj == 1) { + /* SS CX2 SENSE ADJH TEST */ + logError(0, "%s SS CX2 SENSE ADJHORIZ TEST:\n", tag); + ret = computeAdjHoriz(ssCompData.ix2_sn, 1, ssCompData.header.sense_node, &adjhor); + if (ret < 0) { + logError(1, "%s production_test_data: computeAdjHoriz SS CX2 SENSE ADJH failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s SS CX2 SENSE ADJH computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, SS_CX2_SENSE_ADJH_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX2_SENSE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapAdj(adjhor, 1, ssCompData.header.sense_node - 1, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMapAdj SS CX2 SENSE ADJH failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS CX2 SENSE ADJH TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS CX2 SENSE ADJH TEST:.................OK\n", tag); + + kfree(thresholds_max); + kfree(adjhor); + } else + logError(0, "%s SS CX2 SENSE ADJ TEST:.................SKIPPED\n\n", tag); + + /* SS TOTAL CX SENSE */ + logError(0, "%s SS TOTAL CX SENSE TEST:\n", tag); + if (todo->SelfSenseCxTotal == 1 || todo->SelfSenseCxTotalAdj == 1) { + ret = computeTotal(ssCompData.cx2_sn, ssCompData.s_cx1, 1, ssCompData.header.sense_node, CX1_WEIGHT, CX2_WEIGHT, &total_cx); + if (ret < 0) { + logError(1, "%s production_test_data: computeTotal Cx Sense failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s SS TOTAL CX SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseCxTotal == 1) { + ret = parseProductionTestLimits(path_limits, SS_TOTAL_CX_SENSE_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load the min thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_SENSE_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, SS_TOTAL_CX_SENSE_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_SENSE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapTotal(total_cx, 1, ssCompData.header.sense_node, thresholds_min, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap SS TOTAL CX SENSE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS TOTAL CX SENSE MIN MAX TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS TOTAL CX SENSE MIN MAX TEST:.................OK\n\n", tag); + + kfree(thresholds_min); + kfree(thresholds_max); + } else + logError(0, "%s SS TOTAL CX SENSE MIN MAX TEST:.................SKIPPED\n", tag); + + /* SS TOTAL IX SENSE ADJH TEST */ + logError(0, "%s SS TOTAL CX SENSE ADJ TEST:\n", tag); + if (todo->SelfSenseCxTotalAdj == 1) { + logError(0, "%s SS TOTAL CX SENSE ADJHORIZ TEST:\n", tag); + ret = computeAdjHorizTotal(total_cx, 1, ssCompData.header.sense_node, &total_adjhor); + if (ret < 0) { + logError(1, "%s production_test_data: computeAdjHoriz SS TOTAL CX SENSE ADJH failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s SS TOTAL CX SENSE ADJ HORIZ computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, SS_TOTAL_CX_SENSE_ADJH_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_SENSE_ADJH_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapAdjTotal(total_adjhor, 1, ssCompData.header.sense_node - 1, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMapAdj SS TOTAL CX SENSE ADJH failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS TOTAL CX SENSE ADJH TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS TOTAL CX SENSE ADJH TEST:.................OK\n\n", tag); + + kfree(thresholds_max); + kfree(total_adjhor); + } else + logError(0, "%s SS TOTAL CX SENSE ADJ TEST:.................SKIPPED\n", tag); + kfree(total_cx); + } else + logError(0, "%s SS TOTAL CX SENSE TEST:.................SKIPPED\n", tag); + + /* SS compensation data are no usefull anymore */ + + kfree(ssCompData.ix2_fm); + kfree(ssCompData.ix2_sn); + kfree(ssCompData.cx2_fm); + kfree(ssCompData.cx2_sn); + +ERROR: + logError(0, "%s\n", tag); + if (count_fail == 0) { + logError(0, "%s SS IX CX testes finished!.................OK\n\n", tag); + return OK; + } + /* print all kind of data in just one row for readability reason */ + print_frame_u8("SS Init Data Ix2_fm = ", array1dTo2d_u8(ssCompData.ix2_fm, ssCompData.header.force_node, ssCompData.header.force_node), 1, ssCompData.header.force_node); + print_frame_u8("SS Init Data Cx2_fm = ", array1dTo2d_u8(ssCompData.cx2_fm, ssCompData.header.force_node, ssCompData.header.force_node), 1, ssCompData.header.force_node); + print_frame_u8("SS Init Data Ix2_sn = ", array1dTo2d_u8(ssCompData.ix2_sn, ssCompData.header.sense_node, ssCompData.header.sense_node), 1, ssCompData.header.sense_node); + print_frame_u8("SS Init Data Cx2_sn = ", array1dTo2d_u8(ssCompData.cx2_sn, ssCompData.header.sense_node, ssCompData.header.sense_node), 1, ssCompData.header.sense_node); + logError(0, "%s SS IX CX testes finished!.................FAILED fails_count = %d\n\n", tag, count_fail); + return (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA); +} + +int production_test_data(char *path_limits, int stop_on_fail, TestToDo *todo) +{ + int res = OK, ret; + + if (todo == NULL) { + logError(1, "%s production_test_data: No TestToDo specified!! ERROR = %02X\n", tag, (ERROR_OP_NOT_ALLOW | ERROR_PROD_TEST_DATA)); + return (ERROR_OP_NOT_ALLOW | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s DATA Production test is starting...\n", tag); + + ret = production_test_ms_raw(path_limits, stop_on_fail, todo); + res |= ret; + if (ret < 0) { + logError(1, "%s production_test_data: production_test_ms_raw failed... ERROR = %02X\n", tag, ret); + if (stop_on_fail == 1) + goto END; + } + + ret = production_test_ms_cx(path_limits, stop_on_fail, todo); + res |= ret; + if (res < 0) { + logError(1, "%s production_test_data: production_test_ms_cx failed... ERROR = %02X\n", tag, ret); + if (stop_on_fail == 1) + goto END; + } + + ret = production_test_ss_raw(path_limits, stop_on_fail, todo); + res |= ret; + if (ret < 0) { + logError(1, "%s production_test_data: production_test_ss_raw failed... ERROR = %02X\n", tag, ret); + if (stop_on_fail == 1) + goto END; + } + + ret = production_test_ss_ix_cx(path_limits, stop_on_fail, todo); + res |= ret; + if (ret < 0) { + logError(1, "%s production_test_data: production_test_ss_ix_cx failed... ERROR = %02X\n", tag, ret); + if (stop_on_fail == 1) + goto END; + } + +END: + if (res < OK) + logError(0, "%s DATA Production test failed!\n", tag); + else + logError(0, "%s DATA Production test finished!\n", tag); + return res; +} + +int save_mp_flag(u32 signature) +{ + int res = -1, ret; + int i; + u8 cmd[6] = {FTS_CMD_WRITE_MP_FLAG, 0x00, 0x00, 0x00, 0x00, 0x00}; + + u32ToU8(signature, &cmd[2]); + + logError(0, "%s Starting Saving Flag with signature = %08X ...\n", tag, signature); + + for (i = 0; i < SAVE_FLAG_RETRY && res < OK; i++) { + logError(0, "%s Attempt number %d to save mp flag !\n", tag, i+1); + logError(0, "%s Command write flag sent...\n", tag); + res = fts_writeFwCmd(cmd, 6); + if (res >= OK) + res = save_cx_tuning(); + + } + + ret = readChipInfo(1); /* need to update the MP Flag */ + if (ret < OK) { + logError(1, "%s save_mp_flag: read chip info ERROR %08X\n", tag, ret); + } + + res |= ret; + if (res < OK) { + logError(1, "%s save_mp_flag: ERROR %08X ...\n", tag, res); + return res; + } + logError(1, "%s Saving Flag DONE!\n", tag); + return OK; +} + +int parseProductionTestLimits(char *path, char *label, int **data, int *row, int *column) +{ + + int find = 0; + char *token = NULL; + int i = 0; + int j = 0; + int z = 0; + + char *line = NULL; + int fd = -1; + char *buf = NULL; + int n, size, pointer = 0, ret; + char *data_file = NULL; +#ifndef LIMITS_H_FILE + const struct firmware *fw = NULL; + struct device *dev = NULL; + + dev = getDev(); + if (dev != NULL) + fd = request_firmware(&fw, path, dev); +#else + fd = 0; +#endif + + line = (char *)kmalloc(1800*sizeof(char), GFP_KERNEL); + buf = line; + + if (fd == 0) { +#ifndef LIMITS_H_FILE + size = fw->size; + data_file = (char *)fw->data; + logError(0, "%s Start to reading %s...\n", tag, path); +#else + size = LIMITS_SIZE_NAME; + data_file = (char *)(LIMITS_ARRAY_NAME); +#endif + + logError(0, "%s The size of the limits file is %d bytes...\n", tag, size); + + while (find == 0) { + /* start to look for the wanted label */ + if (readLine(&data_file[pointer], &line, size-pointer, &n) < 0) { + find = -1; + break; + } + pointer += n; + if (line[0] == '*') { /* each header row start with * ex. *label,n_row,n_colum */ + buf = line; + line += 1; + token = strsep(&line, ","); + if (strcmp(token, label) == 0) { + /* if the row is the wanted one i retrieve rows and columns info */ + find = 1; + token = strsep(&line, ","); + if (token != NULL) { + sscanf(token, "%d", row); + logError(0, "%s Row = %d\n", tag, *row); + } else { + logError(1, "%s parseProductionTestLimits 1: ERROR %02X\n", tag, ERROR_FILE_PARSE); + /* release_firmware(fw); */ + /* return ERROR_FILE_PARSE; */ + ret = ERROR_FILE_PARSE; + goto END; + } + token = strsep(&line, ","); + if (token != NULL) { + sscanf(token, "%d", column); + logError(0, "%s Column = %d\n", tag, *column); + } else { + logError(1, "%s parseProductionTestLimits 2: ERROR %02X\n", tag, ERROR_FILE_PARSE); + /* release_firmware(fw); */ + /* return ERROR_FILE_PARSE; */ + ret = ERROR_FILE_PARSE; + goto END; + } + + *data = (int *)kmalloc(((*row)*(*column))*sizeof(int), GFP_KERNEL); + /* allocate the memory for containing the data */ + j = 0; + if (*data == NULL) { + logError(1, "%s parseProductionTestLimits: ERROR %02X\n", tag, ERROR_ALLOC); + /* release_firmware(fw); */ + /* return ERROR_ALLOC; */ + ret = ERROR_ALLOC; + goto END; + } + + /* start to read the data */ + for (i = 0; i < *row; i++) { + line = buf; + if (readLine(&data_file[pointer], &line, size-pointer, &n) < 0) { + logError(1, "%s parseProductionTestLimits : ERROR %02X\n", tag, ERROR_FILE_READ); + /* release_firmware(fw); */ + /* return ERROR_FILE_READ; */ + ret = ERROR_FILE_READ; + goto END; + } + pointer += n; + token = strsep(&line, ","); + for (z = 0; (z < *column) && (token != NULL); z++) { + sscanf(token, "%d", ((*data) + j)); + j++; + token = strsep(&line, ","); + } + } + if (j == ((*row)*(*column))) { /* check that all the data are read */ + logError(0, "%s READ DONE!\n", tag); + /* release_firmware(fw); */ + /* return OK; */ + ret = OK; + goto END; + } + logError(1, "%s parseProductionTestLimits 3: ERROR %02X\n", tag, ERROR_FILE_PARSE); + /* release_firmware(fw); */ + /* return ERROR_FILE_PARSE; */ + ret = ERROR_FILE_PARSE; + goto END; + } + } + + } + logError(1, "%s parseProductionTestLimits: ERROR %02X\n", tag, ERROR_LABEL_NOT_FOUND); + ret = ERROR_LABEL_NOT_FOUND; +END: +#ifndef LIMITS_H_FILE + release_firmware(fw); +#endif + return ret; + + } else { + logError(1, "%s parseProductionTestLimits: ERROR %02X\n", tag, ERROR_FILE_NOT_FOUND); + return ERROR_FILE_NOT_FOUND; + } + +} + +int readLine(char *data, char **line, int size, int *n) +{ + int i = 0; + if (size < 1) + return 1; + + while (data[i] != '\n' && i < size) { + *(*line + i) = data[i]; + i++; + } + *n = i+1; + *(*line + i) = '\0'; + + return OK; + +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTest.h b/drivers/input/touchscreen/st/fts_lib/ftsTest.h new file mode 100644 index 000000000000..93da901c9f42 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsTest.h @@ -0,0 +1,158 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FTS API for MP test * +* * +************************************************************************** +************************************************************************** + +*/ + +#include "ftsSoftware.h" + +#define LIMITS_FILE "stm_fts_production_limits.csv" + +#define WAIT_FOR_FRESH_FRAMES 100 /* ms */ +#define WAIT_AFTER_SENSEOFF 50 /* ms */ +#define TIMEOUT_ITO_TEST_RESULT 200 /* ms */ +#define TIMEOUT_INITIALIZATION_TEST_RESULT 5000 /* ms */ + +/* LABELS PRODUCTION TEST LIMITS FILE */ +#define MS_RAW_MIN_MAX "MS_RAW_DATA_MIN_MAX" +#define MS_RAW_GAP "MS_RAW_DATA_GAP" +#define MS_CX1_MIN_MAX "MS_TOUCH_ACTIVE_CX1_MIN_MAX" +#define MS_CX2_MAP_MIN "MS_TOUCH_ACTIVE_CX2_MIN" +#define MS_CX2_MAP_MAX "MS_TOUCH_ACTIVE_CX2_MAX" +#define MS_CX2_ADJH_MAP_MAX "MS_TOUCH_ACTIVE_CX2_ADJ_HORIZONTAL" +#define MS_CX2_ADJV_MAP_MAX "MS_TOUCH_ACTIVE_CX2_ADJ_VERTICAL" +#define MS_TOTAL_CX_MAP_MIN "MS_TOUCH_ACTIVE_TOTAL_CX_MIN" +#define MS_TOTAL_CX_MAP_MAX "MS_TOUCH_ACTIVE_TOTAL_CX_MAX" +#define MS_TOTAL_CX_ADJH_MAP_MAX "MS_TOUCH_ACTIVE_TOTAL_CX_ADJ_HORIZONTAL" +#define MS_TOTAL_CX_ADJV_MAP_MAX "MS_TOUCH_ACTIVE_TOTAL_CX_ADJ_VERTICAL" +#define SS_RAW_FORCE_MIN_MAX "SS_RAW_DATA_FORCE_MIN_MAX" +#define SS_RAW_SENSE_MIN_MAX "SS_RAW_DATA_SENSE_MIN_MAX" +#define SS_RAW_FORCE_GAP "SS_RAW_DATA_FORCE_GAP" +#define SS_RAW_SENSE_GAP "SS_RAW_DATA_SENSE_GAP" +#define SS_IX1_FORCE_MIN_MAX "SS_TOUCH_ACTIVE_IX1_FORCE_MIN_MAX" +#define SS_IX1_SENSE_MIN_MAX "SS_TOUCH_ACTIVE_IX1_SENSE_MIN_MAX" +#define SS_CX1_FORCE_MIN_MAX "SS_TOUCH_ACTIVE_CX1_FORCE_MIN_MAX" +#define SS_CX1_SENSE_MIN_MAX "SS_TOUCH_ACTIVE_CX1_SENSE_MIN_MAX" +#define SS_IX2_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_IX2_FORCE_MIN" +#define SS_IX2_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_IX2_FORCE_MAX" +#define SS_IX2_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_IX2_SENSE_MIN" +#define SS_IX2_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_IX2_SENSE_MAX" +#define SS_IX2_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_IX2_ADJ_VERTICAL" +#define SS_IX2_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_IX2_ADJ_HORIZONTAL" +#define SS_CX2_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_CX2_FORCE_MIN" +#define SS_CX2_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_CX2_FORCE_MAX" +#define SS_CX2_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_CX2_SENSE_MIN" +#define SS_CX2_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_CX2_SENSE_MAX" +#define SS_CX2_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_CX2_ADJ_VERTICAL" +#define SS_CX2_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_CX2_ADJ_HORIZONTAL" + +/* TOTAL SS */ +#define SS_TOTAL_IX_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_IX_FORCE_MIN" +#define SS_TOTAL_IX_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_FORCE_MAX" +#define SS_TOTAL_IX_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_IX_SENSE_MIN" +#define SS_TOTAL_IX_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_SENSE_MAX" +#define SS_TOTAL_IX_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_ADJ_VERTICAL" +#define SS_TOTAL_IX_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_ADJ_HORIZONTAL" +#define SS_TOTAL_CX_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_CX_FORCE_MIN" +#define SS_TOTAL_CX_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_FORCE_MAX" +#define SS_TOTAL_CX_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_CX_SENSE_MIN" +#define SS_TOTAL_CX_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_SENSE_MAX" +#define SS_TOTAL_CX_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_ADJ_VERTICAL" +#define SS_TOTAL_CX_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_ADJ_HORIZONTAL" + +/* KEYS */ +#define MS_KEY_RAW_MIN_MAX "MS_KEY_RAW_DATA_MIN_MAX" +#define MS_KEY_CX1_MIN_MAX "MS_KEY_CX1_MIN_MAX" +#define MS_KEY_CX2_MAP_MIN "MS_KEY_CX2_MIN" +#define MS_KEY_CX2_MAP_MAX "MS_KEY_CX2_MAX" +#define MS_KEY_TOTAL_CX_MAP_MIN "MS_KEY_TOTAL_CX_MIN" +#define MS_KEY_TOTAL_CX_MAP_MAX "MS_KEY_TOTAL_CX_MAX" + +/* CONSTANT TOTAL IX */ +#define SS_IX1_FORCE_W "SS_IX1_FORCE_W" +#define SS_IX2_FORCE_W "SS_IX2_FORCE_W" +#define SS_IX1_SENSE_W "SS_IX1_SENSE_W" +#define SS_IX2_SENSE_W "SS_IX2_SENSE_W" + +#define SAVE_FLAG_RETRY 3 + +typedef struct { + int MutualRaw; + int MutualRawGap; + int MutualCx1; + int MutualCx2; + int MutualCx2Adj; + int MutualCxTotal; + int MutualCxTotalAdj; + + int MutualKeyRaw; + int MutualKeyCx1; + int MutualKeyCx2; + int MutualKeyCxTotal; + + int SelfForceRaw; + int SelfForceRawGap; + int SelfForceIx1; + int SelfForceIx2; + int SelfForceIx2Adj; + int SelfForceIxTotal; + int SelfForceIxTotalAdj; + int SelfForceCx1; + int SelfForceCx2; + int SelfForceCx2Adj; + int SelfForceCxTotal; + int SelfForceCxTotalAdj; + + int SelfSenseRaw; + int SelfSenseRawGap; + int SelfSenseIx1; + int SelfSenseIx2; + int SelfSenseIx2Adj; + int SelfSenseIxTotal; + int SelfSenseIxTotalAdj; + int SelfSenseCx1; + int SelfSenseCx2; + int SelfSenseCx2Adj; + int SelfSenseCxTotal; + int SelfSenseCxTotalAdj; + +} TestToDo; + +int computeAdjHoriz(u8 *data, int row, int column, u8 **result); +int computeAdjHorizTotal(u16 *data, int row, int column, u16 **result); +int computeAdjVert(u8 *data, int row, int column, u8 **result); +int computeAdjVertTotal(u16 *data, int row, int column, u16 **result); +int computeTotal(u8 *data, u8 main, int row, int column, int m, int n, u16 **result); +int checkLimitsMinMax(short *data, int row, int column, int min, int max); +int checkLimitsMap(u8 *data, int row, int column, int *min, int *max); +int checkLimitsMapTotal(u16 *data, int row, int column, int *min, int *max); +int checkLimitsMapAdj(u8 *data, int row, int column, int *max); +int checkLimitsMapAdjTotal(u16 *data, int row, int column, int *max); +int production_test_ito(void); +int production_test_initialization(void); +int ms_compensation_tuning(void); +int ss_compensation_tuning(void); +int lp_timer_calibration(void); +int save_cx_tuning(void); +int production_test_splited_initialization(int saveToFlash); +int production_test_main(char *pathThresholds, int stop_on_fail, + int saveInit, TestToDo *todo, u32 signature); +int production_test_ms_raw(char *path_limits, int stop_on_fail, TestToDo *todo); +int production_test_ms_cx(char *path_limits, int stop_on_fail, TestToDo *todo); +int production_test_ss_raw(char *path_limits, int stop_on_fail, TestToDo *todo); +int production_test_ss_ix_cx(char *path_limits, int stop_on_fail, TestToDo *todo); +int production_test_data(char *path_limits, int stop_on_fail, TestToDo *todo); +int production_test_ms_key_cx(char *path_limits, int stop_on_fail, TestToDo *todo); +int production_test_ms_key_raw(char *path_limits); +int save_mp_flag(u32 signature); +int parseProductionTestLimits(char *path, char *label, int **data, int *row, int *column); +int readLine(char *data, char **line, int size, int *n); diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTime.c b/drivers/input/touchscreen/st/fts_lib/ftsTime.c new file mode 100644 index 000000000000..03a2a39fe10b --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsTime.c @@ -0,0 +1,84 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FTS Utility for mesuring/handling the time * +* * +************************************************************************** +************************************************************************** + +*/ + +#include "ftsCrossCompile.h" +#include "ftsTime.h" + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <stdarg.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/serio.h> +#include <linux/time.h> +#include <linux/pm.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/power_supply.h> +#include <linux/firmware.h> +#include <linux/regulator/consumer.h> +#include <linux/of_gpio.h> +/* #include <linux/sec_sysfs.h> */ + +void startStopWatch(StopWatch *w) +{ + w->start = current_kernel_time(); +} + +void stopStopWatch(StopWatch *w) +{ + w->end = current_kernel_time(); +} + +int elapsedMillisecond(StopWatch *w) +{ + int result; + + result = ((w->end.tv_sec - w->start.tv_sec)*1000) + (w->end.tv_nsec - w->start.tv_nsec) / 1000000; + return result; +} + +int elapsedNanosecond(StopWatch *w) +{ + int result; + + result = ((w->end.tv_sec - w->start.tv_sec)*1000000000) + (w->end.tv_nsec - w->start.tv_nsec); + return result; +} + +char *timestamp(void) +{ + char *result = NULL; + result = (char *)kmalloc((1)*sizeof(char), GFP_KERNEL); + if (result == NULL) + return NULL; + result[0] = ' '; + return result; +} + +void stdelay(unsigned long ms) +{ + msleep(ms); +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTime.h b/drivers/input/touchscreen/st/fts_lib/ftsTime.h new file mode 100644 index 000000000000..5eeca6eace97 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsTime.h @@ -0,0 +1,29 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FTS Utility for mesuring/handling the time * +* * +************************************************************************** +************************************************************************** + +*/ + +#include "ftsCrossCompile.h" + +#include <linux/time.h> + +typedef struct { + struct timespec start, end; +} StopWatch; + +void startStopWatch(StopWatch *w); +void stopStopWatch(StopWatch *w); +int elapsedMillisecond(StopWatch *w); +int elapsedNanosecond(StopWatch *w); +char *timestamp(void); +void stdelay(unsigned long ms); diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTool.c b/drivers/input/touchscreen/st/fts_lib/ftsTool.c new file mode 100644 index 000000000000..4c5d54f17ea7 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsTool.c @@ -0,0 +1,706 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FTS Utility Functions * +* * +************************************************************************** +************************************************************************** + +*/ + +#include "ftsCompensation.h" +#include "ftsCrossCompile.h" +#include "ftsError.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTime.h" +#include "ftsTool.h" +#include "../fts.h" /* needed for the PHONE_KEY define */ + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <stdarg.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/serio.h> +#include <linux/init.h> +#include <linux/pm.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/power_supply.h> +#include <linux/firmware.h> +#include <linux/gpio.h> + +/* static char tag[8]="[ FTS ]\0"; */ +static int reset_gpio = GPIO_NOT_DEFINED; +static int system_resetted_up; +static int system_resetted_down; +extern chipInfo ftsInfo; + +int readB2(u16 address, u8 *outBuf, int len) +{ + int remaining = len; + int toRead = 0; + int retry = 0; + int ret; + int event_to_search[3]; + u8 *readEvent = (u8 *)kmalloc(FIFO_EVENT_SIZE*sizeof(u8), GFP_KERNEL); + u8 cmd[4] = { FTS_CMD_REQU_FW_CONF, 0x00, 0x00, (u8)len }; + + if (readEvent == NULL) { + logError(1, "%s readB2: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + u16ToU8_be(address, &cmd[1]); + + logError(0, "%s %s", tag, printHex("Command B2 = ", cmd, 4)); + do { + remaining = len; + ret = fts_writeFwCmd(cmd, 4); + if (ret < 0) { + logError(1, "%s readB2: ERROR %02X\n", tag, ERROR_I2C_W); + return ret; + } /* ask to the FW the data */ + logError(0, "%s Command to FW sent!\n", tag); + event_to_search[0] = (int)EVENTID_FW_CONFIGURATION; + + while (remaining > OK) { + event_to_search[1] = (int)((address & 0xFF00)>>8); + event_to_search[2] = (int) (address & 0x00FF); + if (remaining > B2_DATA_BYTES) { + toRead = B2_DATA_BYTES; + remaining -= B2_DATA_BYTES; + } else { + toRead = remaining; + remaining = 0; + } + + ret = pollForEvent(event_to_search, 3, readEvent, GENERAL_TIMEOUT); + if (ret >= OK) { /* start the polling for reading the reply */ + memcpy(outBuf, &readEvent[3], toRead); + retry = 0; + outBuf += toRead; + + } else { + retry += 1; + break; + } + address += B2_DATA_BYTES; + } + + } while (retry < B2_RETRY && retry != 0); + + kfree(readEvent); + if (retry == B2_RETRY) { + logError(1, "%s readB2: ERROR %02X\n", tag, ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } + logError(0, "%s B2 read %d bytes\n", tag, len); + + return OK; +} + +int readB2U16(u16 address, u8 *outBuf, int byteToRead) +{ + + int remaining = byteToRead; + int toRead = 0; + int ret; + + u8 *buff = (u8 *)kmalloc((B2_CHUNK + 1)*sizeof(u8), GFP_KERNEL); + if (buff == NULL) { + logError(1, "%s readB2U16: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + while (remaining > 0) { + if (remaining >= B2_CHUNK) { + toRead = B2_CHUNK; + remaining -= B2_CHUNK; + } else { + toRead = remaining; + remaining = 0; + } + + ret = readB2(address, buff, toRead); + if (ret < 0) + return ret; + memcpy(outBuf, buff, toRead); + + address += toRead; + + outBuf += toRead; + + } + + kfree(buff); + return OK; +} + +int releaseInformation(void) +{ + int ret; + u8 cmd[1] = { FTS_CMD_RELEASE_INFO }; + int event_to_search[1]; + u8 readEvent[FIFO_EVENT_SIZE]; + + event_to_search[0] = (int)EVENTID_RELEASE_INFO; + + logError(0, "%s releaseInformation started... Chip INFO:\n", tag); + + ret = fts_writeFwCmd(cmd, 1); + if (ret < OK) { + logError(1, "%s releaseInformation: ERROR %02X\n", tag, ret); + return ret; + } + + ret = pollForEvent(event_to_search, 1, &readEvent[0], RELEASE_INFO_TIMEOUT); + /* start the polling for reading the reply */ + if (ret < OK) { + logError(1, "%s releaseInformation: ERROR %02X\n", tag, ret); + return ret; + } + + logError(0, "%s releaseInformation: Finished!\n", tag, ret); + return OK; + +} + +char *printHex(char *label, u8 *buff, int count) +{ + int i, offset; + char *result = NULL; + + offset = strlen(label); + result = (char *)kmalloc(((offset + 3 * count) + 1)*sizeof(char), GFP_KERNEL); + if (result != NULL) { + strlcpy(result, label, sizeof(result)); + + for (i = 0; i < count; i++) { + snprintf(&result[offset + i * 3], 4, "%02X ", buff[i]); + } + strlcat(result, "\n", sizeof(result)); + } + return result; +} + +int pollForEvent(int *event_to_search, int event_bytes, u8 *readData, int time_to_wait) +{ + int i, find, retry, count_err; + int time_to_count; + int err_handling = OK; + StopWatch clock; + + u8 cmd[1] = { FIFO_CMD_READONE }; + char *temp = NULL; + + find = 0; + retry = 0; + count_err = 0; + time_to_count = time_to_wait / TIMEOUT_RESOLUTION; + + startStopWatch(&clock); + while (find != 1 && retry < time_to_count && fts_readCmd(cmd, 1, readData, FIFO_EVENT_SIZE) >= 0) { + /* Log of errors */ + if (readData[0] == EVENTID_ERROR_EVENT) { + logError(1, "%s %s", tag, printHex("ERROR EVENT = ", readData, FIFO_EVENT_SIZE)); + count_err++; + err_handling = errorHandler(readData, FIFO_EVENT_SIZE); + if ((err_handling&0xF0FF0000) == ERROR_HANDLER_STOP_PROC) { + logError(1, "%s pollForEvent: forced to be stopped! ERROR %08X\n", tag, err_handling); + return err_handling; + } + } else { + if (readData[0] != EVENTID_NO_EVENT) { + logError(1, "%s %s", tag, printHex("READ EVENT = ", readData, FIFO_EVENT_SIZE)); + } + if (readData[0] == EVENTID_CONTROL_READY && event_to_search[0] != EVENTID_CONTROL_READY) { + logError(1, "%s pollForEvent: Unmanned Controller Ready Event! Setting reset flags...\n", tag); + setSystemResettedUp(1); + setSystemResettedDown(1); + } + } + + find = 1; + + for (i = 0; i < event_bytes; i++) { + + if (event_to_search[i] != -1 && (int)readData[i] != event_to_search[i]) { + find = 0; + break; + } + } + + retry++; + msleep(TIMEOUT_RESOLUTION); + } + stopStopWatch(&clock); + if ((retry >= time_to_count) && find != 1) { + logError(1, "%s pollForEvent: ERROR %02X\n", tag, ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } else if (find == 1) { + temp = printHex("FOUND EVENT = ", readData, FIFO_EVENT_SIZE); + if (temp != NULL) + logError(0, "%s %s", tag, temp); + kfree(temp); + logError(0, "%s Event found in %d ms (%d iterations)! Number of errors found = %d\n", tag, elapsedMillisecond(&clock), retry, count_err); + return count_err; + } + logError(1, "%s pollForEvent: ERROR %02X\n", tag, ERROR_I2C_R); + return ERROR_I2C_R; +} + +int flushFIFO(void) +{ + + u8 cmd = FIFO_CMD_FLUSH; /* flush the FIFO */ + if (fts_writeCmd(&cmd, 1) < 0) { + logError(1, "%s flushFIFO: ERROR %02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + + logError(0, "%s FIFO flushed!\n", tag); + return OK; + +} + +int fts_disableInterrupt(void) +{ + u8 cmd[4] = { FTS_CMD_HW_REG_W, 0x00, 0x00, IER_DISABLE }; /* disable interrupt */ + u16ToU8_be(IER_ADDR, &cmd[1]); + + if (fts_writeCmd(cmd, 4) < OK) { + logError(1, "%s fts_disableInterrupt: ERROR %02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + logError(0, "%s Interrupt Disabled!\n", tag); + return OK; +} + +int fts_enableInterrupt(void) +{ + u8 cmd[4] = { FTS_CMD_HW_REG_W, 0x00, 0x00, IER_ENABLE }; /* enable interrupt */ + u16ToU8_be(IER_ADDR, &cmd[1]); + if (fts_writeCmd(cmd, 4) < 0) { + logError(1, "%s fts_enableInterrupt: ERROR %02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + logError(0, "%s Interrupt Enabled!\n", tag); + return OK; +} + +int u8ToU16n(u8 *src, int src_length, u16 *dst) +{ + int i, j; + + if (src_length % 2 != 0) { + return 0; + } + j = 0; + dst = (u16 *)kmalloc((src_length / 2)*sizeof(u16), GFP_KERNEL); + for (i = 0; i < src_length; i += 2) { + dst[j] = ((src[i+1] & 0x00FF) << 8) + (src[i] & 0x00FF); + j++; + } + + return (src_length / 2); +} + +int u8ToU16(u8 *src, u16 *dst) +{ + *dst = (u16)(((src[1] & 0x00FF) << 8) + (src[0] & 0x00FF)); + return 0; +} + +int u8ToU16_le(u8 *src, u16 *dst) +{ + *dst = (u16)(((src[0] & 0x00FF) << 8) + (src[1] & 0x00FF)); + return 0; +} + +int u16ToU8n(u16 *src, int src_length, u8 *dst) +{ + int i, j; + dst = (u8 *)kmalloc((2 * src_length)*sizeof(u8), GFP_KERNEL); + j = 0; + for (i = 0; i < src_length; i++) { + dst[j] = (u8) (src[i] & 0xFF00)>>8; + dst[j+1] = (u8) (src[i] & 0x00FF); + j += 2; + } + + return src_length * 2; + +} + +int u16ToU8(u16 src, u8 *dst) +{ + dst[0] = (u8)((src & 0xFF00) >> 8); + dst[1] = (u8)(src & 0x00FF); + return 0; +} + +int u16ToU8_be(u16 src, u8 *dst) +{ + dst[0] = (u8)((src & 0xFF00) >> 8); + dst[1] = (u8)(src & 0x00FF); + return 0; +} + +int u16ToU8_le(u16 src, u8 *dst) +{ + dst[1] = (u8)((src & 0xFF00) >> 8); + dst[0] = (u8)(src & 0x00FF); + return 0; +} + +int u8ToU32(u8 *src, u32 *dst) +{ + *dst = (u32)(((src[3] & 0x000000FF) << 24) + ((src[2] & 0x000000FF) << 16) + ((src[1] & 0x000000FF) << 8) + (src[0] & 0x000000FF)); + return 0; +} + +int u32ToU8(u32 src, u8 *dst) +{ + dst[3] = (u8)((src & 0xFF000000) >> 24); + dst[2] = (u8)((src & 0x00FF0000) >> 16); + dst[1] = (u8)((src & 0x0000FF00) >> 8); + dst[0] = (u8)(src & 0x000000FF); + return 0; +} + +int attempt_function(int(*code)(void), unsigned long wait_before_retry, int retry_count) +{ + int result; + int count = 0; + + do { + result = code(); + count++; + msleep(wait_before_retry); + } while (count < retry_count && result < 0); + + if (count == retry_count) + return (result | ERROR_TIMEOUT); + else + return result; + +} + +void setResetGpio(int gpio) +{ + reset_gpio = gpio; + logError(1, "%s setResetGpio: reset_gpio = %d\n", tag, reset_gpio); +} + +int fts_system_reset(void) +{ + u8 readData[FIFO_EVENT_SIZE]; + int event_to_search; + int res = -1; + int i; + u8 cmd[4] = { FTS_CMD_HW_REG_W, 0x00, 0x00, SYSTEM_RESET_VALUE }; + event_to_search = (int)EVENTID_CONTROL_READY; + + u16ToU8_be(SYSTEM_RESET_ADDRESS, &cmd[1]); + logError(0, "%s System resetting...\n", tag); + for (i = 0; i < SYSTEM_RESET_RETRY && res < 0; i++) { + + if (reset_gpio == GPIO_NOT_DEFINED) { + res = fts_writeCmd(cmd, 4); + } else { + gpio_set_value(reset_gpio, 0); + msleep(10); + gpio_set_value(reset_gpio, 1); + res = OK; + } + if (res < OK) { + logError(1, "%s fts_system_reset: ERROR %02X\n", tag, ERROR_I2C_W); + } else { + res = pollForEvent(&event_to_search, 1, readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s fts_system_reset: ERROR %02X\n", tag, res); + } + } + } + if (res < OK) { + logError(1, "%s fts_system_reset...failed after 3 attempts: ERROR %02X\n", tag, (res | ERROR_SYSTEM_RESET_FAIL)); + return (res | ERROR_SYSTEM_RESET_FAIL); + } + logError(0, "%s System reset DONE!\n", tag); + system_resetted_down = 1; + system_resetted_up = 1; + return OK; + +} + +int isSystemResettedDown(void) +{ + return system_resetted_down; +} + +int isSystemResettedUp(void) +{ + return system_resetted_up; +} + +void setSystemResettedDown(int val) +{ + system_resetted_down = val; +} + +void setSystemResettedUp(int val) +{ + system_resetted_up = val; +} + +int senseOn(void) +{ + int ret; + u8 cmd[1] = { FTS_CMD_MS_MT_SENSE_ON }; + + ret = fts_writeFwCmd(cmd, 1); + if (ret < OK) { + logError(1, "%s senseOn: ERROR %02X\n", tag, ERROR_SENSE_ON_FAIL); + return (ret|ERROR_SENSE_ON_FAIL); + } + + logError(0, "%s senseOn: SENSE ON\n", tag); + return OK; +} + +int senseOff(void) +{ + int ret; + u8 cmd[1] = { FTS_CMD_MS_MT_SENSE_OFF }; + + ret = fts_writeFwCmd(cmd, 1); + if (ret < OK) { + logError(1, "%s senseOff: ERROR %02X\n", tag, ERROR_SENSE_OFF_FAIL); + return (ret | ERROR_SENSE_OFF_FAIL); + } + + logError(0, "%s senseOff: SENSE OFF\n", tag); + return OK; + +} + +int keyOn(void) +{ + int ret; + u8 cmd[1] = { FTS_CMD_MS_KEY_ON }; + + ret = fts_writeFwCmd(cmd, 1); + if (ret < OK) { + logError(1, "%s keyOn: ERROR %02X\n", tag, ERROR_SENSE_ON_FAIL); + return (ret | ERROR_SENSE_ON_FAIL); + } + + logError(0, "%s keyOn: KEY ON\n", tag); + return OK; + +} + +int keyOff(void) +{ + int ret; + u8 cmd[1] = { FTS_CMD_MS_KEY_OFF }; + + ret = fts_writeFwCmd(cmd, 1); + if (ret < OK) { + logError(1, "%s keyOff: ERROR %02X\n", tag, ERROR_SENSE_OFF_FAIL); + return (ret | ERROR_SENSE_OFF_FAIL); + } + + logError(0, "%s keyOff: KEY OFF\n", tag); + return OK; + +} + +int cleanUp(int enableTouch) +{ + int res; + + logError(0, "%s cleanUp: system reset...\n", tag); + res = fts_system_reset(); + if (res < OK) + return res; + if (enableTouch) { + logError(0, "%s cleanUp: enabling touches...\n", tag); + res = senseOn(); + if (res < OK) + return res; +#ifdef PHONE_KEY + res = keyOn(); + if (res < OK) + return res; +#endif + logError(0, "%s cleanUp: enabling interrupts...\n", tag); + res = fts_enableInterrupt(); + if (res < OK) + return res; + } + return OK; + +} + +int checkEcho(u8 *cmd, int size) +{ + int ret, i; + int event_to_search[size+1]; + u8 readData[FIFO_EVENT_SIZE]; + + if ((ftsInfo.u32_echoEn & 0x00000001) != ECHO_ENABLED) { + logError(1, "%s ECHO Not Enabled!\n", tag); + return OK; + } + if (size < 1) { + logError(1, "%s checkEcho: Error Size = %d not valid! or ECHO not Enabled! ERROR %08X\n", tag, size, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + if ((size+2) > FIFO_EVENT_SIZE) + size = FIFO_EVENT_SIZE-2; + /* Echo event EC xx xx xx xx xx xx fifo_status therefore for command + *with more than 6 bytes will echo only the first 6 + */ + event_to_search[0] = EVENTID_ECHO; + for (i = 1; i <= size; i++) { + event_to_search[i] = cmd[i-1]; + } + ret = pollForEvent(event_to_search, size+1, readData, GENERAL_TIMEOUT); + if (ret < OK) { + logError(1, "%s checkEcho: Echo Event not found! ERROR %02X\n", tag, ret); + return (ret | ERROR_CHECK_ECHO_FAIL); + } + + logError(0, "%s ECHO OK!\n", tag); + return OK; +} + +int featureEnableDisable(int on_off, u8 feature) +{ + int ret; + u8 cmd[2] = { 0x00, feature }; + + if (on_off == FEAT_ENABLE) { + cmd[0] = FTS_CMD_FEATURE_ENABLE; + logError(0, "%s featureEnableDisable: Enabling feature %02X ...\n", tag, feature); + } else { + cmd[0] = FTS_CMD_FEATURE_DISABLE; + logError(0, "%s featureEnableDisable: Disabling feature %02X ...\n", tag, feature); + } + + ret = fts_writeCmd(cmd, 2); /* not use writeFwCmd because this function can be called also during interrupt enable and should be fast */ + if (ret < OK) { + logError(1, "%s featureEnableDisable: ERROR %02X\n", tag, ret); + return (ret | ERROR_FEATURE_ENABLE_DISABLE); + } + + logError(0, "%s featureEnableDisable: DONE!\n", tag); + return OK; + +} + +short **array1dTo2d_short(short *data, int size, int columns) +{ + + int i; + short **matrix = (short **)kmalloc(((int)(size / columns))*sizeof(short *), GFP_KERNEL); + if (matrix != NULL) { + for (i = 0; i < (int)(size / columns); i++) { + matrix[i] = (short *)kmalloc(columns*sizeof(short), GFP_KERNEL); + } + + for (i = 0; i < size; i++) + matrix[i / columns][i % columns] = data[i]; + } + + return matrix; +} + +u8 **array1dTo2d_u8(u8 *data, int size, int columns) +{ + + int i; + u8 **matrix = (u8 **)kmalloc(((int)(size / columns))*sizeof(u8 *), GFP_KERNEL); + if (matrix != NULL) { + for (i = 0; i < (int)(size / columns); i++) { + matrix[i] = (u8 *)kmalloc(columns*sizeof(u8), GFP_KERNEL); + } + + for (i = 0; i < size; i++) + matrix[i / columns][i % columns] = data[i]; + } + + return matrix; +} + +void print_frame_short(char *label, short **matrix, int row, int column) +{ + int i, j; + logError(0, "%s %s\n", tag, label); + for (i = 0; i < row; i++) { + logError(0, "%s ", tag); + for (j = 0; j < column; j++) { + printk("%d ", matrix[i][j]); + } + logError(0, "\n"); + kfree(matrix[i]); + } +} + +void print_frame_u8(char *label, u8 **matrix, int row, int column) +{ + int i, j; + logError(0, "%s %s\n", tag, label); + for (i = 0; i < row; i++) { + logError(0, "%s ", tag); + for (j = 0; j < column; j++) { + printk("%d ", matrix[i][j]); + } + logError(0, "\n"); + kfree(matrix[i]); + } +} + +void print_frame_u32(char *label, u32 **matrix, int row, int column) +{ + int i, j; + logError(0, "%s %s\n", tag, label); + for (i = 0; i < row; i++) { + logError(0, "%s ", tag); + for (j = 0; j < column; j++) { + printk("%d ", matrix[i][j]); + } + logError(0, "\n"); + kfree(matrix[i]); + } +} + +void print_frame_int(char *label, int **matrix, int row, int column) +{ + int i, j; + logError(0, "%s %s\n", tag, label); + for (i = 0; i < row; i++) { + logError(0, "%s ", tag); + for (j = 0; j < column; j++) { + printk("%d ", matrix[i][j]); + } + logError(0, "\n"); + kfree(matrix[i]); + } +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTool.h b/drivers/input/touchscreen/st/fts_lib/ftsTool.h new file mode 100644 index 000000000000..a90e79fc5607 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsTool.h @@ -0,0 +1,64 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FTS Utility Functions * +* * +************************************************************************** +************************************************************************** + +*/ +#define GPIO_NOT_DEFINED -1 + +#define TIMEOUT_RESOLUTION 10 /* ms */ +#define GENERAL_TIMEOUT (50*TIMEOUT_RESOLUTION) /* ms */ +#define RELEASE_INFO_TIMEOUT (15*TIMEOUT_RESOLUTION) /* ms */ + +#define FEAT_ENABLE 1 +#define FEAT_DISABLE 0 + +#define SYSTEM_RESET_RETRY 3 + +#define B2_RETRY 2 + +int readB2(u16 address, u8 *outBuf, int len); +int readB2U16(u16 address, u8 *outBuf, int byteToRead); +int releaseInformation(void); +char *printHex(char *label, u8 *buff, int count); +int pollForEvent(int *event_to_search, int event_bytes, u8 *readData, int time_to_wait); +int fts_disableInterrupt(void); +int fts_enableInterrupt(void); +int u8ToU16(u8 *src, u16 *dst); +int u8ToU16_le(u8 *src, u16 *dst); +int u8ToU16n(u8 *src, int src_length, u16 *dst); +int u16ToU8(u16 src, u8 *dst); +int u16ToU8_le(u16 src, u8 *dst); +int u16ToU8_be(u16 src, u8 *dst); +int u16ToU8n(u16 *src, int src_length, u8 *dst); +int u8ToU32(u8 *src, u32 *dst); +int u32ToU8(u32 src, u8 *dst); +int attempt_function(int(*code)(void), unsigned long wait_before_retry, int retry_count); +void setResetGpio(int gpio); +int fts_system_reset(void); +int isSystemResettedUp(void); +int isSystemResettedDown(void); +void setSystemResettedUp(int val); +void setSystemResettedDown(int val); +int senseOn(void); +int senseOff(void); +int keyOn(void); +int keyOff(void); +int featureEnableDisable(int on_off, u8 feature); +int checkEcho(u8 *cmd, int size); +void print_frame_short(char *label, short **matrix, int row, int column); +short **array1dTo2d_short(short *data, int size, int columns); +u8 **array1dTo2d_u8(u8 *data, int size, int columns); +void print_frame_u8(char *label, u8 **matrix, int row, int column); +void print_frame_u32(char *label, u32 **matrix, int row, int column); +void print_frame_int(char *label, int **matrix, int row, int column); +int cleanUp(int enableTouch); +int flushFIFO(void); diff --git a/drivers/input/touchscreen/st/fts_limits.h b/drivers/input/touchscreen/st/fts_limits.h new file mode 100644 index 000000000000..d3be1a2b1e1a --- /dev/null +++ b/drivers/input/touchscreen/st/fts_limits.h @@ -0,0 +1,10 @@ +#ifndef FTS_LIMITS_H +#define FTS_LIMITS_H +/* This is an auto generated header file +* --->Remember to change the name of the two variables!<--- */ +const uint32_t myArray2_size; + +const uint8_t myArray2[] = { +}; + +#endif diff --git a/drivers/iommu/dma-mapping-fast.c b/drivers/iommu/dma-mapping-fast.c index df5140d23015..6d90221486c9 100644 --- a/drivers/iommu/dma-mapping-fast.c +++ b/drivers/iommu/dma-mapping-fast.c @@ -696,7 +696,11 @@ static struct dma_fast_smmu_mapping *__fast_smmu_create_mapping_sized( fast->num_4k_pages = size >> FAST_PAGE_SHIFT; fast->bitmap_size = BITS_TO_LONGS(fast->num_4k_pages) * sizeof(long); - fast->bitmap = kzalloc(fast->bitmap_size, GFP_KERNEL); + fast->bitmap = kzalloc(fast->bitmap_size, GFP_KERNEL | __GFP_NOWARN | + __GFP_NORETRY); + if (!fast->bitmap) + fast->bitmap = vzalloc(fast->bitmap_size); + if (!fast->bitmap) goto err2; @@ -780,7 +784,7 @@ void fast_smmu_detach_device(struct device *dev, dev->archdata.mapping = NULL; set_dma_ops(dev, NULL); - kfree(mapping->fast->bitmap); + kvfree(mapping->fast->bitmap); kfree(mapping->fast); } EXPORT_SYMBOL(fast_smmu_detach_device); diff --git a/drivers/iommu/io-pgtable-fast.c b/drivers/iommu/io-pgtable-fast.c index ba0af42f8df3..5d32a382e291 100644 --- a/drivers/iommu/io-pgtable-fast.c +++ b/drivers/iommu/io-pgtable-fast.c @@ -349,7 +349,12 @@ av8l_fast_prepopulate_pgtables(struct av8l_fast_io_pgtable *data, int i, j, pg = 0; struct page **pages, *page; - pages = kmalloc(sizeof(*pages) * NUM_PGTBL_PAGES, GFP_KERNEL); + pages = kmalloc(sizeof(*pages) * NUM_PGTBL_PAGES, __GFP_NOWARN | + __GFP_NORETRY); + + if (!pages) + pages = vmalloc(sizeof(*pages) * NUM_PGTBL_PAGES); + if (!pages) return -ENOMEM; @@ -418,7 +423,7 @@ err_free_pages: for (i = 0; i < pg; ++i) __free_page(pages[i]); err_free_pages_arr: - kfree(pages); + kvfree(pages); return -ENOMEM; } @@ -513,7 +518,7 @@ static void av8l_fast_free_pgtable(struct io_pgtable *iop) vunmap(data->pmds); for (i = 0; i < NUM_PGTBL_PAGES; ++i) __free_page(data->pages[i]); - kfree(data->pages); + kvfree(data->pages); kfree(data); } diff --git a/drivers/leds/leds-qpnp-flash.c b/drivers/leds/leds-qpnp-flash.c index c27c0593cd10..cd76941b87ca 100644 --- a/drivers/leds/leds-qpnp-flash.c +++ b/drivers/leds/leds-qpnp-flash.c @@ -226,6 +226,7 @@ struct flash_led_platform_data { }; struct qpnp_flash_led_buffer { + struct mutex debugfs_lock; /* Prevent thread concurrency */ size_t rpos; size_t wpos; size_t len; @@ -282,6 +283,7 @@ static int flash_led_dbgfs_file_open(struct qpnp_flash_led *led, log->rpos = 0; log->wpos = 0; log->len = logbufsize - sizeof(*log); + mutex_init(&log->debugfs_lock); led->log = log; led->buffer_cnt = 1; @@ -303,20 +305,26 @@ static int flash_led_dfs_close(struct inode *inode, struct file *file) if (led && led->log) { file->private_data = NULL; + mutex_destroy(&led->log->debugfs_lock); kfree(led->log); } return 0; } +#define MIN_BUFFER_WRITE_LEN 20 static int print_to_log(struct qpnp_flash_led_buffer *log, const char *fmt, ...) { va_list args; int cnt; - char *log_buf = &log->data[log->wpos]; + char *log_buf; size_t size = log->len - log->wpos; + if (size < MIN_BUFFER_WRITE_LEN) + return 0; /* not enough buffer left */ + + log_buf = &log->data[log->wpos]; va_start(args, fmt); cnt = vscnprintf(log_buf, size, fmt, args); va_end(args); @@ -330,29 +338,31 @@ static ssize_t flash_led_dfs_latched_reg_read(struct file *fp, char __user *buf, struct qpnp_flash_led *led = fp->private_data; struct qpnp_flash_led_buffer *log = led->log; uint val; - int rc; + int rc = 0; size_t len; size_t ret; - if (log->rpos >= log->wpos && led->buffer_cnt == 0) - return 0; + mutex_lock(&log->debugfs_lock); + if ((log->rpos >= log->wpos && led->buffer_cnt == 0) || + ((log->len - log->wpos) < MIN_BUFFER_WRITE_LEN)) + goto unlock_mutex; rc = regmap_read(led->regmap, INT_LATCHED_STS(led->base), &val); if (rc) { dev_err(&led->pdev->dev, "Unable to read from address %x, rc(%d)\n", INT_LATCHED_STS(led->base), rc); - return -EINVAL; + goto unlock_mutex; } led->buffer_cnt--; rc = print_to_log(log, "0x%05X ", INT_LATCHED_STS(led->base)); if (rc == 0) - return rc; + goto unlock_mutex; rc = print_to_log(log, "0x%02X ", val); if (rc == 0) - return rc; + goto unlock_mutex; if (log->wpos > 0 && log->data[log->wpos - 1] == ' ') log->data[log->wpos - 1] = '\n'; @@ -362,36 +372,43 @@ static ssize_t flash_led_dfs_latched_reg_read(struct file *fp, char __user *buf, ret = copy_to_user(buf, &log->data[log->rpos], len); if (ret) { pr_err("error copy register value to user\n"); - return -EFAULT; + rc = -EFAULT; + goto unlock_mutex; } len -= ret; *ppos += len; log->rpos += len; - return len; + rc = len; + +unlock_mutex: + mutex_unlock(&log->debugfs_lock); + return rc; } static ssize_t flash_led_dfs_fault_reg_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos) { struct qpnp_flash_led *led = fp->private_data; struct qpnp_flash_led_buffer *log = led->log; - int rc; + int rc = 0; size_t len; size_t ret; - if (log->rpos >= log->wpos && led->buffer_cnt == 0) - return 0; + mutex_lock(&log->debugfs_lock); + if ((log->rpos >= log->wpos && led->buffer_cnt == 0) || + ((log->len - log->wpos) < MIN_BUFFER_WRITE_LEN)) + goto unlock_mutex; led->buffer_cnt--; rc = print_to_log(log, "0x%05X ", FLASH_LED_FAULT_STATUS(led->base)); if (rc == 0) - return rc; + goto unlock_mutex; rc = print_to_log(log, "0x%02X ", led->fault_reg); if (rc == 0) - return rc; + goto unlock_mutex; if (log->wpos > 0 && log->data[log->wpos - 1] == ' ') log->data[log->wpos - 1] = '\n'; @@ -401,14 +418,19 @@ static ssize_t flash_led_dfs_fault_reg_read(struct file *fp, char __user *buf, ret = copy_to_user(buf, &log->data[log->rpos], len); if (ret) { pr_err("error copy register value to user\n"); - return -EFAULT; + rc = -EFAULT; + goto unlock_mutex; } len -= ret; *ppos += len; log->rpos += len; - return len; + rc = len; + +unlock_mutex: + mutex_unlock(&log->debugfs_lock); + return rc; } static ssize_t flash_led_dfs_fault_reg_enable(struct file *file, @@ -421,10 +443,14 @@ static ssize_t flash_led_dfs_fault_reg_enable(struct file *file, size_t ret = 0; struct qpnp_flash_led *led = file->private_data; - char *kbuf = kmalloc(count + 1, GFP_KERNEL); + char *kbuf; - if (!kbuf) - return -ENOMEM; + mutex_lock(&led->log->debugfs_lock); + kbuf = kmalloc(count + 1, GFP_KERNEL); + if (!kbuf) { + ret = -ENOMEM; + goto unlock_mutex; + } ret = copy_from_user(kbuf, buf, count); if (!ret) { @@ -453,6 +479,8 @@ static ssize_t flash_led_dfs_fault_reg_enable(struct file *file, free_buf: kfree(kbuf); +unlock_mutex: + mutex_unlock(&led->log->debugfs_lock); return ret; } @@ -465,10 +493,14 @@ static ssize_t flash_led_dfs_dbg_enable(struct file *file, int data; size_t ret = 0; struct qpnp_flash_led *led = file->private_data; - char *kbuf = kmalloc(count + 1, GFP_KERNEL); + char *kbuf; - if (!kbuf) - return -ENOMEM; + mutex_lock(&led->log->debugfs_lock); + kbuf = kmalloc(count + 1, GFP_KERNEL); + if (!kbuf) { + ret = -ENOMEM; + goto unlock_mutex; + } ret = copy_from_user(kbuf, buf, count); if (ret == count) { @@ -496,6 +528,8 @@ static ssize_t flash_led_dfs_dbg_enable(struct file *file, free_buf: kfree(kbuf); +unlock_mutex: + mutex_unlock(&led->log->debugfs_lock); return ret; } diff --git a/drivers/media/dvb-core/dvb_demux.c b/drivers/media/dvb-core/dvb_demux.c index 60f7cfbd9f9b..65eaf4066149 100644 --- a/drivers/media/dvb-core/dvb_demux.c +++ b/drivers/media/dvb-core/dvb_demux.c @@ -131,6 +131,39 @@ static const struct dvb_dmx_video_patterns h264_non_idr = { DMX_IDX_H264_NON_IDR_START }; +/* + * Forbidden (1 bit) + NAL idc (2 bits) + NAL type (5 bits) + * I-Slice NAL idc = 3, NAL type = 5, 01100101 mask 0x7F + */ +static const struct dvb_dmx_video_patterns h264_idr_islice = { + {0x00, 0x00, 0x01, 0x65, 0x80}, + {0xFF, 0xFF, 0xFF, 0x7F, 0x80}, + 5, + DMX_IDX_H264_IDR_ISLICE_START +}; + +/* + * Forbidden (1 bit) + NAL idc (2 bits) + NAL type (5 bits) + * P-Slice NAL idc = 2, NAL type = 1, 01000001 mask 0x7F + */ +static const struct dvb_dmx_video_patterns h264_non_idr_pslice = { + {0x00, 0x00, 0x01, 0x41, 0x80}, + {0xFF, 0xFF, 0xFF, 0x7F, 0x80}, + 5, + DMX_IDX_H264_NON_IDR_PSLICE_START +}; + +/* + * Forbidden (1 bit) + NAL idc (2 bits) + NAL type (5 bits) + * B-Slice NAL idc = 0, NAL type = 1, 00000001 mask 0x7F + */ +static const struct dvb_dmx_video_patterns h264_non_idr_bslice = { + {0x00, 0x00, 0x01, 0x01, 0x80}, + {0xFF, 0xFF, 0xFF, 0x7F, 0x80}, + 5, + DMX_IDX_H264_NON_IDR_BSLICE_START +}; + static const struct dvb_dmx_video_patterns h264_non_access_unit_del = { {0x00, 0x00, 0x01, 0x09}, {0xFF, 0xFF, 0xFF, 0x1F}, @@ -1011,6 +1044,18 @@ static void dvb_dmx_process_pattern_result(struct dvb_demux_feed *feed, } else if (feed->prev_frame_type & DMX_IDX_H264_NON_IDR_START) { idx_event.type = DMX_IDX_H264_NON_IDR_END; frame_end_in_seq = DMX_IDX_H264_FIRST_SPS_FRAME_END; + } else if (feed->prev_frame_type & + DMX_IDX_H264_IDR_ISLICE_START) { + idx_event.type = DMX_IDX_H264_IDR_END; + frame_end_in_seq = DMX_IDX_H264_FIRST_SPS_FRAME_END; + } else if (feed->prev_frame_type & + DMX_IDX_H264_NON_IDR_PSLICE_START) { + idx_event.type = DMX_IDX_H264_NON_IDR_END; + frame_end_in_seq = DMX_IDX_H264_FIRST_SPS_FRAME_END; + } else if (feed->prev_frame_type & + DMX_IDX_H264_NON_IDR_BSLICE_START) { + idx_event.type = DMX_IDX_H264_NON_IDR_END; + frame_end_in_seq = DMX_IDX_H264_FIRST_SPS_FRAME_END; } else { idx_event.type = DMX_IDX_VC1_FRAME_END; frame_end_in_seq = DMX_IDX_VC1_FIRST_SEQ_FRAME_END; @@ -1848,6 +1893,15 @@ const struct dvb_dmx_video_patterns *dvb_dmx_get_pattern(u64 dmx_idx_pattern) case DMX_IDX_H264_NON_IDR_START: return &h264_non_idr; + case DMX_IDX_H264_IDR_ISLICE_START: + return &h264_idr_islice; + + case DMX_IDX_H264_NON_IDR_PSLICE_START: + return &h264_non_idr_pslice; + + case DMX_IDX_H264_NON_IDR_BSLICE_START: + return &h264_non_idr_bslice; + case DMX_IDX_H264_ACCESS_UNIT_DEL: return &h264_non_access_unit_del; @@ -1975,6 +2029,40 @@ static void dvb_dmx_init_idx_state(struct dvb_demux_feed *feed) feed->pattern_num++; } + /* H264 IDR ISlice */ + if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) && + (feed->idx_params.types & + (DMX_IDX_H264_IDR_ISLICE_START | DMX_IDX_H264_IDR_END | + DMX_IDX_H264_NON_IDR_END | + DMX_IDX_H264_FIRST_SPS_FRAME_START | + DMX_IDX_H264_FIRST_SPS_FRAME_END))) { + feed->patterns[feed->pattern_num] = + dvb_dmx_get_pattern(DMX_IDX_H264_IDR_ISLICE_START); + feed->pattern_num++; + } + /* H264 non-IDR PSlice */ + if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) && + (feed->idx_params.types & + (DMX_IDX_H264_NON_IDR_PSLICE_START | DMX_IDX_H264_NON_IDR_END | + DMX_IDX_H264_IDR_END | + DMX_IDX_H264_FIRST_SPS_FRAME_START | + DMX_IDX_H264_FIRST_SPS_FRAME_END))) { + feed->patterns[feed->pattern_num] = + dvb_dmx_get_pattern(DMX_IDX_H264_NON_IDR_PSLICE_START); + feed->pattern_num++; + } + /* H264 non-IDR BSlice */ + if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) && + (feed->idx_params.types & + (DMX_IDX_H264_NON_IDR_BSLICE_START | DMX_IDX_H264_NON_IDR_END | + DMX_IDX_H264_IDR_END | + DMX_IDX_H264_FIRST_SPS_FRAME_START | + DMX_IDX_H264_FIRST_SPS_FRAME_END))) { + feed->patterns[feed->pattern_num] = + dvb_dmx_get_pattern(DMX_IDX_H264_NON_IDR_BSLICE_START); + feed->pattern_num++; + } + if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) && (feed->idx_params.types & DMX_IDX_H264_ACCESS_UNIT_DEL)) { feed->patterns[feed->pattern_num] = @@ -3253,7 +3341,8 @@ static int dvbdmx_get_pes_pids(struct dmx_demux *demux, u16 * pids) { struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; - memcpy(pids, dvbdemux->pids, 5 * sizeof(u16)); + /* 4 Demux Instances each with group of 5 pids */ + memcpy(pids, dvbdemux->pids, DMX_PES_OTHER*sizeof(u16)); return 0; } diff --git a/drivers/media/dvb-core/dvb_demux.h b/drivers/media/dvb-core/dvb_demux.h index 779de7ed078a..10a4cbbba2e7 100644 --- a/drivers/media/dvb-core/dvb_demux.h +++ b/drivers/media/dvb-core/dvb_demux.h @@ -360,6 +360,31 @@ static inline int dvb_dmx_is_video_feed(struct dvb_demux_feed *feed) } /** + * dvb_dmx_is_audio_feed - Returns whether the PES feed + * is audio one. + * + * @feed: The feed to be checked. + * + * Return 1 if feed is audio feed, 0 otherwise. + */ +static inline int dvb_dmx_is_audio_feed(struct dvb_demux_feed *feed) +{ + if (feed->type != DMX_TYPE_TS) + return 0; + + if (feed->ts_type & (~TS_DECODER)) + return 0; + + if ((feed->pes_type == DMX_PES_AUDIO0) || + (feed->pes_type == DMX_PES_AUDIO1) || + (feed->pes_type == DMX_PES_AUDIO2) || + (feed->pes_type == DMX_PES_AUDIO3)) + return 1; + + return 0; +} + +/** * dvb_dmx_is_pcr_feed - Returns whether the PES feed * is PCR one. * diff --git a/drivers/media/platform/msm/broadcast/tspp.c b/drivers/media/platform/msm/broadcast/tspp.c index 275b8b90af05..b706d598e6b8 100644 --- a/drivers/media/platform/msm/broadcast/tspp.c +++ b/drivers/media/platform/msm/broadcast/tspp.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, 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 @@ -45,6 +45,8 @@ #include <linux/string.h> #include <linux/msm-bus.h> #include <linux/interrupt.h> /* tasklet */ +#include <asm/arch_timer.h> /* Timer */ +#include <linux/avtimer_kernel.h> /* Timer */ /* * General defines @@ -100,6 +102,7 @@ #define TSIF_TEST_RESET_OFF (0x1c) #define TSIF_TEST_EXPORT_OFF (0x20) #define TSIF_TEST_CURRENT_OFF (0x24) +#define TSIF_TTS_CTL_OFF (0x38) #define TSIF_DATA_PORT_OFF (0x100) @@ -128,6 +131,12 @@ #define TSIF_STS_CTL_STOP BIT(3) #define TSIF_STS_CTL_START BIT(0) +/* bits for TSIF_TTS_CTRL register */ +#define TSIF_TTS_CTL_TTS_ENDIANNESS BIT(4) +#define TSIF_TTS_CTL_TTS_SOURCE BIT(3) +#define TSIF_TTS_CTL_TTS_LENGTH_1 BIT(1) +#define TSIF_TTS_CTL_TTS_LENGTH_0 BIT(0) + /* * TSPP register offsets */ @@ -255,6 +264,7 @@ static const struct debugfs_entry debugfs_tsif_regs[] = { {"test_export", S_IRUGO | S_IWUSR, TSIF_TEST_EXPORT_OFF}, {"test_current", S_IRUGO, TSIF_TEST_CURRENT_OFF}, {"data_port", S_IRUSR, TSIF_DATA_PORT_OFF}, + {"tts_source", S_IRUSR | S_IWUSR, TSIF_TTS_CTL_OFF}, }; static const struct debugfs_entry debugfs_tspp_regs[] = { @@ -369,6 +379,8 @@ struct tspp_tsif_device { u32 stat_overflow; u32 stat_lost_sync; u32 stat_timeout; + enum tsif_tts_source tts_source; + u32 lpass_timer_enable; }; enum tspp_buf_state { @@ -477,6 +489,7 @@ struct tspp_device { /* pinctrl */ struct mutex mutex; struct tspp_pinctrl pinctrl; + unsigned int tts_source; /* Time stamp source type LPASS timer/TCR */ struct dentry *dent; struct dentry *debugfs_regs[ARRAY_SIZE(debugfs_tspp_regs)]; @@ -911,6 +924,8 @@ static int tspp_start_tsif(struct tspp_tsif_device *tsif_device) { int start_hardware = 0; u32 ctl; + u32 tts_ctl; + int retval; if (tsif_device->ref_count == 0) { start_hardware = 1; @@ -949,19 +964,57 @@ static int tspp_start_tsif(struct tspp_tsif_device *tsif_device) TSIF_STS_CTL_TEST_MODE; break; case TSPP_TSIF_MODE_1: - ctl |= TSIF_STS_CTL_EN_TIME_LIM | - TSIF_STS_CTL_EN_TCR; + ctl |= TSIF_STS_CTL_EN_TIME_LIM; + if (tsif_device->tts_source != TSIF_TTS_LPASS_TIMER) + ctl |= TSIF_STS_CTL_EN_TCR; break; case TSPP_TSIF_MODE_2: ctl |= TSIF_STS_CTL_EN_TIME_LIM | - TSIF_STS_CTL_EN_TCR | TSIF_STS_CTL_MODE_2; + if (tsif_device->tts_source != TSIF_TTS_LPASS_TIMER) + ctl |= TSIF_STS_CTL_EN_TCR; break; default: pr_warn("tspp: unknown tsif mode 0x%x", tsif_device->mode); } + /* Set 4bytes Time Stamp for TCR */ + if (tsif_device->tts_source == TSIF_TTS_LPASS_TIMER) { + if (tsif_device->lpass_timer_enable == 0) { + retval = avcs_core_open(); + if (retval < 0) { + pr_warn("tspp: avcs open fail:%d\n", + retval); + return retval; + } + retval = avcs_core_disable_power_collapse(1); + if (retval < 0) { + pr_warn("tspp: avcs power enable:%d\n", + retval); + return retval; + } + tsif_device->lpass_timer_enable = 1; + } + + tts_ctl = readl_relaxed(tsif_device->base + + TSIF_TTS_CTL_OFF); + tts_ctl = 0; + /* Set LPASS Timer TTS source */ + tts_ctl |= TSIF_TTS_CTL_TTS_SOURCE; + /* Set 4 byte TTS */ + tts_ctl |= TSIF_TTS_CTL_TTS_LENGTH_0; + + writel_relaxed(tts_ctl, tsif_device->base + + TSIF_TTS_CTL_OFF); + /* write TTS control register */ + wmb(); + tts_ctl = readl_relaxed(tsif_device->base + + TSIF_TTS_CTL_OFF); + } + writel_relaxed(ctl, tsif_device->base + TSIF_STS_CTL_OFF); + /* write Status control register */ + wmb(); writel_relaxed(tsif_device->time_limit, tsif_device->base + TSIF_TIME_LIMIT_OFF); /* assure register configuration is done before starting TSIF */ @@ -982,8 +1035,13 @@ static int tspp_start_tsif(struct tspp_tsif_device *tsif_device) static void tspp_stop_tsif(struct tspp_tsif_device *tsif_device) { - if (tsif_device->ref_count == 0) + if (tsif_device->ref_count == 0) { + if (tsif_device->lpass_timer_enable == 1) { + if (avcs_core_disable_power_collapse(0) == 0) + tsif_device->lpass_timer_enable = 0; + } return; + } tsif_device->ref_count--; @@ -1113,6 +1171,7 @@ static int tspp_global_reset(struct tspp_device *pdev) pdev->tsif[i].data_inverse = 0; pdev->tsif[i].sync_inverse = 0; pdev->tsif[i].enable_inverse = 0; + pdev->tsif[i].lpass_timer_enable = 0; } writel_relaxed(TSPP_RST_RESET, pdev->base + TSPP_RST); /* assure state is reset before continuing with configuration */ @@ -1885,6 +1944,89 @@ int tspp_get_ref_clk_counter(u32 dev, enum tspp_source source, u32 *tcr_counter) EXPORT_SYMBOL(tspp_get_ref_clk_counter); /** + * tspp_get_lpass_time_counter - return the LPASS Timer counter value. + * + * @dev: TSPP device (up to TSPP_MAX_DEVICES) + * @source: The TSIF source from which the counter should be read + * @tcr_counter: the value of TCR counter + * + * Return error status + * + * If source is neither TSIF 0 or TSIF1 0 is returned. + */ +int tspp_get_lpass_time_counter(u32 dev, enum tspp_source source, + u64 *lpass_time_counter) +{ + struct tspp_device *pdev; + struct tspp_tsif_device *tsif_device; + + if (!lpass_time_counter) + return -EINVAL; + + pdev = tspp_find_by_id(dev); + if (!pdev) { + pr_err("tspp_get_lpass_time_counter: can't find device %i\n", + dev); + return -ENODEV; + } + + switch (source) { + case TSPP_SOURCE_TSIF0: + tsif_device = &pdev->tsif[0]; + break; + + case TSPP_SOURCE_TSIF1: + tsif_device = &pdev->tsif[1]; + break; + + default: + tsif_device = NULL; + break; + } + + if (tsif_device && tsif_device->ref_count) { + if (avcs_core_query_timer(lpass_time_counter) < 0) { + pr_err("tspp_get_lpass_time_counter: read error\n"); + *lpass_time_counter = 0; + return -ENETRESET; + } + } else + *lpass_time_counter = 0; + + return 0; +} +EXPORT_SYMBOL(tspp_get_lpass_time_counter); + +/** + * tspp_get_tts_source - Return the TTS source value. + * + * @dev: TSPP device (up to TSPP_MAX_DEVICES) + * @tts_source:Updated TTS source type + * + * Return error status + * + */ +int tspp_get_tts_source(u32 dev, int *tts_source) +{ + struct tspp_device *pdev; + + if (tts_source == NULL) + return -EINVAL; + + pdev = tspp_find_by_id(dev); + if (!pdev) { + pr_err("tspp_get_tts_source: can't find device %i\n", + dev); + return -ENODEV; + } + + *tts_source = pdev->tts_source; + + return 0; +} +EXPORT_SYMBOL(tspp_get_tts_source); + +/** * tspp_add_filter - add a TSPP filter to a channel. * * @dev: TSPP device (up to TSPP_MAX_DEVICES) @@ -2891,6 +3033,20 @@ static int msm_tspp_probe(struct platform_device *pdev) goto err_irq; device->req_irqs = false; + /* Check whether AV timer time stamps are enabled */ + if (!of_property_read_u32(pdev->dev.of_node, "qcom,lpass-timer-tts", + &device->tts_source)) { + if (device->tts_source == 1) + device->tts_source = TSIF_TTS_LPASS_TIMER; + else + device->tts_source = TSIF_TTS_TCR; + } else { + device->tts_source = TSIF_TTS_TCR; + } + + for (i = 0; i < TSPP_TSIF_INSTANCES; i++) + device->tsif[i].tts_source = device->tts_source; + /* power management */ pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); diff --git a/drivers/media/platform/msm/dvb/adapter/Makefile b/drivers/media/platform/msm/dvb/adapter/Makefile index f7da6b5b2f06..662bf99c4d7e 100644 --- a/drivers/media/platform/msm/dvb/adapter/Makefile +++ b/drivers/media/platform/msm/dvb/adapter/Makefile @@ -1,5 +1,6 @@ ccflags-y += -Idrivers/media/dvb-core/ ccflags-y += -Idrivers/media/platform/msm/dvb/include/ +ccflags-y += -Idrivers/media/platform/msm/dvb/demux/ obj-$(CONFIG_DVB_MPQ) += mpq-adapter.o diff --git a/drivers/media/platform/msm/dvb/demux/Kconfig b/drivers/media/platform/msm/dvb/demux/Kconfig index 319e2ab2eb96..d160c820d190 100644 --- a/drivers/media/platform/msm/dvb/demux/Kconfig +++ b/drivers/media/platform/msm/dvb/demux/Kconfig @@ -44,3 +44,11 @@ choice TSPP hardware support. All demux tasks will be performed in SW. endchoice + +config DVB_MPQ_MEDIA_BOX_DEMUX + bool "Media box demux support" + depends on DVB_MPQ_DEMUX + + help + Use this option if your HW is Qualcomm media box and demux + support is required on that media box. diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c index 0188637af85c..ef0ef1512211 100644 --- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c +++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, 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 @@ -64,6 +64,11 @@ static int video_nonsecure_ion_heap = ION_IOMMU_HEAP_ID; module_param(video_nonsecure_ion_heap, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(video_nonsecure_ion_heap, "ION heap for non-secure video buffer allocation"); +/* ION heap IDs used for allocating audio output buffer */ +static int audio_nonsecure_ion_heap = ION_IOMMU_HEAP_ID; +module_param(audio_nonsecure_ion_heap, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(audio_nonsecure_ion_heap, "ION heap for non-secure audio buffer allocation"); + /* Value of TS packet scramble bits field for even key */ static int mpq_sdmx_scramble_even = 0x2; module_param(mpq_sdmx_scramble_even, int, S_IRUGO | S_IWUSR); @@ -104,6 +109,25 @@ module_param(tsif_mode, int, S_IRUGO | S_IWUSR); static int clock_inv; module_param(clock_inv, int, S_IRUGO | S_IWUSR); +/* TSIF Timestamp source: 0 = TSIF Clock Reference, 1 = LPASS time counter */ +enum tsif_tts_source { + TSIF_TTS_TCR = 0, /* Time stamps from TCR counter */ + TSIF_TTS_LPASS_TIMER /* Time stamps from AV/Qtimer Timer */ +}; + +/* Store all mpq feeds corresponding to 4 TS programs in a Transport Stream */ +static struct mpq_feed *store_mpq_audio_feed[CONFIG_DVB_MPQ_NUM_DMX_DEVICES] = { + NULL, NULL, NULL, NULL}; +static struct mpq_feed *store_mpq_video_feed[CONFIG_DVB_MPQ_NUM_DMX_DEVICES] = { + NULL, NULL, NULL, NULL}; +static int non_predicted_video_frame; +/* trigger video ES frame events on MPEG2 B frames and H264 non-IDR frames */ +#ifdef CONFIG_DVB_MPQ_MEDIA_BOX_DEMUX +static int video_b_frame_events = 1; +#else +static int video_b_frame_events; +#endif + /* Global data-structure for managing demux devices */ static struct { @@ -147,6 +171,96 @@ int mpq_dmx_get_param_clock_inv(void) return clock_inv; } +struct mpq_streambuffer *consumer_video_streambuffer(int dmx_ts_pes_video) +{ + struct mpq_streambuffer *streambuffer = NULL; + struct mpq_video_feed_info *feed_data = NULL; + + switch (dmx_ts_pes_video) { + case DMX_PES_VIDEO0: + if (store_mpq_video_feed[0] != NULL) { + feed_data = &store_mpq_video_feed[0]->video_info; + feed_data->stream_interface = + MPQ_ADAPTER_VIDEO0_STREAM_IF; + } + break; + case DMX_PES_VIDEO1: + if (store_mpq_video_feed[1] != NULL) { + feed_data = &store_mpq_video_feed[1]->video_info; + feed_data->stream_interface = + MPQ_ADAPTER_VIDEO1_STREAM_IF; + } + break; + case DMX_PES_VIDEO2: + if (store_mpq_video_feed[2] != NULL) { + feed_data = &store_mpq_video_feed[2]->video_info; + feed_data->stream_interface = + MPQ_ADAPTER_VIDEO2_STREAM_IF; + } + break; + case DMX_PES_VIDEO3: + if (store_mpq_video_feed[3] != NULL) { + feed_data = &store_mpq_video_feed[3]->video_info; + feed_data->stream_interface = + MPQ_ADAPTER_VIDEO3_STREAM_IF; + } + break; + } + + if (feed_data != NULL) + mpq_adapter_get_stream_if(feed_data->stream_interface, + &streambuffer); + + return streambuffer; +} +EXPORT_SYMBOL(consumer_video_streambuffer); + +struct mpq_streambuffer *consumer_audio_streambuffer(int dmx_ts_pes_audio) +{ + struct mpq_streambuffer *streambuffer = NULL; + struct mpq_audio_feed_info *feed_data = NULL; + + switch (dmx_ts_pes_audio) { + case DMX_PES_AUDIO0: + if (store_mpq_audio_feed[0] != NULL) { + feed_data = &store_mpq_audio_feed[0]->audio_info; + feed_data->stream_interface = + MPQ_ADAPTER_AUDIO0_STREAM_IF; + } + break; + case DMX_PES_AUDIO1: + if (store_mpq_audio_feed[1] != NULL) { + feed_data = &store_mpq_audio_feed[1]->audio_info; + feed_data->stream_interface = + MPQ_ADAPTER_AUDIO1_STREAM_IF; + } + break; + case DMX_PES_AUDIO2: + if (store_mpq_audio_feed[2] != NULL) { + feed_data = &store_mpq_audio_feed[2]->audio_info; + feed_data->stream_interface = + MPQ_ADAPTER_AUDIO2_STREAM_IF; + } + break; + case DMX_PES_AUDIO3: + if (store_mpq_audio_feed[3] != NULL) { + feed_data = &store_mpq_audio_feed[3]->audio_info; + feed_data->stream_interface = + MPQ_ADAPTER_AUDIO3_STREAM_IF; + } + break; + } + + if (feed_data != NULL) + mpq_adapter_get_stream_if(feed_data->stream_interface, + &streambuffer); + + return streambuffer; +} +EXPORT_SYMBOL(consumer_audio_streambuffer); + + + /* Check that PES header is valid and that it is a video PES */ static int mpq_dmx_is_valid_video_pes(struct pes_packet_header *pes_header) { @@ -163,6 +277,25 @@ static int mpq_dmx_is_valid_video_pes(struct pes_packet_header *pes_header) return 0; } +static int mpq_dmx_is_valid_audio_pes(struct pes_packet_header *pes_header) +{ + /* start-code valid? */ + if ((pes_header->packet_start_code_prefix_1 != 0) || + (pes_header->packet_start_code_prefix_2 != 0) || + (pes_header->packet_start_code_prefix_3 != 1)) + return -EINVAL; + + /* Note: AC3 stream ID = 0xBD */ + if (pes_header->stream_id == 0xBD) + return 0; + + /* stream_id is audio? */ /* 110x xxxx = Audio Stream IDs */ + if ((pes_header->stream_id & 0xE0) != 0xC0) + return -EINVAL; + + return 0; +} + /* Check if a framing pattern is a video frame pattern or a header pattern */ static inline int mpq_dmx_is_video_frame( enum dmx_video_codec codec, @@ -170,6 +303,10 @@ static inline int mpq_dmx_is_video_frame( { switch (codec) { case DMX_VIDEO_CODEC_MPEG2: + if (video_b_frame_events == 1) + if (pattern_type == DMX_IDX_MPEG_B_FRAME_START) + non_predicted_video_frame = 1; + if ((pattern_type == DMX_IDX_MPEG_I_FRAME_START) || (pattern_type == DMX_IDX_MPEG_P_FRAME_START) || (pattern_type == DMX_IDX_MPEG_B_FRAME_START)) @@ -177,9 +314,20 @@ static inline int mpq_dmx_is_video_frame( return 0; case DMX_VIDEO_CODEC_H264: - if ((pattern_type == DMX_IDX_H264_IDR_START) || - (pattern_type == DMX_IDX_H264_NON_IDR_START)) - return 1; + if (video_b_frame_events == 1) { + if (pattern_type == DMX_IDX_H264_NON_IDR_BSLICE_START) + non_predicted_video_frame = 1; + + if ((pattern_type == DMX_IDX_H264_IDR_ISLICE_START) || + (pattern_type == + DMX_IDX_H264_NON_IDR_PSLICE_START) || + (pattern_type == DMX_IDX_H264_NON_IDR_BSLICE_START)) + return 1; + } else { + if ((pattern_type == DMX_IDX_H264_IDR_START) || + (pattern_type == DMX_IDX_H264_NON_IDR_START)) + return 1; + } return 0; case DMX_VIDEO_CODEC_VC1: @@ -219,10 +367,23 @@ static inline int mpq_dmx_get_pattern_params( case DMX_VIDEO_CODEC_H264: patterns[0] = dvb_dmx_get_pattern(DMX_IDX_H264_SPS); patterns[1] = dvb_dmx_get_pattern(DMX_IDX_H264_PPS); - patterns[2] = dvb_dmx_get_pattern(DMX_IDX_H264_IDR_START); - patterns[3] = dvb_dmx_get_pattern(DMX_IDX_H264_NON_IDR_START); - patterns[4] = dvb_dmx_get_pattern(DMX_IDX_H264_SEI); - *patterns_num = 5; + if (video_b_frame_events != 1) { + patterns[2] = dvb_dmx_get_pattern + (DMX_IDX_H264_IDR_START); + patterns[3] = dvb_dmx_get_pattern + (DMX_IDX_H264_NON_IDR_START); + patterns[4] = dvb_dmx_get_pattern(DMX_IDX_H264_SEI); + *patterns_num = 5; + } else { + patterns[2] = dvb_dmx_get_pattern + (DMX_IDX_H264_IDR_ISLICE_START); + patterns[3] = dvb_dmx_get_pattern + (DMX_IDX_H264_NON_IDR_PSLICE_START); + patterns[4] = dvb_dmx_get_pattern + (DMX_IDX_H264_NON_IDR_BSLICE_START); + patterns[5] = dvb_dmx_get_pattern(DMX_IDX_H264_SEI); + *patterns_num = 6; + } break; case DMX_VIDEO_CODEC_VC1: @@ -254,12 +415,20 @@ void mpq_dmx_update_decoder_stat(struct mpq_feed *mpq_feed) struct mpq_demux *mpq_demux = mpq_feed->mpq_demux; enum mpq_adapter_stream_if idx; - if (!dvb_dmx_is_video_feed(mpq_feed->dvb_demux_feed) || - (mpq_feed->video_info.stream_interface > - MPQ_ADAPTER_VIDEO3_STREAM_IF)) + if (!dvb_dmx_is_video_feed(mpq_feed->dvb_demux_feed) && + !dvb_dmx_is_audio_feed(mpq_feed->dvb_demux_feed)) return; - idx = mpq_feed->video_info.stream_interface; + if (dvb_dmx_is_video_feed(mpq_feed->dvb_demux_feed) && + mpq_feed->video_info.stream_interface <= + MPQ_ADAPTER_VIDEO3_STREAM_IF) + idx = mpq_feed->video_info.stream_interface; + else if (dvb_dmx_is_audio_feed(mpq_feed->dvb_demux_feed) && + mpq_feed->audio_info.stream_interface <= + MPQ_ADAPTER_AUDIO3_STREAM_IF) + idx = mpq_feed->audio_info.stream_interface; + else + return; curr_time = current_kernel_time(); if (unlikely(!mpq_demux->decoder_stat[idx].out_count)) { @@ -703,6 +872,7 @@ int mpq_dmx_plugin_init(mpq_dmx_init dmx_init_func) mpq_demux->sdmx_session_handle = SDMX_INVALID_SESSION_HANDLE; mpq_demux->sdmx_eos = 0; mpq_demux->sdmx_log_level = SDMX_LOG_NO_PRINT; + mpq_demux->ts_packet_timestamp_source = 0; if (mpq_demux->demux.feednum > MPQ_MAX_DMX_FILES) { MPQ_DVB_ERR_PRINT( @@ -1050,9 +1220,33 @@ int mpq_dmx_reuse_decoder_buffer(struct dvb_demux_feed *feed, int cookie) mutex_unlock(&mpq_demux->mutex); return ret; - } + } else if (dvb_dmx_is_audio_feed(feed)) { + struct mpq_audio_feed_info *feed_data; + struct mpq_feed *mpq_feed; + struct mpq_streambuffer *stream_buffer; + int ret; - /* else */ + mutex_lock(&mpq_demux->mutex); + mpq_feed = feed->priv; + feed_data = &mpq_feed->audio_info; + + spin_lock(&feed_data->audio_buffer_lock); + stream_buffer = feed_data->audio_buffer; + if (stream_buffer == NULL) { + MPQ_DVB_ERR_PRINT( + "%s: invalid feed, feed_data->audio_buffer is NULL\n", + __func__); + spin_unlock(&feed_data->audio_buffer_lock); + mutex_unlock(&mpq_demux->mutex); + return -EINVAL; + } + + ret = mpq_streambuffer_pkt_dispose(stream_buffer, cookie, 1); + spin_unlock(&feed_data->audio_buffer_lock); + mutex_unlock(&mpq_demux->mutex); + + return ret; + } MPQ_DVB_ERR_PRINT("%s: Invalid feed type %d\n", __func__, feed->pes_type); @@ -1383,6 +1577,295 @@ int mpq_dmx_flush_stream_buffer(struct dvb_demux_feed *feed) return ret; } +static int mpq_dmx_init_audio_internal_buffers( + struct mpq_demux *mpq_demux, + struct mpq_audio_feed_info *feed_data, + struct dmx_decoder_buffers *dec_buffs) +{ + struct ion_handle *temp_handle = NULL; + void *payload_buffer = NULL; + int actual_buffer_size = 0; + int ret = 0; + + MPQ_DVB_DBG_PRINT("%s: Internal audio decoder buffer allocation\n", + __func__); + + actual_buffer_size = dec_buffs->buffers_size; + actual_buffer_size += (SZ_4K - 1); + actual_buffer_size &= ~(SZ_4K - 1); + + temp_handle = ion_alloc(mpq_demux->ion_client, + actual_buffer_size, SZ_4K, + ION_HEAP(audio_nonsecure_ion_heap), + mpq_demux->decoder_alloc_flags); + + if (IS_ERR_OR_NULL(temp_handle)) { + ret = PTR_ERR(temp_handle); + MPQ_DVB_ERR_PRINT( + "%s: FAILED to allocate audio payload buffer %d\n", + __func__, ret); + if (!ret) + ret = -ENOMEM; + goto end; + } + + payload_buffer = ion_map_kernel(mpq_demux->ion_client, temp_handle); + + if (IS_ERR_OR_NULL(payload_buffer)) { + ret = PTR_ERR(payload_buffer); + MPQ_DVB_ERR_PRINT( + "%s: FAILED to map audio payload buffer %d\n", + __func__, ret); + if (!ret) + ret = -ENOMEM; + goto init_failed_free_payload_buffer; + } + feed_data->buffer_desc.decoder_buffers_num = 1; + feed_data->buffer_desc.ion_handle[0] = temp_handle; + feed_data->buffer_desc.desc[0].base = payload_buffer; + feed_data->buffer_desc.desc[0].size = actual_buffer_size; + feed_data->buffer_desc.desc[0].read_ptr = 0; + feed_data->buffer_desc.desc[0].write_ptr = 0; + feed_data->buffer_desc.desc[0].handle = + ion_share_dma_buf_fd(mpq_demux->ion_client, temp_handle); + if (IS_ERR_VALUE(feed_data->buffer_desc.desc[0].handle)) { + MPQ_DVB_ERR_PRINT( + "%s: FAILED to share audio payload buffer %d\n", + __func__, ret); + ret = -ENOMEM; + goto init_failed_unmap_payload_buffer; + } + + feed_data->buffer_desc.shared_file = fget( + feed_data->buffer_desc.desc[0].handle); + + return 0; + +init_failed_unmap_payload_buffer: + ion_unmap_kernel(mpq_demux->ion_client, temp_handle); + feed_data->buffer_desc.desc[0].base = NULL; +init_failed_free_payload_buffer: + ion_free(mpq_demux->ion_client, temp_handle); + feed_data->buffer_desc.ion_handle[0] = NULL; + feed_data->buffer_desc.desc[0].size = 0; + feed_data->buffer_desc.decoder_buffers_num = 0; + feed_data->buffer_desc.shared_file = NULL; +end: + return ret; +} + +static int mpq_dmx_init_audio_external_buffers( + struct mpq_audio_feed_info *feed_data, + struct dmx_decoder_buffers *dec_buffs, + struct ion_client *client) +{ + struct ion_handle *temp_handle = NULL; + void *payload_buffer = NULL; + int actual_buffer_size = 0; + int ret = 0; + int i; + + /* + * Payload buffer was allocated externally (through ION). + * Map the ion handles to kernel memory + */ + MPQ_DVB_DBG_PRINT("%s: External audio decoder buffer allocation\n", + __func__); + + actual_buffer_size = dec_buffs->buffers_size; + if (!dec_buffs->is_linear) { + MPQ_DVB_DBG_PRINT("%s: Ex. Ring-buffer\n", __func__); + feed_data->buffer_desc.decoder_buffers_num = 1; + } else { + MPQ_DVB_DBG_PRINT("%s: Ex. Linear\n", __func__); + feed_data->buffer_desc.decoder_buffers_num = + dec_buffs->buffers_num; + } + + for (i = 0; i < feed_data->buffer_desc.decoder_buffers_num; i++) { + ret = mpq_map_buffer_to_kernel( + client, + dec_buffs->handles[i], + &temp_handle, + &payload_buffer); + if (ret < 0) { + MPQ_DVB_ERR_PRINT( + "%s: Failed mapping audio buffer %d\n", + __func__, i); + goto init_failed; + } + feed_data->buffer_desc.ion_handle[i] = temp_handle; + feed_data->buffer_desc.desc[i].base = payload_buffer; + feed_data->buffer_desc.desc[i].handle = + dec_buffs->handles[i]; + feed_data->buffer_desc.desc[i].size = + dec_buffs->buffers_size; + feed_data->buffer_desc.desc[i].read_ptr = 0; + feed_data->buffer_desc.desc[i].write_ptr = 0; + + MPQ_DVB_DBG_PRINT( + "%s: Audio Buffer #%d: base=0x%p, handle=%d, size=%d\n", + __func__, i, + feed_data->buffer_desc.desc[i].base, + feed_data->buffer_desc.desc[i].handle, + feed_data->buffer_desc.desc[i].size); + } + + return 0; + +init_failed: + for (i = 0; i < feed_data->buffer_desc.decoder_buffers_num; i++) { + if (feed_data->buffer_desc.ion_handle[i]) { + if (feed_data->buffer_desc.desc[i].base) { + ion_unmap_kernel(client, + feed_data->buffer_desc.ion_handle[i]); + feed_data->buffer_desc.desc[i].base = NULL; + } + ion_free(client, feed_data->buffer_desc.ion_handle[i]); + feed_data->buffer_desc.ion_handle[i] = NULL; + feed_data->buffer_desc.desc[i].size = 0; + } + } + return ret; +} +static int mpq_dmx_init_audio_streambuffer( + struct mpq_feed *feed, + struct mpq_audio_feed_info *feed_data, + struct mpq_streambuffer *stream_buffer) +{ + int ret; + void *packet_buffer = NULL; + struct mpq_demux *mpq_demux = feed->mpq_demux; + struct ion_client *client = mpq_demux->ion_client; + struct dmx_decoder_buffers *dec_buffs = NULL; + enum mpq_streambuffer_mode mode; + + dec_buffs = feed->dvb_demux_feed->feed.ts.decoder_buffers; + + /* Allocate packet buffer holding the meta-data */ + packet_buffer = vmalloc(AUDIO_META_DATA_BUFFER_SIZE); + + if (packet_buffer == NULL) { + MPQ_DVB_ERR_PRINT( + "%s: FAILED to allocate packets buffer\n", __func__); + ret = -ENOMEM; + goto end; + } + + MPQ_DVB_DBG_PRINT("%s: dec_buffs: num=%d, size=%d, linear=%d\n", + __func__, dec_buffs->buffers_num, + dec_buffs->buffers_size, + dec_buffs->is_linear); + + if (dec_buffs->buffers_num == 0) + ret = mpq_dmx_init_audio_internal_buffers( + mpq_demux, feed_data, dec_buffs); + else + ret = mpq_dmx_init_audio_external_buffers( + feed_data, dec_buffs, client); + + if (ret != 0) + goto init_failed_free_packet_buffer; + + mode = dec_buffs->is_linear ? MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR : + MPQ_STREAMBUFFER_BUFFER_MODE_RING; + ret = mpq_streambuffer_init( + feed_data->audio_buffer, + mode, + feed_data->buffer_desc.desc, + feed_data->buffer_desc.decoder_buffers_num, + packet_buffer, + AUDIO_META_DATA_BUFFER_SIZE); + + if (ret != 0) + goto init_failed_free_packet_buffer; + + goto end; + + +init_failed_free_packet_buffer: + vfree(packet_buffer); +end: + return ret; +} + +static void mpq_dmx_release_audio_streambuffer( + struct mpq_feed *feed, + struct mpq_audio_feed_info *feed_data, + struct mpq_streambuffer *audio_buffer, + struct ion_client *client) +{ + int buf_num = 0; + int i; + struct dmx_decoder_buffers *dec_buffs = + feed->dvb_demux_feed->feed.ts.decoder_buffers; + + mpq_adapter_unregister_stream_if(feed_data->stream_interface); + + mpq_streambuffer_terminate(audio_buffer); + + vfree(audio_buffer->packet_data.data); + + buf_num = feed_data->buffer_desc.decoder_buffers_num; + + for (i = 0; i < buf_num; i++) { + if (feed_data->buffer_desc.ion_handle[i]) { + if (feed_data->buffer_desc.desc[i].base) { + ion_unmap_kernel(client, + feed_data->buffer_desc.ion_handle[i]); + feed_data->buffer_desc.desc[i].base = NULL; + } + + /* + * Un-share the buffer if kernel is the one that + * shared it. + */ + if (!dec_buffs->buffers_num && + feed_data->buffer_desc.shared_file) { + fput(feed_data->buffer_desc.shared_file); + feed_data->buffer_desc.shared_file = NULL; + } + + ion_free(client, feed_data->buffer_desc.ion_handle[i]); + feed_data->buffer_desc.ion_handle[i] = NULL; + feed_data->buffer_desc.desc[i].size = 0; + } + } +} + +int mpq_dmx_flush_audio_stream_buffer(struct dvb_demux_feed *feed) +{ + struct mpq_feed *mpq_feed = feed->priv; + struct mpq_audio_feed_info *feed_data = &mpq_feed->audio_info; + struct mpq_streambuffer *sbuff; + int ret = 0; + + if (!dvb_dmx_is_audio_feed(feed)) { + MPQ_DVB_DBG_PRINT("%s: not a audio feed, feed type=%d\n", + __func__, feed->pes_type); + return 0; + } + + spin_lock(&feed_data->audio_buffer_lock); + + sbuff = feed_data->audio_buffer; + if (sbuff == NULL) { + MPQ_DVB_DBG_PRINT("%s: feed_data->audio_buffer is NULL\n", + __func__); + spin_unlock(&feed_data->audio_buffer_lock); + return -ENODEV; + } + + ret = mpq_streambuffer_flush(sbuff); + if (ret) + MPQ_DVB_ERR_PRINT("%s: mpq_streambuffer_flush failed, ret=%d\n", + __func__, ret); + + spin_unlock(&feed_data->audio_buffer_lock); + + return ret; +} + static int mpq_dmx_flush_buffer(struct dmx_ts_feed *ts_feed, size_t length) { struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed; @@ -1398,6 +1881,10 @@ static int mpq_dmx_flush_buffer(struct dmx_ts_feed *ts_feed, size_t length) MPQ_DVB_DBG_PRINT("%s: flushing video buffer\n", __func__); ret = mpq_dmx_flush_stream_buffer(feed); + } else if (dvb_dmx_is_audio_feed(feed)) { + MPQ_DVB_DBG_PRINT("%s: flushing audio buffer\n", __func__); + + ret = mpq_dmx_flush_audio_stream_buffer(feed); } mutex_unlock(&demux->mutex); @@ -1437,21 +1924,25 @@ int mpq_dmx_init_video_feed(struct mpq_feed *mpq_feed) /* Register the new stream-buffer interface to MPQ adapter */ switch (mpq_feed->dvb_demux_feed->pes_type) { case DMX_PES_VIDEO0: + store_mpq_video_feed[0] = mpq_feed; feed_data->stream_interface = MPQ_ADAPTER_VIDEO0_STREAM_IF; break; case DMX_PES_VIDEO1: + store_mpq_video_feed[1] = mpq_feed; feed_data->stream_interface = MPQ_ADAPTER_VIDEO1_STREAM_IF; break; case DMX_PES_VIDEO2: + store_mpq_video_feed[2] = mpq_feed; feed_data->stream_interface = MPQ_ADAPTER_VIDEO2_STREAM_IF; break; case DMX_PES_VIDEO3: + store_mpq_video_feed[3] = mpq_feed; feed_data->stream_interface = MPQ_ADAPTER_VIDEO3_STREAM_IF; break; @@ -1551,6 +2042,126 @@ init_failed_free_priv_data: return ret; } +/* Register the new stream-buffer interface to MPQ adapter */ +int mpq_dmx_init_audio_feed(struct mpq_feed *mpq_feed) +{ + int ret; + struct mpq_audio_feed_info *feed_data = &mpq_feed->audio_info; + struct mpq_demux *mpq_demux = mpq_feed->mpq_demux; + struct mpq_streambuffer *stream_buffer; + + switch (mpq_feed->dvb_demux_feed->pes_type) { + case DMX_PES_AUDIO0: + store_mpq_audio_feed[0] = mpq_feed; + feed_data->stream_interface = + MPQ_ADAPTER_AUDIO0_STREAM_IF; + break; + + case DMX_PES_AUDIO1: + store_mpq_audio_feed[1] = mpq_feed; + feed_data->stream_interface = + MPQ_ADAPTER_AUDIO1_STREAM_IF; + break; + + case DMX_PES_AUDIO2: + store_mpq_audio_feed[2] = mpq_feed; + feed_data->stream_interface = + MPQ_ADAPTER_AUDIO2_STREAM_IF; + break; + + case DMX_PES_AUDIO3: + store_mpq_audio_feed[3] = mpq_feed; + feed_data->stream_interface = + MPQ_ADAPTER_AUDIO3_STREAM_IF; + break; + + default: + MPQ_DVB_ERR_PRINT( + "%s: Invalid pes type %d\n", + __func__, + mpq_feed->dvb_demux_feed->pes_type); + ret = -EINVAL; + goto init_failed_free_priv_data; + } + + /* make sure not occupied already */ + stream_buffer = NULL; + mpq_adapter_get_stream_if( + feed_data->stream_interface, + &stream_buffer); + if (stream_buffer != NULL) { + MPQ_DVB_ERR_PRINT( + "%s: Audio interface %d already occupied!\n", + __func__, feed_data->stream_interface); + ret = -EBUSY; + goto init_failed_free_priv_data; + } + + feed_data->audio_buffer = + &mpq_dmx_info.decoder_buffers[feed_data->stream_interface]; + + ret = mpq_dmx_init_audio_streambuffer( + mpq_feed, feed_data, feed_data->audio_buffer); + if (ret) { + MPQ_DVB_ERR_PRINT( + "%s: mpq_dmx_init_streambuffer failed, err = %d\n", + __func__, ret); + goto init_failed_free_priv_data; + } + + ret = mpq_adapter_register_stream_if( + feed_data->stream_interface, + feed_data->audio_buffer); + + if (ret < 0) { + MPQ_DVB_ERR_PRINT( + "%s: mpq_adapter_register_stream_if failed, err = %d\n", + __func__, ret); + goto init_failed_free_stream_buffer; + } + + spin_lock_init(&feed_data->audio_buffer_lock); + + feed_data->pes_header_left_bytes = PES_MANDATORY_FIELDS_LEN; + feed_data->pes_header_offset = 0; + mpq_feed->dvb_demux_feed->pusi_seen = 0; + mpq_feed->dvb_demux_feed->peslen = 0; + feed_data->fullness_wait_cancel = 0; + mpq_streambuffer_get_data_rw_offset(feed_data->audio_buffer, NULL, + &feed_data->frame_offset); + feed_data->saved_pts_dts_info.pts_exist = 0; + feed_data->saved_pts_dts_info.dts_exist = 0; + feed_data->new_pts_dts_info.pts_exist = 0; + feed_data->new_pts_dts_info.dts_exist = 0; + feed_data->saved_info_used = 1; + feed_data->new_info_exists = 0; + feed_data->first_pts_dts_copy = 1; + feed_data->tei_errs = 0; + feed_data->last_continuity = -1; + feed_data->continuity_errs = 0; + feed_data->ts_packets_num = 0; + feed_data->ts_dropped_bytes = 0; + + mpq_demux->decoder_stat[feed_data->stream_interface].drop_count = 0; + mpq_demux->decoder_stat[feed_data->stream_interface].out_count = 0; + mpq_demux->decoder_stat[feed_data->stream_interface]. + out_interval_sum = 0; + mpq_demux->decoder_stat[feed_data->stream_interface]. + out_interval_max = 0; + mpq_demux->decoder_stat[feed_data->stream_interface].ts_errors = 0; + mpq_demux->decoder_stat[feed_data->stream_interface].cc_errors = 0; + + return 0; + +init_failed_free_stream_buffer: + mpq_dmx_release_audio_streambuffer(mpq_feed, feed_data, + feed_data->audio_buffer, mpq_demux->ion_client); + mpq_adapter_unregister_stream_if(feed_data->stream_interface); +init_failed_free_priv_data: + feed_data->audio_buffer = NULL; + return ret; +} + /** * mpq_dmx_terminate_video_feed - terminate video feed information * that was previously initialized in mpq_dmx_init_video_feed @@ -1563,11 +2174,12 @@ int mpq_dmx_terminate_video_feed(struct mpq_feed *mpq_feed) { struct mpq_streambuffer *video_buffer; struct mpq_video_feed_info *feed_data; - struct mpq_demux *mpq_demux = mpq_feed->mpq_demux; + struct mpq_demux *mpq_demux; if (mpq_feed == NULL) return -EINVAL; + mpq_demux = mpq_feed->mpq_demux; feed_data = &mpq_feed->video_info; spin_lock(&feed_data->video_buffer_lock); @@ -1582,6 +2194,30 @@ int mpq_dmx_terminate_video_feed(struct mpq_feed *mpq_feed) return 0; } +int mpq_dmx_terminate_audio_feed(struct mpq_feed *mpq_feed) +{ + struct mpq_streambuffer *audio_buffer; + struct mpq_audio_feed_info *feed_data; + struct mpq_demux *mpq_demux; + + if (mpq_feed == NULL) + return -EINVAL; + + mpq_demux = mpq_feed->mpq_demux; + feed_data = &mpq_feed->audio_info; + + spin_lock(&feed_data->audio_buffer_lock); + audio_buffer = feed_data->audio_buffer; + feed_data->audio_buffer = NULL; + wake_up_all(&audio_buffer->raw_data.queue); + spin_unlock(&feed_data->audio_buffer_lock); + + mpq_dmx_release_audio_streambuffer(mpq_feed, feed_data, + audio_buffer, mpq_demux->ion_client); + + return 0; +} + struct dvb_demux_feed *mpq_dmx_peer_rec_feed(struct dvb_demux_feed *feed) { struct dvb_demux_feed *tmp; @@ -1814,6 +2450,12 @@ int mpq_dmx_terminate_feed(struct dvb_demux_feed *feed) MPQ_DVB_ERR_PRINT( "%s: mpq_dmx_terminate_video_feed failed. ret = %d\n", __func__, ret); + } else if (dvb_dmx_is_audio_feed(feed)) { + ret = mpq_dmx_terminate_audio_feed(mpq_feed); + if (ret) + MPQ_DVB_ERR_PRINT( + "%s: mpq_dmx_terminate_audio_feed failed. ret = %d\n", + __func__, ret); } if (mpq_feed->sdmx_buf_handle) { @@ -1835,8 +2477,9 @@ int mpq_dmx_terminate_feed(struct dvb_demux_feed *feed) int mpq_dmx_decoder_fullness_init(struct dvb_demux_feed *feed) { + struct mpq_feed *mpq_feed; + if (dvb_dmx_is_video_feed(feed)) { - struct mpq_feed *mpq_feed; struct mpq_video_feed_info *feed_data; mpq_feed = feed->priv; @@ -1844,13 +2487,18 @@ int mpq_dmx_decoder_fullness_init(struct dvb_demux_feed *feed) feed_data->fullness_wait_cancel = 0; return 0; + } else if (dvb_dmx_is_audio_feed(feed)) { + struct mpq_audio_feed_info *feed_data; + + mpq_feed = feed->priv; + feed_data = &mpq_feed->audio_info; + feed_data->fullness_wait_cancel = 0; + + return 0; } - /* else */ - MPQ_DVB_DBG_PRINT( - "%s: Invalid feed type %d\n", - __func__, - feed->pes_type); + MPQ_DVB_DBG_PRINT("%s: Invalid feed type %d\n", __func__, + feed->pes_type); return -EINVAL; } @@ -1864,7 +2512,7 @@ int mpq_dmx_decoder_fullness_init(struct dvb_demux_feed *feed) * * Return 1 if required free bytes are available, 0 otherwise. */ -static inline int mpq_dmx_check_decoder_fullness( +static inline int mpq_dmx_check_video_decoder_fullness( struct mpq_streambuffer *sbuff, size_t required_space) { @@ -1888,6 +2536,29 @@ static inline int mpq_dmx_check_decoder_fullness( return (free >= required_space); } +static inline int mpq_dmx_check_audio_decoder_fullness( + struct mpq_streambuffer *sbuff, + size_t required_space) +{ + ssize_t free = mpq_streambuffer_data_free(sbuff); + ssize_t free_meta = mpq_streambuffer_metadata_free(sbuff); + + /* Verify meta-data buffer can contain at least 1 packet */ + if (free_meta < AUDIO_META_DATA_PACKET_SIZE) + return 0; + + /* + * For linear buffers, verify there's enough space for this TSP + * and an additional buffer is free, as framing might required one + * more buffer to be available. + */ + if (sbuff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR) + return (free >= required_space && + sbuff->pending_buffers_count < sbuff->buffers_num-1); + else + return (free >= required_space); /* Ring buffer mode */ +} + /** * Checks whether decoder's output buffer has free space * for specific number of bytes, if not, the function waits @@ -1943,7 +2614,8 @@ static int mpq_dmx_decoder_fullness_check( if ((feed_data->video_buffer != NULL) && (!feed_data->fullness_wait_cancel) && - (!mpq_dmx_check_decoder_fullness(sbuff, required_space))) { + (!mpq_dmx_check_video_decoder_fullness(sbuff, + required_space))) { DEFINE_WAIT(__wait); for (;;) { @@ -1952,7 +2624,7 @@ static int mpq_dmx_decoder_fullness_check( TASK_INTERRUPTIBLE); if (!feed_data->video_buffer || feed_data->fullness_wait_cancel || - mpq_dmx_check_decoder_fullness(sbuff, + mpq_dmx_check_video_decoder_fullness(sbuff, required_space)) break; @@ -1987,11 +2659,102 @@ static int mpq_dmx_decoder_fullness_check( return 0; } +static int mpq_dmx_audio_decoder_fullness_check( + struct dvb_demux_feed *feed, + size_t required_space, + int lock_feed) +{ + struct mpq_demux *mpq_demux = feed->demux->priv; + struct mpq_streambuffer *sbuff = NULL; + struct mpq_audio_feed_info *feed_data; + struct mpq_feed *mpq_feed; + int ret = 0; + + if (!dvb_dmx_is_audio_feed(feed)) { + MPQ_DVB_DBG_PRINT("%s: Invalid feed type %d\n", + __func__, + feed->pes_type); + return -EINVAL; + } + + if (lock_feed) { + mutex_lock(&mpq_demux->mutex); + } else if (!mutex_is_locked(&mpq_demux->mutex)) { + MPQ_DVB_ERR_PRINT( + "%s: Mutex should have been locked\n", + __func__); + return -EINVAL; + } + + mpq_feed = feed->priv; + feed_data = &mpq_feed->audio_info; + + sbuff = feed_data->audio_buffer; + if (sbuff == NULL) { + if (lock_feed) + mutex_unlock(&mpq_demux->mutex); + MPQ_DVB_ERR_PRINT("%s: mpq_streambuffer object is NULL\n", + __func__); + return -EINVAL; + } + + if ((feed_data->audio_buffer != NULL) && + (!feed_data->fullness_wait_cancel) && + (!mpq_dmx_check_audio_decoder_fullness(sbuff, + required_space))) { + DEFINE_WAIT(__wait); + + for (;;) { + prepare_to_wait(&sbuff->raw_data.queue, + &__wait, TASK_INTERRUPTIBLE); + if (!feed_data->audio_buffer || + feed_data->fullness_wait_cancel || + mpq_dmx_check_audio_decoder_fullness(sbuff, + required_space)) + break; + + if (!signal_pending(current)) { + mutex_unlock(&mpq_demux->mutex); + schedule(); + mutex_lock(&mpq_demux->mutex); + continue; + } + + ret = -ERESTARTSYS; + break; + } + finish_wait(&sbuff->raw_data.queue, &__wait); + } + + if (ret < 0) { + if (lock_feed) + mutex_unlock(&mpq_demux->mutex); + return ret; + } + + if ((feed_data->fullness_wait_cancel) || + (feed_data->audio_buffer == NULL)) { + if (lock_feed) + mutex_unlock(&mpq_demux->mutex); + return -EINVAL; + } + + if (lock_feed) + mutex_unlock(&mpq_demux->mutex); + return 0; +} + int mpq_dmx_decoder_fullness_wait( struct dvb_demux_feed *feed, size_t required_space) { - return mpq_dmx_decoder_fullness_check(feed, required_space, 1); + if (dvb_dmx_is_video_feed(feed)) + return mpq_dmx_decoder_fullness_check(feed, required_space, 1); + else if (dvb_dmx_is_audio_feed(feed)) + return mpq_dmx_audio_decoder_fullness_check(feed, + required_space, 1); + + return 0; } int mpq_dmx_decoder_fullness_abort(struct dvb_demux_feed *feed) @@ -2009,8 +2772,7 @@ int mpq_dmx_decoder_fullness_abort(struct dvb_demux_feed *feed) spin_lock(&feed_data->video_buffer_lock); if (feed_data->video_buffer == NULL) { MPQ_DVB_DBG_PRINT( - "%s: video_buffer released\n", - __func__); + "%s: video_buffer released\n", __func__); spin_unlock(&feed_data->video_buffer_lock); return 0; } @@ -2020,13 +2782,33 @@ int mpq_dmx_decoder_fullness_abort(struct dvb_demux_feed *feed) spin_unlock(&feed_data->video_buffer_lock); return 0; + } else if (dvb_dmx_is_audio_feed(feed)) { + struct mpq_feed *mpq_feed; + struct mpq_audio_feed_info *feed_data; + struct dvb_ringbuffer *audio_buff; + + mpq_feed = feed->priv; + feed_data = &mpq_feed->audio_info; + + feed_data->fullness_wait_cancel = 1; + + spin_lock(&feed_data->audio_buffer_lock); + if (feed_data->audio_buffer == NULL) { + MPQ_DVB_DBG_PRINT( + "%s: audio_buffer released\n", __func__); + spin_unlock(&feed_data->audio_buffer_lock); + return 0; + } + + audio_buff = &feed_data->audio_buffer->raw_data; + wake_up_all(&audio_buff->queue); + spin_unlock(&feed_data->audio_buffer_lock); + + return 0; } - /* else */ MPQ_DVB_ERR_PRINT( - "%s: Invalid feed type %d\n", - __func__, - feed->pes_type); + "%s: Invalid feed type %d\n", __func__, feed->pes_type); return -EINVAL; } @@ -2087,6 +2869,62 @@ int mpq_dmx_parse_mandatory_pes_header( return 0; } +int mpq_dmx_parse_mandatory_audio_pes_header( + struct dvb_demux_feed *feed, + struct mpq_audio_feed_info *feed_data, + struct pes_packet_header *pes_header, + const u8 *buf, + u32 *ts_payload_offset, + int *bytes_avail) +{ + int left_size, copy_len; + + if (feed_data->pes_header_offset < PES_MANDATORY_FIELDS_LEN) { + left_size = + PES_MANDATORY_FIELDS_LEN - + feed_data->pes_header_offset; + + copy_len = (left_size > *bytes_avail) ? + *bytes_avail : + left_size; + + memcpy((u8 *)((u8 *)pes_header + feed_data->pes_header_offset), + (buf + *ts_payload_offset), + copy_len); + + feed_data->pes_header_offset += copy_len; + + if (left_size > *bytes_avail) + return -EINVAL; + + /* else - we have beginning of PES header */ + *bytes_avail -= left_size; + *ts_payload_offset += left_size; + + /* Make sure the PES packet is valid */ + if (mpq_dmx_is_valid_audio_pes(pes_header) < 0) { + /* + * Since the new PES header parsing + * failed, reset pusi_seen to drop all + * data until next PUSI + */ + feed->pusi_seen = 0; + feed_data->pes_header_offset = 0; + + MPQ_DVB_ERR_PRINT( + "%s: invalid packet\n", + __func__); + + return -EINVAL; + } + + feed_data->pes_header_left_bytes = + pes_header->pes_header_data_length; + } + + return 0; +} + static inline void mpq_dmx_get_pts_dts(struct mpq_video_feed_info *feed_data, struct pes_packet_header *pes_header) { @@ -2126,6 +2964,46 @@ static inline void mpq_dmx_get_pts_dts(struct mpq_video_feed_info *feed_data, feed_data->new_info_exists = 1; } +static inline void mpq_dmx_get_audio_pts_dts( + struct mpq_audio_feed_info *feed_data, + struct pes_packet_header *pes_header) +{ + struct dmx_pts_dts_info *info = &(feed_data->new_pts_dts_info); + + /* Get PTS/DTS information from PES header */ + + if ((pes_header->pts_dts_flag == 2) || + (pes_header->pts_dts_flag == 3)) { + info->pts_exist = 1; + + info->pts = + ((u64)pes_header->pts_1 << 30) | + ((u64)pes_header->pts_2 << 22) | + ((u64)pes_header->pts_3 << 15) | + ((u64)pes_header->pts_4 << 7) | + (u64)pes_header->pts_5; + } else { + info->pts_exist = 0; + info->pts = 0; + } + + if (pes_header->pts_dts_flag == 3) { + info->dts_exist = 1; + + info->dts = + ((u64)pes_header->dts_1 << 30) | + ((u64)pes_header->dts_2 << 22) | + ((u64)pes_header->dts_3 << 15) | + ((u64)pes_header->dts_4 << 7) | + (u64)pes_header->dts_5; + } else { + info->dts_exist = 0; + info->dts = 0; + } + + feed_data->new_info_exists = 1; +} + int mpq_dmx_parse_remaining_pes_header( struct dvb_demux_feed *feed, struct mpq_video_feed_info *feed_data, @@ -2218,6 +3096,96 @@ int mpq_dmx_parse_remaining_pes_header( return 0; } +int mpq_dmx_parse_remaining_audio_pes_header( + struct dvb_demux_feed *feed, + struct mpq_audio_feed_info *feed_data, + struct pes_packet_header *pes_header, + const u8 *buf, + u32 *ts_payload_offset, + int *bytes_avail) +{ + int left_size, copy_len; + + /* Remaining header bytes that need to be processed? */ + if (!feed_data->pes_header_left_bytes) + return 0; + + /* Did we capture the PTS value (if exists)? */ + if ((*bytes_avail != 0) && + (feed_data->pes_header_offset < + (PES_MANDATORY_FIELDS_LEN+5)) && + ((pes_header->pts_dts_flag == 2) || + (pes_header->pts_dts_flag == 3))) { + + /* 5 more bytes should be there */ + left_size = + PES_MANDATORY_FIELDS_LEN + 5 - + feed_data->pes_header_offset; + + copy_len = + (left_size > *bytes_avail) ? *bytes_avail : left_size; + + memcpy((u8 *)((u8 *)pes_header + feed_data->pes_header_offset), + (buf + *ts_payload_offset), copy_len); + + feed_data->pes_header_offset += copy_len; + feed_data->pes_header_left_bytes -= copy_len; + + if (left_size > *bytes_avail) + return -EINVAL; + + /* else - we have the PTS */ + *bytes_avail -= copy_len; + *ts_payload_offset += copy_len; + } + + /* Did we capture the DTS value (if exist)? */ + if ((*bytes_avail != 0) && + (feed_data->pes_header_offset < + (PES_MANDATORY_FIELDS_LEN+10)) && + (pes_header->pts_dts_flag == 3)) { + + /* 5 more bytes should be there */ + left_size = + PES_MANDATORY_FIELDS_LEN + 10 - + feed_data->pes_header_offset; + + copy_len = (left_size > *bytes_avail) ? + *bytes_avail : + left_size; + + memcpy((u8 *)((u8 *)pes_header + feed_data->pes_header_offset), + (buf + *ts_payload_offset), + copy_len); + + feed_data->pes_header_offset += copy_len; + feed_data->pes_header_left_bytes -= copy_len; + + if (left_size > *bytes_avail) + return -EINVAL; + + /* else - we have the DTS */ + *bytes_avail -= copy_len; + *ts_payload_offset += copy_len; + } + + /* Any more header bytes?! */ + if (feed_data->pes_header_left_bytes >= *bytes_avail) { + feed_data->pes_header_left_bytes -= *bytes_avail; + return -EINVAL; + } + + /* get PTS/DTS information from PES header to be written later */ + mpq_dmx_get_audio_pts_dts(feed_data, pes_header); + + /* Got PES header, process payload */ + *bytes_avail -= feed_data->pes_header_left_bytes; + *ts_payload_offset += feed_data->pes_header_left_bytes; + feed_data->pes_header_left_bytes = 0; + + return 0; +} + static void mpq_dmx_check_continuity(struct mpq_video_feed_info *feed_data, int current_continuity, int discontinuity_indicator) @@ -2249,6 +3217,37 @@ static void mpq_dmx_check_continuity(struct mpq_video_feed_info *feed_data, feed_data->last_continuity = current_continuity; } +static void mpq_dmx_check_audio_continuity( + struct mpq_audio_feed_info *feed_data, + int current_continuity, + int discontinuity_indicator) +{ + const int max_continuity = 0x0F; /* 4 bits in the TS packet header */ + + /* sanity check */ + if (unlikely((current_continuity < 0) || + (current_continuity > max_continuity))) { + MPQ_DVB_DBG_PRINT( + "%s: received invalid continuity counter value %d\n", + __func__, current_continuity); + return; + } + + /* reset last continuity */ + if ((feed_data->last_continuity == -1) || (discontinuity_indicator)) { + feed_data->last_continuity = current_continuity; + return; + } + + /* check for continuity errors */ + if (current_continuity != + ((feed_data->last_continuity + 1) & max_continuity)) + feed_data->continuity_errs++; + + /* save for next time */ + feed_data->last_continuity = current_continuity; +} + static inline void mpq_dmx_prepare_es_event_data( struct mpq_streambuffer_packet_header *packet, struct mpq_adapter_video_meta_data *meta_data, @@ -2295,6 +3294,43 @@ static inline void mpq_dmx_prepare_es_event_data( feed_data->continuity_errs = 0; } +static inline void mpq_dmx_prepare_audio_es_event_data( + struct mpq_streambuffer_packet_header *packet, + struct mpq_adapter_audio_meta_data *meta_data, + struct mpq_audio_feed_info *feed_data, + struct mpq_streambuffer *stream_buffer, + struct dmx_data_ready *data, + int cookie) +{ + struct dmx_pts_dts_info *pts_dts; + + pts_dts = &meta_data->info.pes.pts_dts_info; + data->buf.stc = meta_data->info.pes.stc; + + data->data_length = 0; + data->buf.handle = packet->raw_data_handle; + data->buf.cookie = cookie; + data->buf.offset = packet->raw_data_offset; + data->buf.len = packet->raw_data_len; + data->buf.pts_exists = pts_dts->pts_exist; + data->buf.pts = pts_dts->pts; + data->buf.dts_exists = pts_dts->dts_exist; + data->buf.dts = pts_dts->dts; + data->buf.tei_counter = feed_data->tei_errs; + data->buf.cont_err_counter = feed_data->continuity_errs; + data->buf.ts_packets_num = feed_data->ts_packets_num; + data->buf.ts_dropped_bytes = feed_data->ts_dropped_bytes; + data->status = DMX_OK_DECODER_BUF; + + MPQ_DVB_DBG_PRINT("%s: cookie=%d\n", __func__, data->buf.cookie); + + /* reset counters */ + feed_data->ts_packets_num = 0; + feed_data->ts_dropped_bytes = 0; + feed_data->tei_errs = 0; + feed_data->continuity_errs = 0; +} + static int mpq_sdmx_dvr_buffer_desc(struct mpq_demux *mpq_demux, struct sdmx_buff_descr *buf_desc) { @@ -2488,6 +3524,81 @@ static void mpq_dmx_decoder_pes_closure(struct mpq_demux *mpq_demux, spin_unlock(&feed_data->video_buffer_lock); } +/* + * in audio handling although ES frames are send to decoder, close the + * pes packet + */ +static void mpq_dmx_decoder_audio_pes_closure(struct mpq_demux *mpq_demux, + struct mpq_feed *mpq_feed) +{ + struct mpq_streambuffer_packet_header packet; + struct mpq_streambuffer *stream_buffer; + struct mpq_adapter_audio_meta_data meta_data; + struct mpq_audio_feed_info *feed_data; + struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed; + struct dmx_data_ready data; + int cookie; + + feed_data = &mpq_feed->audio_info; + + /* + * spin-lock is taken to protect against manipulation of audio + * output buffer by the API (terminate audio feed, re-use of audio + * buffers). + */ + spin_lock(&feed_data->audio_buffer_lock); + stream_buffer = feed_data->audio_buffer; + + if (stream_buffer == NULL) { + MPQ_DVB_DBG_PRINT("%s: audio_buffer released\n", __func__); + spin_unlock(&feed_data->audio_buffer_lock); + return; + } + + /* + * Close previous PES. + * Push new packet to the meta-data buffer. + */ + if ((feed->pusi_seen) && (feed_data->pes_header_left_bytes == 0)) { + packet.raw_data_len = feed->peslen; + mpq_streambuffer_get_buffer_handle(stream_buffer, + 0, /* current write buffer handle */ + &packet.raw_data_handle); + packet.raw_data_offset = feed_data->frame_offset; + packet.user_data_len = + sizeof(struct mpq_adapter_audio_meta_data); + + mpq_dmx_write_audio_pts_dts(feed_data, + &(meta_data.info.pes.pts_dts_info)); + + meta_data.packet_type = DMX_PES_PACKET; + meta_data.info.pes.stc = feed_data->prev_stc; + + mpq_dmx_update_decoder_stat(mpq_feed); + + cookie = mpq_streambuffer_pkt_write(stream_buffer, &packet, + (u8 *)&meta_data); + if (cookie >= 0) { + /* Save write offset where new PES will begin */ + mpq_streambuffer_get_data_rw_offset(stream_buffer, NULL, + &feed_data->frame_offset); + mpq_dmx_prepare_audio_es_event_data(&packet, &meta_data, + feed_data, stream_buffer, &data, cookie); + feed->data_ready_cb.ts(&feed->feed.ts, &data); + } else { + MPQ_DVB_ERR_PRINT( + "%s: mpq_sb_pkt_write failed, ret=%d\n", + __func__, cookie); + } + } + /* Reset PES info */ + feed->peslen = 0; + feed_data->pes_header_offset = 0; + feed_data->pes_header_left_bytes = PES_MANDATORY_FIELDS_LEN; + + spin_unlock(&feed_data->audio_buffer_lock); +} + static int mpq_dmx_process_video_packet_framing( struct dvb_demux_feed *feed, const u8 *buf, @@ -2824,6 +3935,7 @@ static int mpq_dmx_process_video_packet_framing( pending_data_len -= bytes_to_write; feed_data->pending_pattern_len += bytes_to_write; } + non_predicted_video_frame = 0; is_video_frame = mpq_dmx_is_video_frame( feed->video_codec, @@ -2863,9 +3975,9 @@ static int mpq_dmx_process_video_packet_framing( ret = mpq_streambuffer_pkt_write(stream_buffer, &packet, (u8 *)&meta_data); if (ret < 0) { - MPQ_DVB_ERR_PRINT( - "%s: mpq_streambuffer_pkt_write failed, ret=%d\n", - __func__, ret); + MPQ_DVB_ERR_PRINT + ("%s: mpq_sb_pkt_write failed ret=%d\n", + __func__, ret); if (ret == -ENOSPC) mpq_dmx_notify_overflow(feed); } else { @@ -2873,7 +3985,15 @@ static int mpq_dmx_process_video_packet_framing( &packet, &meta_data, feed_data, stream_buffer, &data, ret); - feed->data_ready_cb.ts(&feed->feed.ts, &data); + /* Trigger ES Data Event for VPTS */ + if (video_b_frame_events == 1) { + if (non_predicted_video_frame == 1) + feed->data_ready_cb.ts + (&feed->feed.ts, &data); + } else { + feed->data_ready_cb.ts(&feed->feed.ts, + &data); + } if (feed_data->video_buffer->mode == MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR) @@ -3071,8 +4191,8 @@ static int mpq_dmx_process_video_packet_no_framing( stream_buffer, &packet, (u8 *)&meta_data); if (cookie < 0) { - MPQ_DVB_ERR_PRINT( - "%s: mpq_streambuffer_pkt_write failed, ret=%d\n", + MPQ_DVB_ERR_PRINT + ("%s: write failed, ret=%d\n", __func__, cookie); } else { /* @@ -3200,60 +4320,335 @@ static int mpq_dmx_process_video_packet_no_framing( return 0; } -int mpq_dmx_decoder_buffer_status(struct dvb_demux_feed *feed, - struct dmx_buffer_status *dmx_buffer_status) +/* + * parse PES headers and send down ES packets to decoder + * Trigger a new ES Data Event with APTS and QTimer in 1st PES + */ +static int mpq_dmx_process_audio_packet_no_framing( + struct dvb_demux_feed *feed, + const u8 *buf, + u64 curr_stc) { - struct mpq_demux *mpq_demux = feed->demux->priv; - struct mpq_video_feed_info *feed_data; - struct mpq_streambuffer *video_buff; + int bytes_avail; + u32 ts_payload_offset; + struct mpq_audio_feed_info *feed_data; + const struct ts_packet_header *ts_header; + struct mpq_streambuffer *stream_buffer; + struct pes_packet_header *pes_header; + struct mpq_demux *mpq_demux; struct mpq_feed *mpq_feed; + int discontinuity_indicator = 0; + struct dmx_data_ready data; + int cookie; + int ret; - if (!dvb_dmx_is_video_feed(feed)) { - MPQ_DVB_ERR_PRINT( - "%s: Invalid feed type %d\n", - __func__, - feed->pes_type); - return -EINVAL; + mpq_demux = feed->demux->priv; + mpq_feed = feed->priv; + feed_data = &mpq_feed->audio_info; + + /* + * spin-lock is taken to protect against manipulation of audio + * output buffer by the API (terminate audio feed, re-use of audio + * buffers). Mutex on the audio-feed cannot be held here + * since SW demux holds a spin-lock while calling write_to_decoder + */ + spin_lock(&feed_data->audio_buffer_lock); + stream_buffer = feed_data->audio_buffer; + if (stream_buffer == NULL) { + MPQ_DVB_DBG_PRINT( + "%s: audio_buffer released\n", + __func__); + spin_unlock(&feed_data->audio_buffer_lock); + return 0; } - mutex_lock(&mpq_demux->mutex); + ts_header = (const struct ts_packet_header *)buf; - mpq_feed = feed->priv; - feed_data = &mpq_feed->video_info; - video_buff = feed_data->video_buffer; - if (!video_buff) { - mutex_unlock(&mpq_demux->mutex); - return -EINVAL; + pes_header = &feed_data->pes_header; + + /* Make sure this TS packet has a payload and not scrambled */ + if ((ts_header->sync_byte != 0x47) || + (ts_header->adaptation_field_control == 0) || + (ts_header->adaptation_field_control == 2) || + (ts_header->transport_scrambling_control)) { + /* continue to next packet */ + spin_unlock(&feed_data->audio_buffer_lock); + return 0; } - dmx_buffer_status->error = video_buff->raw_data.error; + if (ts_header->payload_unit_start_indicator) { /* PUSI? */ + if (feed->pusi_seen) { /* Did we see PUSI before? */ + struct mpq_streambuffer_packet_header packet; + struct mpq_adapter_audio_meta_data meta_data; + + /* + * Close previous PES. + * Push new packet to the meta-data buffer. + * Double check that we are not in middle of + * previous PES header parsing. + */ + + if (feed_data->pes_header_left_bytes == 0) { + packet.raw_data_len = feed->peslen; + mpq_streambuffer_get_buffer_handle( + stream_buffer, + 0, /* current write buffer handle */ + &packet.raw_data_handle); + packet.raw_data_offset = + feed_data->frame_offset; + packet.user_data_len = + sizeof(struct + mpq_adapter_audio_meta_data); + + mpq_dmx_write_audio_pts_dts(feed_data, + &(meta_data.info.pes.pts_dts_info)); + + /* Mark that we detected start of new PES */ + feed_data->first_pts_dts_copy = 1; + + meta_data.packet_type = DMX_PES_PACKET; + meta_data.info.pes.stc = feed_data->prev_stc; + + mpq_dmx_update_decoder_stat(mpq_feed); - if (video_buff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR) { - dmx_buffer_status->fullness = - video_buff->buffers[0].size * - video_buff->pending_buffers_count; - dmx_buffer_status->free_bytes = - video_buff->buffers[0].size * - (video_buff->buffers_num - - video_buff->pending_buffers_count); - dmx_buffer_status->size = - video_buff->buffers[0].size * - video_buff->buffers_num; + /* actual writing of stream audio headers */ + cookie = mpq_streambuffer_pkt_write( + stream_buffer, &packet, + (u8 *)&meta_data); + if (cookie < 0) { + MPQ_DVB_ERR_PRINT + ("%s: write failed, ret=%d\n", + __func__, cookie); + } else { + /* + * Save write offset where new PES + * will begin + */ + mpq_streambuffer_get_data_rw_offset( + stream_buffer, + NULL, + &feed_data->frame_offset); + + mpq_dmx_prepare_audio_es_event_data( + &packet, &meta_data, + feed_data, + stream_buffer, &data, cookie); + + /* + * Trigger ES data event for APTS + * and AFRAME + */ + feed->data_ready_cb.ts(&feed->feed.ts, + &data); + } + } else { + MPQ_DVB_ERR_PRINT( + "%s: received PUSI while handling PES header of previous PES\n", + __func__); + } + + /* Reset PES info */ + feed->peslen = 0; + feed_data->pes_header_offset = 0; + feed_data->pes_header_left_bytes = + PES_MANDATORY_FIELDS_LEN; + } else { + feed->pusi_seen = 1; + } + + feed_data->prev_stc = curr_stc; + } + + /* + * Parse PES data only if PUSI was encountered, + * otherwise the data is dropped + */ + if (!feed->pusi_seen) { + spin_unlock(&feed_data->audio_buffer_lock); + return 0; /* drop and wait for next packets */ + } + + ts_payload_offset = sizeof(struct ts_packet_header); + + /* + * Skip adaptation field if exists. + * Save discontinuity indicator if exists. + */ + if (ts_header->adaptation_field_control == 3) { + const struct ts_adaptation_field *adaptation_field = + (const struct ts_adaptation_field *)(buf + + ts_payload_offset); + + discontinuity_indicator = + adaptation_field->discontinuity_indicator; + ts_payload_offset += buf[ts_payload_offset] + 1; + } + + bytes_avail = TS_PACKET_SIZE - ts_payload_offset; + + /* The audio decoder requires ES packets ! */ + + /* Get the mandatory fields of the audio PES header */ + if (mpq_dmx_parse_mandatory_audio_pes_header(feed, feed_data, + pes_header, buf, + &ts_payload_offset, + &bytes_avail)) { + spin_unlock(&feed_data->audio_buffer_lock); + return 0; + } + + if (mpq_dmx_parse_remaining_audio_pes_header(feed, feed_data, + pes_header, buf, + &ts_payload_offset, + &bytes_avail)) { + spin_unlock(&feed_data->audio_buffer_lock); + return 0; + } + + /* + * If we reached here, + * then we are now at the PES payload data + */ + if (bytes_avail == 0) { + spin_unlock(&feed_data->audio_buffer_lock); + return 0; + } + + /* + * Need to back-up the PTS information + * of the start of new PES + */ + if (feed_data->first_pts_dts_copy) { + mpq_dmx_save_audio_pts_dts(feed_data); + feed_data->first_pts_dts_copy = 0; + } + + /* Update error counters based on TS header */ + feed_data->ts_packets_num++; + feed_data->tei_errs += ts_header->transport_error_indicator; + mpq_demux->decoder_stat[feed_data->stream_interface].ts_errors += + ts_header->transport_error_indicator; + mpq_dmx_check_audio_continuity(feed_data, + ts_header->continuity_counter, + discontinuity_indicator); + mpq_demux->decoder_stat[feed_data->stream_interface].cc_errors += + feed_data->continuity_errs; + + /* actual writing of audio data for a stream */ + ret = mpq_streambuffer_data_write(stream_buffer, buf+ts_payload_offset, + bytes_avail); + if (ret < 0) { + mpq_demux->decoder_stat + [feed_data->stream_interface].drop_count += bytes_avail; + feed_data->ts_dropped_bytes += bytes_avail; + if (ret == -ENOSPC) + mpq_dmx_notify_overflow(feed); } else { - dmx_buffer_status->fullness = - mpq_streambuffer_data_avail(video_buff); - dmx_buffer_status->free_bytes = - mpq_streambuffer_data_free(video_buff); - dmx_buffer_status->size = video_buff->buffers[0].size; + feed->peslen += bytes_avail; } - mpq_streambuffer_get_data_rw_offset( - video_buff, - &dmx_buffer_status->read_offset, - &dmx_buffer_status->write_offset); + spin_unlock(&feed_data->audio_buffer_lock); - mutex_unlock(&mpq_demux->mutex); + return 0; +} + +/* function ptr used in several places, handle differently */ +int mpq_dmx_decoder_buffer_status(struct dvb_demux_feed *feed, + struct dmx_buffer_status *dmx_buffer_status) +{ + + if (dvb_dmx_is_video_feed(feed)) { + struct mpq_demux *mpq_demux = feed->demux->priv; + struct mpq_video_feed_info *feed_data; + struct mpq_streambuffer *video_buff; + struct mpq_feed *mpq_feed; + + mutex_lock(&mpq_demux->mutex); + + mpq_feed = feed->priv; + feed_data = &mpq_feed->video_info; + video_buff = feed_data->video_buffer; + if (!video_buff) { + mutex_unlock(&mpq_demux->mutex); + return -EINVAL; + } + + dmx_buffer_status->error = video_buff->raw_data.error; + + if (video_buff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR) { + dmx_buffer_status->fullness = + video_buff->buffers[0].size * + video_buff->pending_buffers_count; + dmx_buffer_status->free_bytes = + video_buff->buffers[0].size * + (video_buff->buffers_num - + video_buff->pending_buffers_count); + dmx_buffer_status->size = + video_buff->buffers[0].size * + video_buff->buffers_num; + } else { + dmx_buffer_status->fullness = + mpq_streambuffer_data_avail(video_buff); + dmx_buffer_status->free_bytes = + mpq_streambuffer_data_free(video_buff); + dmx_buffer_status->size = video_buff->buffers[0].size; + } + + mpq_streambuffer_get_data_rw_offset( + video_buff, + &dmx_buffer_status->read_offset, + &dmx_buffer_status->write_offset); + + mutex_unlock(&mpq_demux->mutex); + + } else if (dvb_dmx_is_audio_feed(feed)) { + struct mpq_demux *mpq_demux = feed->demux->priv; + struct mpq_audio_feed_info *feed_data; + struct mpq_streambuffer *audio_buff; + struct mpq_feed *mpq_feed; + + mutex_lock(&mpq_demux->mutex); + + mpq_feed = feed->priv; + feed_data = &mpq_feed->audio_info; + audio_buff = feed_data->audio_buffer; + if (!audio_buff) { + mutex_unlock(&mpq_demux->mutex); + return -EINVAL; + } + + dmx_buffer_status->error = audio_buff->raw_data.error; + + if (audio_buff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR) { + dmx_buffer_status->fullness = + audio_buff->buffers[0].size * + audio_buff->pending_buffers_count; + dmx_buffer_status->free_bytes = + audio_buff->buffers[0].size * + (audio_buff->buffers_num - + audio_buff->pending_buffers_count); + dmx_buffer_status->size = + audio_buff->buffers[0].size * + audio_buff->buffers_num; + } else { + dmx_buffer_status->fullness = + mpq_streambuffer_data_avail(audio_buff); + dmx_buffer_status->free_bytes = + mpq_streambuffer_data_free(audio_buff); + dmx_buffer_status->size = audio_buff->buffers[0].size; + } + + mpq_streambuffer_get_data_rw_offset( + audio_buff, + &dmx_buffer_status->read_offset, + &dmx_buffer_status->write_offset); + mutex_unlock(&mpq_demux->mutex); + } else { + MPQ_DVB_ERR_PRINT("%s: Invalid feed type %d\n", + __func__, feed->pes_type); + return -EINVAL; + } return 0; } @@ -3268,10 +4663,18 @@ int mpq_dmx_process_video_packet( (mpq_demux->demux.tsp_format != DMX_TSP_FORMAT_192_TAIL)) { curr_stc = 0; } else { - curr_stc = buf[STC_LOCATION_IDX + 2] << 16; - curr_stc += buf[STC_LOCATION_IDX + 1] << 8; - curr_stc += buf[STC_LOCATION_IDX]; - curr_stc *= 256; /* convert from 105.47 KHZ to 27MHz */ + if (mpq_demux->ts_packet_timestamp_source != + TSIF_TTS_LPASS_TIMER) { + curr_stc = buf[STC_LOCATION_IDX + 2] << 16; + curr_stc += buf[STC_LOCATION_IDX + 1] << 8; + curr_stc += buf[STC_LOCATION_IDX]; + curr_stc *= 256; /* convert from 105.47 KHZ to 27MHz */ + } else { + curr_stc = buf[STC_LOCATION_IDX + 3] << 24; + curr_stc += buf[STC_LOCATION_IDX + 2] << 16; + curr_stc += buf[STC_LOCATION_IDX + 1] << 8; + curr_stc += buf[STC_LOCATION_IDX]; + } } if (!video_framing) @@ -3282,6 +4685,34 @@ int mpq_dmx_process_video_packet( curr_stc); } +int mpq_dmx_process_audio_packet( + struct dvb_demux_feed *feed, + const u8 *buf) +{ + u64 curr_stc; + struct mpq_demux *mpq_demux = feed->demux->priv; + + if ((mpq_demux->source >= DMX_SOURCE_DVR0) && + (mpq_demux->demux.tsp_format != DMX_TSP_FORMAT_192_TAIL)) { + curr_stc = 0; + } else { + if (mpq_demux->ts_packet_timestamp_source != + TSIF_TTS_LPASS_TIMER) { + curr_stc = buf[STC_LOCATION_IDX + 2] << 16; + curr_stc += buf[STC_LOCATION_IDX + 1] << 8; + curr_stc += buf[STC_LOCATION_IDX]; + curr_stc *= 256; /* convert from 105.47 KHZ to 27MHz */ + } else { + curr_stc = buf[STC_LOCATION_IDX + 3] << 24; + curr_stc += buf[STC_LOCATION_IDX + 2] << 16; + curr_stc += buf[STC_LOCATION_IDX + 1] << 8; + curr_stc += buf[STC_LOCATION_IDX]; + } + } + + return mpq_dmx_process_audio_packet_no_framing(feed, buf, curr_stc); +} + int mpq_dmx_extract_pcr_and_dci(const u8 *buf, u64 *pcr, int *dci) { const struct ts_packet_header *ts_header; @@ -3342,10 +4773,18 @@ int mpq_dmx_process_pcr_packet( (mpq_demux->demux.tsp_format != DMX_TSP_FORMAT_192_TAIL)) { stc = 0; } else { - stc = buf[STC_LOCATION_IDX + 2] << 16; - stc += buf[STC_LOCATION_IDX + 1] << 8; - stc += buf[STC_LOCATION_IDX]; - stc *= 256; /* convert from 105.47 KHZ to 27MHz */ + if (mpq_demux->ts_packet_timestamp_source != + TSIF_TTS_LPASS_TIMER) { + stc = buf[STC_LOCATION_IDX + 2] << 16; + stc += buf[STC_LOCATION_IDX + 1] << 8; + stc += buf[STC_LOCATION_IDX]; + stc *= 256; /* convert from 105.47 KHZ to 27MHz */ + } else { + stc = buf[STC_LOCATION_IDX + 3] << 24; + stc += buf[STC_LOCATION_IDX + 2] << 16; + stc += buf[STC_LOCATION_IDX + 1] << 8; + stc += buf[STC_LOCATION_IDX]; + } } data.data_length = 0; @@ -3356,45 +4795,86 @@ int mpq_dmx_process_pcr_packet( return 0; } -int mpq_dmx_decoder_eos_cmd(struct mpq_feed *mpq_feed) +int mpq_dmx_decoder_eos_cmd(struct mpq_feed *mpq_feed, int feed_type) { - struct mpq_video_feed_info *feed_data = &mpq_feed->video_info; - struct mpq_streambuffer *stream_buffer; - struct mpq_streambuffer_packet_header oob_packet; - struct mpq_adapter_video_meta_data oob_meta_data; - int ret; + if (feed_type == 1) { /* video feed */ + struct mpq_video_feed_info *feed_data = &mpq_feed->video_info; + struct mpq_streambuffer *stream_buffer; + struct mpq_streambuffer_packet_header oob_packet; + struct mpq_adapter_video_meta_data oob_meta_data; + int ret; - spin_lock(&feed_data->video_buffer_lock); - stream_buffer = feed_data->video_buffer; + spin_lock(&feed_data->video_buffer_lock); + stream_buffer = feed_data->video_buffer; + + if (stream_buffer == NULL) { + MPQ_DVB_DBG_PRINT("%s: video_buffer released\n", + __func__); + spin_unlock(&feed_data->video_buffer_lock); + return 0; + } + + memset(&oob_packet, 0, sizeof(oob_packet)); + oob_packet.user_data_len = sizeof(oob_meta_data); + oob_meta_data.packet_type = DMX_EOS_PACKET; + + ret = mpq_streambuffer_pkt_write(stream_buffer, &oob_packet, + (u8 *)&oob_meta_data); - if (stream_buffer == NULL) { - MPQ_DVB_DBG_PRINT("%s: video_buffer released\n", __func__); spin_unlock(&feed_data->video_buffer_lock); - return 0; - } + return (ret < 0) ? ret : 0; - memset(&oob_packet, 0, sizeof(oob_packet)); - oob_packet.user_data_len = sizeof(oob_meta_data); - oob_meta_data.packet_type = DMX_EOS_PACKET; + } else if (feed_type == 2) { /* audio feed */ + struct mpq_audio_feed_info *feed_data = &mpq_feed->audio_info; + struct mpq_streambuffer *stream_buffer; + struct mpq_streambuffer_packet_header oob_packet; + struct mpq_adapter_audio_meta_data oob_meta_data; + int ret; - ret = mpq_streambuffer_pkt_write(stream_buffer, &oob_packet, - (u8 *)&oob_meta_data); + spin_lock(&feed_data->audio_buffer_lock); + stream_buffer = feed_data->audio_buffer; - spin_unlock(&feed_data->video_buffer_lock); - return (ret < 0) ? ret : 0; + if (stream_buffer == NULL) { + MPQ_DVB_DBG_PRINT("%s: audio_buffer released\n", + __func__); + spin_unlock(&feed_data->audio_buffer_lock); + return 0; + } + + memset(&oob_packet, 0, sizeof(oob_packet)); + oob_packet.user_data_len = sizeof(oob_meta_data); + oob_meta_data.packet_type = DMX_EOS_PACKET; + + ret = mpq_streambuffer_pkt_write(stream_buffer, &oob_packet, + (u8 *)&oob_meta_data); + + spin_unlock(&feed_data->audio_buffer_lock); + return (ret < 0) ? ret : 0; + } + + return 0; } void mpq_dmx_convert_tts(struct dvb_demux_feed *feed, const u8 timestamp[TIMESTAMP_LEN], u64 *timestampIn27Mhz) { + struct mpq_demux *mpq_demux = feed->demux->priv; + if (unlikely(!timestampIn27Mhz)) return; - *timestampIn27Mhz = timestamp[2] << 16; - *timestampIn27Mhz += timestamp[1] << 8; - *timestampIn27Mhz += timestamp[0]; - *timestampIn27Mhz *= 256; /* convert from 105.47 KHZ to 27MHz */ + if (mpq_demux->ts_packet_timestamp_source != TSIF_TTS_LPASS_TIMER) { + *timestampIn27Mhz = timestamp[2] << 16; + *timestampIn27Mhz += timestamp[1] << 8; + *timestampIn27Mhz += timestamp[0]; + *timestampIn27Mhz *= 256; /* convert from 105.47 KHZ to 27MHz */ + } else { + *timestampIn27Mhz = timestamp[3] << 24; + *timestampIn27Mhz += timestamp[2] << 16; + *timestampIn27Mhz += timestamp[1] << 8; + *timestampIn27Mhz += timestamp[0]; + } } int mpq_sdmx_open_session(struct mpq_demux *mpq_demux) @@ -3904,6 +5384,16 @@ int mpq_dmx_init_mpq_feed(struct dvb_demux_feed *feed) } } + if (dvb_dmx_is_audio_feed(feed)) { + ret = mpq_dmx_init_audio_feed(mpq_feed); + if (ret) { + MPQ_DVB_ERR_PRINT( + "%s: mpq_dmx_init_audio_feed failed, ret=%d\n", + __func__, ret); + goto init_mpq_feed_end; + } + } + /* * sdmx is not relevant for recording filters, which always use * regular filters (non-sdmx) @@ -3924,6 +5414,8 @@ int mpq_dmx_init_mpq_feed(struct dvb_demux_feed *feed) __func__, ret); if (dvb_dmx_is_video_feed(feed)) mpq_dmx_terminate_video_feed(mpq_feed); + else if (dvb_dmx_is_audio_feed(feed)) + mpq_dmx_terminate_audio_feed(mpq_feed); } init_mpq_feed_end: @@ -4662,7 +6154,7 @@ decoder_filter_check_flags: if (sts->status_indicators & SDMX_FILTER_STATUS_EOS) { /* Notify decoder via the stream buffer */ - ret = mpq_dmx_decoder_eos_cmd(mpq_feed); + ret = mpq_dmx_decoder_eos_cmd(mpq_feed, 1); if (ret) MPQ_DVB_ERR_PRINT( "%s: Failed to notify decoder on EOS, ret=%d\n", @@ -5162,11 +6654,19 @@ int mpq_dmx_oob_command(struct dvb_demux_feed *feed, else mpq_dmx_decoder_frame_closure(mpq_demux, mpq_feed); - ret = mpq_dmx_decoder_eos_cmd(mpq_feed); + ret = mpq_dmx_decoder_eos_cmd(mpq_feed, 1); if (ret) MPQ_DVB_ERR_PRINT( "%s: Couldn't write oob eos packet\n", __func__); + } else if (dvb_dmx_is_audio_feed(feed)) { + mpq_dmx_decoder_audio_pes_closure(mpq_demux, + mpq_feed); + ret = mpq_dmx_decoder_eos_cmd(mpq_feed, 2); + if (ret) + MPQ_DVB_ERR_PRINT( + "%s: Couldn't write oob eos packet\n", + __func__); } ret = feed->data_ready_cb.ts(&feed->feed.ts, &event); } else if (!mpq_demux->sdmx_eos) { diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h index f36e9e7e7a23..ca6372b7d152 100644 --- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h +++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, 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 @@ -44,12 +44,20 @@ #define VIDEO_META_DATA_BUFFER_SIZE \ (VIDEO_NUM_OF_PES_PACKETS * VIDEO_META_DATA_PACKET_SIZE) +#define AUDIO_NUM_OF_PES_PACKETS 100 + +#define AUDIO_META_DATA_PACKET_SIZE \ + (DVB_RINGBUFFER_PKTHDRSIZE + \ + sizeof(struct mpq_streambuffer_packet_header) + \ + sizeof(struct mpq_adapter_audio_meta_data)) + +#define AUDIO_META_DATA_BUFFER_SIZE \ + (AUDIO_NUM_OF_PES_PACKETS * AUDIO_META_DATA_PACKET_SIZE) + /* Max number open() request can be done on demux device */ #define MPQ_MAX_DMX_FILES 128 -/** - * TSIF alias name length - */ +/* TSIF alias name length */ #define TSIF_NAME_LENGTH 20 /** @@ -332,6 +340,30 @@ const struct dvb_dmx_video_patterns *patterns[DVB_DMX_MAX_SEARCH_PATTERN_NUM]; u64 prev_stc; }; +/* require a bare minimal mpq_audio_feed_info struct */ +struct mpq_audio_feed_info { + struct mpq_streambuffer *audio_buffer; + spinlock_t audio_buffer_lock; + struct mpq_decoder_buffers_desc buffer_desc; + struct pes_packet_header pes_header; + u32 pes_header_left_bytes; + u32 pes_header_offset; + int fullness_wait_cancel; + enum mpq_adapter_stream_if stream_interface; + u32 frame_offset; /* pes frame offset */ + struct dmx_pts_dts_info saved_pts_dts_info; + struct dmx_pts_dts_info new_pts_dts_info; + int saved_info_used; + int new_info_exists; + int first_pts_dts_copy; + u32 tei_errs; + int last_continuity; + u32 continuity_errs; + u32 ts_packets_num; + u32 ts_dropped_bytes; + u64 prev_stc; +}; + /** * mpq feed object - mpq common plugin feed information * @@ -367,6 +399,7 @@ struct mpq_feed { struct ion_handle *sdmx_buf_handle; struct mpq_video_feed_info video_info; + struct mpq_audio_feed_info audio_info; }; /** @@ -509,6 +542,7 @@ struct mpq_demux { enum sdmx_log_level sdmx_log_level; struct timespec last_notification_time; + int ts_packet_timestamp_source; }; /** @@ -879,11 +913,12 @@ struct dvb_demux_feed *mpq_dmx_peer_rec_feed(struct dvb_demux_feed *feed); /** * mpq_dmx_decoder_eos_cmd() - Report EOS event to the mpq_streambuffer * - * @mpq_feed: Video mpq_feed object for notification + * @mpq_feed: Audio/Video mpq_feed object for notification + * @feed_type: Feed type( Audio or Video ) * * Return error code */ -int mpq_dmx_decoder_eos_cmd(struct mpq_feed *mpq_feed); +int mpq_dmx_decoder_eos_cmd(struct mpq_feed *mpq_feed, int feed_type); /** * mpq_dmx_parse_mandatory_pes_header() - Parse non-optional PES header fields @@ -1023,5 +1058,66 @@ int mpq_dmx_get_param_scramble_even(void); /* Return the common module parameter mpq_sdmx_scramble_default_discard */ int mpq_dmx_get_param_scramble_default_discard(void); +/* APIs for Audio stream buffers interface -- Added for broadcase use case */ +/* + * The Audio/Video drivers (or consumers) require the stream_buffer information + * for consuming packet headers and compressed AV data from the + * ring buffer filled by demux driver which is the producer + */ +struct mpq_streambuffer *consumer_audio_streambuffer(int dmx_ts_pes_audio); +struct mpq_streambuffer *consumer_video_streambuffer(int dmx_ts_pes_video); + +int mpq_dmx_init_audio_feed(struct mpq_feed *mpq_feed); + +int mpq_dmx_terminate_audio_feed(struct mpq_feed *mpq_feed); + +int mpq_dmx_parse_remaining_audio_pes_header( + struct dvb_demux_feed *feed, + struct mpq_audio_feed_info *feed_data, + struct pes_packet_header *pes_header, + const u8 *buf, + u32 *ts_payload_offset, + int *bytes_avail); + +static inline void mpq_dmx_save_audio_pts_dts( + struct mpq_audio_feed_info *feed_data) +{ + if (feed_data->new_info_exists) { + feed_data->saved_pts_dts_info.pts_exist = + feed_data->new_pts_dts_info.pts_exist; + feed_data->saved_pts_dts_info.pts = + feed_data->new_pts_dts_info.pts; + feed_data->saved_pts_dts_info.dts_exist = + feed_data->new_pts_dts_info.dts_exist; + feed_data->saved_pts_dts_info.dts = + feed_data->new_pts_dts_info.dts; + + feed_data->new_info_exists = 0; + feed_data->saved_info_used = 0; + } +} + +/* + * mpq_dmx_process_audio_packet - Assemble Audio PES data and output to + * stream buffer connected to decoder. + */ +int mpq_dmx_process_audio_packet(struct dvb_demux_feed *feed, const u8 *buf); + +static inline void mpq_dmx_write_audio_pts_dts( + struct mpq_audio_feed_info *feed_data, + struct dmx_pts_dts_info *info) +{ + if (!feed_data->saved_info_used) { + info->pts_exist = feed_data->saved_pts_dts_info.pts_exist; + info->pts = feed_data->saved_pts_dts_info.pts; + info->dts_exist = feed_data->saved_pts_dts_info.dts_exist; + info->dts = feed_data->saved_pts_dts_info.dts; + + feed_data->saved_info_used = 1; + } else { + info->pts_exist = 0; + info->dts_exist = 0; + } +} #endif /* _MPQ_DMX_PLUGIN_COMMON_H */ diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c index be88bc1bf19f..4f4b9170d639 100644 --- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c +++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, 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 @@ -1634,6 +1634,9 @@ static int mpq_tspp_dmx_write_to_decoder( if (dvb_dmx_is_video_feed(feed)) return mpq_dmx_process_video_packet(feed, buf); + if (dvb_dmx_is_audio_feed(feed)) + return mpq_dmx_process_audio_packet(feed, buf); + if (dvb_dmx_is_pcr_feed(feed)) return mpq_dmx_process_pcr_packet(feed, buf); @@ -1663,7 +1666,7 @@ static int mpq_tspp_dmx_get_caps(struct dmx_demux *demux, caps->caps = DMX_CAP_PULL_MODE | DMX_CAP_VIDEO_DECODER_DATA | DMX_CAP_TS_INSERTION | DMX_CAP_VIDEO_INDEXING | - DMX_CAP_AUTO_BUFFER_FLUSH; + DMX_CAP_AUDIO_DECODER_DATA | DMX_CAP_AUTO_BUFFER_FLUSH; caps->recording_max_video_pids_indexed = 0; caps->num_decoders = MPQ_ADAPTER_MAX_NUM_OF_INTERFACES; caps->num_demux_devices = CONFIG_DVB_MPQ_NUM_DMX_DEVICES; @@ -1753,6 +1756,8 @@ static int mpq_tspp_dmx_get_stc(struct dmx_demux *demux, unsigned int num, { enum tspp_source source; u32 tcr_counter; + u64 avtimer_stc = 0; + int tts_source = 0; if (!demux || !stc || !base) return -EINVAL; @@ -1764,11 +1769,18 @@ static int mpq_tspp_dmx_get_stc(struct dmx_demux *demux, unsigned int num, else return -EINVAL; - tspp_get_ref_clk_counter(0, source, &tcr_counter); - - *stc = ((u64)tcr_counter) * 256; /* conversion to 27MHz */ - *base = 300; /* divisor to get 90KHz clock from stc value */ + if (tspp_get_tts_source(0, &tts_source) < 0) + tts_source = TSIF_TTS_TCR; + if (tts_source != TSIF_TTS_LPASS_TIMER) { + tspp_get_ref_clk_counter(0, source, &tcr_counter); + *stc = ((u64)tcr_counter) * 256; /* conversion to 27MHz */ + *base = 300; /* divisor to get 90KHz clock from stc value */ + } else { + if (tspp_get_lpass_time_counter(0, source, &avtimer_stc) < 0) + return -EINVAL; + *stc = avtimer_stc; + } return 0; } @@ -1840,6 +1852,10 @@ static int mpq_tspp_dmx_init( /* Extend dvb-demux debugfs with TSPP statistics. */ mpq_dmx_init_debugfs_entries(mpq_demux); + /* Get the TSIF TTS info */ + if (tspp_get_tts_source(0, &mpq_demux->ts_packet_timestamp_source) < 0) + mpq_demux->ts_packet_timestamp_source = TSIF_TTS_TCR; + return 0; init_failed_dmx_release: diff --git a/drivers/media/platform/msm/dvb/include/mpq_adapter.h b/drivers/media/platform/msm/dvb/include/mpq_adapter.h index 54e671c3b38d..c55a5aa1ae32 100644 --- a/drivers/media/platform/msm/dvb/include/mpq_adapter.h +++ b/drivers/media/platform/msm/dvb/include/mpq_adapter.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, 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 @@ -27,12 +27,24 @@ enum mpq_adapter_stream_if { /** Interface holding stream-buffer for video1 stream */ MPQ_ADAPTER_VIDEO1_STREAM_IF = 1, - /** Interface holding stream-buffer for video1 stream */ + /** Interface holding stream-buffer for video2 stream */ MPQ_ADAPTER_VIDEO2_STREAM_IF = 2, - /** Interface holding stream-buffer for video1 stream */ + /** Interface holding stream-buffer for video3 stream */ MPQ_ADAPTER_VIDEO3_STREAM_IF = 3, + /** Interface holding stream-buffer for audio0 stream */ + MPQ_ADAPTER_AUDIO0_STREAM_IF = 4, + + /** Interface holding stream-buffer for audio1 stream */ + MPQ_ADAPTER_AUDIO1_STREAM_IF = 5, + + /** Interface holding stream-buffer for audio2 stream */ + MPQ_ADAPTER_AUDIO2_STREAM_IF = 6, + + /** Interface holding stream-buffer for audio3 stream */ + MPQ_ADAPTER_AUDIO3_STREAM_IF = 7, + /** Maximum number of interfaces holding stream-buffers */ MPQ_ADAPTER_MAX_NUM_OF_INTERFACES, }; @@ -113,6 +125,17 @@ struct mpq_adapter_video_meta_data { } info; } __packed; +/** The meta-data used for audio interface */ +struct mpq_adapter_audio_meta_data { + /** meta-data packet type */ + enum dmx_packet_type packet_type; + + /** packet-type specific information */ + union { + struct dmx_pes_packet_info pes; + struct dmx_marker_info marker; + } info; +} __packed; /** Callback function to notify on registrations of specific interfaces */ typedef void (*mpq_adapter_stream_if_callback)( diff --git a/drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h b/drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h index b24dc1f3b5ff..62404513007a 100644 --- a/drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h +++ b/drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, 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 @@ -15,7 +15,6 @@ #include "dvb_ringbuffer.h" - /** * DOC: MPQ Stream Buffer * @@ -459,4 +458,37 @@ ssize_t mpq_streambuffer_metadata_free(struct mpq_streambuffer *sbuff); */ int mpq_streambuffer_flush(struct mpq_streambuffer *sbuff); +/* + * ------------------------------------------------------ + * Consumer or AV Decoder Stream Interface to Ring Buffer + * ------------------------------------------------------ + * Producer is Demux Driver + * ------------------------ + * + * call from Audio/Video Decoder Driver to find Audio/Video + * streambuffer AV handles, "DMX_PES_AUDIO0 through 3" or + * DMX_PES_VIDEO0 through 3" interfaces corresponding to 4 programs. + */ + +/* call from Audio/Video Decoder Driver via POLLING to consume + * Headers and Compressed data from ring buffer using streambuffer handle. + * hdrdata[] and cdata[] buffers have to be malloc'd by consumer + * + * -------------------------- + * Consumer Calling Sequence + * -------------------------- + * Find the streambuffer corresponding to a DMX TS PES stream instance. + * 1. consumer_audio_streambuffer() or consumer_video_streambuffer() + * Process the packet headers if required. + * 2. mpq_read_new_packet_hdr_data() + * Process the compressed data by forwarding to AV decoder. + * 3. mpq_read_new_packet_compressed_data() + * Dispose the packet. + * 4. mpq_dispose_new_packet_read() + * + * The Audio/Video drivers (or consumers) require the stream_buffer information + * for consuming packet headers and compressed AV data from the + * ring buffer filled by demux driver which is the producer + */ + #endif /* _MPQ_STREAM_BUFFER_H */ diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c index b485af0ee561..92b6e8ffa92e 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c @@ -438,6 +438,40 @@ static void sde_mdp_parse_vbif_qos(struct platform_device *pdev, } } +static int sde_mdp_parse_vbif_xin_id(struct platform_device *pdev, + struct sde_rot_data_type *mdata) +{ + int rc = 0; + bool default_xin_id = false; + + mdata->nxid = sde_mdp_parse_dt_prop_len(pdev, + "qcom,mdss-rot-xin-id"); + if (!mdata->nxid) { + mdata->nxid = 2; + default_xin_id = true; + } + mdata->vbif_xin_id = kzalloc(sizeof(u32) * + mdata->nxid, GFP_KERNEL); + if (!mdata->vbif_xin_id) { + SDEROT_ERR("xin alloc failure\n"); + return -ENOMEM; + } + if (default_xin_id) { + mdata->vbif_xin_id[XIN_SSPP] = XIN_SSPP; + mdata->vbif_xin_id[XIN_WRITEBACK] = XIN_WRITEBACK; + } else { + rc = sde_mdp_parse_dt_handler(pdev, + "qcom,mdss-rot-xin-id", mdata->vbif_xin_id, + mdata->nxid); + if (rc) { + SDEROT_ERR("vbif xin id setting not found\n"); + return rc; + } + } + + return 0; +} + static int sde_mdp_parse_dt_misc(struct platform_device *pdev, struct sde_rot_data_type *mdata) { @@ -463,6 +497,11 @@ static int sde_mdp_parse_dt_misc(struct platform_device *pdev, "Could not read optional property: highest bank bit\n"); sde_mdp_parse_vbif_qos(pdev, mdata); + rc = sde_mdp_parse_vbif_xin_id(pdev, mdata); + if (rc) { + SDEROT_DBG("vbif xin id dt parse failure\n"); + return rc; + } mdata->mdp_base = mdata->sde_io.base + SDE_MDP_OFFSET; diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h index 92ea32d9e819..5cbdc03ced9d 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h @@ -27,6 +27,10 @@ #define MDSS_MDP_HW_REV_320 0x30020000 /* sdm660 */ #define MDSS_MDP_HW_REV_330 0x30030000 /* sdm630 */ +/* XIN mapping */ +#define XIN_SSPP 0 +#define XIN_WRITEBACK 1 + struct sde_mult_factor { uint32_t numer; uint32_t denom; @@ -167,11 +171,17 @@ struct sde_rot_data_type { u32 *vbif_nrt_qos; u32 npriority_lvl; + u32 *vbif_xin_id; + u32 nxid; + int iommu_attached; int iommu_ref_cnt; int (*iommu_ctrl)(int enable); int (*secure_session_ctrl)(int enable); int (*wait_for_transition)(int state, int request); + void (*vbif_reg_lock)(void); + void (*vbif_reg_unlock)(void); + bool (*handoff_pending)(void); struct sde_rot_vbif_debug_bus *nrt_vbif_dbg_bus; u32 nrt_vbif_dbg_bus_size; @@ -182,6 +192,8 @@ struct sde_rot_data_type { int sec_cam_en; bool callback_request; struct ion_client *iclient; + + bool handoff_done; }; int sde_rotator_base_init(struct sde_rot_data_type **pmdata, diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c index b55bc905ea7a..c3ed1f39c2be 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c @@ -1170,10 +1170,16 @@ static u32 sde_rotator_calc_buf_bw(struct sde_mdp_format_params *fmt, u32 bw; bw = width * height * frame_rate; - if (fmt->chroma_sample == SDE_MDP_CHROMA_420) + + if (sde_mdp_is_tp10_format(fmt)) + bw *= 2; + else if (sde_mdp_is_p010_format(fmt)) + bw *= 3; + else if (fmt->chroma_sample == SDE_MDP_CHROMA_420) bw = (bw * 3) / 2; else bw *= fmt->bpp; + SDEROT_EVTLOG(bw, width, height, frame_rate, fmt->format); return bw; } @@ -1818,6 +1824,17 @@ static int sde_rotator_validate_entry(struct sde_rot_mgr *mgr, int ret; struct sde_rotation_item *item; struct sde_rot_perf *perf; + struct sde_rot_data_type *mdata = sde_rot_get_mdata(); + + /* Check to ensure handoff is completed before 1st rotation request */ + if (!mdata->handoff_done && mdata->handoff_pending) { + mdata->handoff_done = !mdata->handoff_pending(); + if (!mdata->handoff_done) { + SDEROT_INFO( + "Splash Still on, Reject Rotation Request\n"); + return -EINVAL; + } + } item = &entry->item; diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.h index 23548b99fce4..bdd16a93bbb1 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.h +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012, 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012, 2015-2017, 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 @@ -124,10 +124,34 @@ static inline bool sde_mdp_is_linear_format(struct sde_mdp_format_params *fmt) return fmt && (fmt->frame_format == SDE_MDP_FMT_LINEAR); } +static inline bool sde_mdp_is_nv12_format(struct sde_mdp_format_params *fmt) +{ + return fmt && (fmt->fetch_planes == SDE_MDP_PLANE_PSEUDO_PLANAR) && + (fmt->chroma_sample == SDE_MDP_CHROMA_420); +} + +static inline bool sde_mdp_is_nv12_8b_format(struct sde_mdp_format_params *fmt) +{ + return fmt && sde_mdp_is_nv12_format(fmt) && + (fmt->pixel_mode == SDE_MDP_PIXEL_NORMAL); +} + +static inline bool sde_mdp_is_nv12_10b_format(struct sde_mdp_format_params *fmt) +{ + return fmt && sde_mdp_is_nv12_format(fmt) && + (fmt->pixel_mode == SDE_MDP_PIXEL_10BIT); +} + static inline bool sde_mdp_is_tp10_format(struct sde_mdp_format_params *fmt) { - return fmt && ((fmt->format == SDE_PIX_FMT_Y_CBCR_H2V2_TP10_UBWC) || - (fmt->format == SDE_PIX_FMT_Y_CBCR_H2V2_TP10)); + return fmt && sde_mdp_is_nv12_10b_format(fmt) && + fmt->unpack_tight; +} + +static inline bool sde_mdp_is_p010_format(struct sde_mdp_format_params *fmt) +{ + return fmt && sde_mdp_is_nv12_10b_format(fmt) && + !fmt->unpack_tight; } static inline bool sde_mdp_is_yuv_format(struct sde_mdp_format_params *fmt) diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_hwio.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_hwio.h index 051db7863c54..8a8711ea468b 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_hwio.h +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_hwio.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, 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 @@ -65,6 +65,8 @@ #define MMSS_VBIF_NRT_VBIF_IN_WR_LIM_CONF2 0x00C8 #define MMSS_VBIF_NRT_VBIF_OUT_RD_LIM_CONF0 0x00D0 #define MMSS_VBIF_NRT_VBIF_OUT_WR_LIM_CONF0 0x00D4 +#define MDSS_VBIF_QOS_RP_REMAP_BASE 0x550 +#define MDSS_VBIF_QOS_LVL_REMAP_BASE 0x570 #define SDE_MDP_REG_TRAFFIC_SHAPER_EN BIT(31) #define SDE_MDP_REG_TRAFFIC_SHAPER_RD_CLIENT(num) (0x030 + (num * 4)) diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c index d7fb167ab49f..89fde987d4c1 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c @@ -42,9 +42,6 @@ #define TRAFFIC_SHAPE_CLKTICK_14MS 268800 #define TRAFFIC_SHAPE_CLKTICK_12MS 230400 -/* XIN mapping */ -#define XIN_SSPP 0 -#define XIN_WRITEBACK 1 /* wait for at most 2 vsync for lowest refresh rate (24hz) */ #define KOFF_TIMEOUT msecs_to_jiffies(42 * 32) @@ -1698,11 +1695,14 @@ static int sde_hw_rotator_config(struct sde_rot_hw_resource *hw, item->input.format, item->output.format, entry->perf->config.frame_rate); + if (mdata->vbif_reg_lock) + mdata->vbif_reg_lock(); + if (mdata->default_ot_rd_limit) { struct sde_mdp_set_ot_params ot_params; memset(&ot_params, 0, sizeof(struct sde_mdp_set_ot_params)); - ot_params.xin_id = XIN_SSPP; + ot_params.xin_id = mdata->vbif_xin_id[XIN_SSPP]; ot_params.num = 0; /* not used */ ot_params.width = entry->perf->config.input.width; ot_params.height = entry->perf->config.input.height; @@ -1724,7 +1724,7 @@ static int sde_hw_rotator_config(struct sde_rot_hw_resource *hw, struct sde_mdp_set_ot_params ot_params; memset(&ot_params, 0, sizeof(struct sde_mdp_set_ot_params)); - ot_params.xin_id = XIN_WRITEBACK; + ot_params.xin_id = mdata->vbif_xin_id[XIN_WRITEBACK]; ot_params.num = 0; /* not used */ ot_params.width = entry->perf->config.input.width; ot_params.height = entry->perf->config.input.height; @@ -1745,36 +1745,67 @@ static int sde_hw_rotator_config(struct sde_rot_hw_resource *hw, if (test_bit(SDE_QOS_PER_PIPE_LUT, mdata->sde_qos_map)) { u32 qos_lut = 0; /* low priority for nrt read client */ - trace_rot_perf_set_qos_luts(XIN_SSPP, sspp_cfg.fmt->format, - qos_lut, sde_mdp_is_linear_format(sspp_cfg.fmt)); + trace_rot_perf_set_qos_luts(mdata->vbif_xin_id[XIN_SSPP], + sspp_cfg.fmt->format, qos_lut, + sde_mdp_is_linear_format(sspp_cfg.fmt)); SDE_ROTREG_WRITE(rot->mdss_base, ROT_SSPP_CREQ_LUT, qos_lut); } + /* Set CDP control registers to 0 if CDP is disabled */ + if (!test_bit(SDE_QOS_CDP, mdata->sde_qos_map)) { + SDE_ROTREG_WRITE(rot->mdss_base, ROT_SSPP_CDP_CNTL, 0x0); + SDE_ROTREG_WRITE(rot->mdss_base, ROT_WB_CDP_CNTL, 0x0); + } + if (mdata->npriority_lvl > 0) { - u32 mask, reg_val, i, vbif_qos; + u32 mask, reg_val, i, j, vbif_qos, reg_val_lvl, reg_high; for (i = 0; i < mdata->npriority_lvl; i++) { - reg_val = SDE_VBIF_READ(mdata, - MMSS_VBIF_NRT_VBIF_QOS_REMAP_00 + i*4); - mask = 0x3 << (XIN_SSPP * 2); - reg_val &= ~(mask); - vbif_qos = mdata->vbif_nrt_qos[i]; - reg_val |= vbif_qos << (XIN_SSPP * 2); - /* ensure write is issued after the read operation */ - mb(); - SDE_VBIF_WRITE(mdata, - MMSS_VBIF_NRT_VBIF_QOS_REMAP_00 + i*4, - reg_val); + + for (j = 0; j < mdata->nxid; j++) { + reg_high = ((mdata->vbif_xin_id[j] & + 0x8) >> 3) * 4 + (i * 8); + + reg_val = SDE_VBIF_READ(mdata, + MDSS_VBIF_QOS_RP_REMAP_BASE + reg_high); + reg_val_lvl = SDE_VBIF_READ(mdata, + MDSS_VBIF_QOS_LVL_REMAP_BASE + reg_high); + + mask = 0x3 << (mdata->vbif_xin_id[j] * 4); + vbif_qos = mdata->vbif_nrt_qos[i]; + + reg_val &= ~(mask); + reg_val |= vbif_qos << + (mdata->vbif_xin_id[j] * 4); + + reg_val_lvl &= ~(mask); + reg_val_lvl |= vbif_qos << + (mdata->vbif_xin_id[j] * 4); + + pr_debug("idx:%d xin:%d reg:0x%x val:0x%x lvl:0x%x\n", + i, mdata->vbif_xin_id[j], + reg_high, reg_val, reg_val_lvl); + SDE_VBIF_WRITE(mdata, + MDSS_VBIF_QOS_RP_REMAP_BASE + + reg_high, reg_val); + SDE_VBIF_WRITE(mdata, + MDSS_VBIF_QOS_LVL_REMAP_BASE + + reg_high, reg_val_lvl); + } } } /* Enable write gather for writeback to remove write gaps, which * may hang AXI/BIMC/SDE. */ - SDE_VBIF_WRITE(mdata, MMSS_VBIF_NRT_VBIF_WRITE_GATHTER_EN, - BIT(XIN_WRITEBACK)); + if (!((mdata->mdss_version == MDSS_MDP_HW_REV_320) || + (mdata->mdss_version == MDSS_MDP_HW_REV_330))) + SDE_VBIF_WRITE(mdata, MMSS_VBIF_NRT_VBIF_WRITE_GATHTER_EN, + BIT(mdata->vbif_xin_id[XIN_WRITEBACK])); + if (mdata->vbif_reg_unlock) + mdata->vbif_reg_unlock(); return 0; } diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c index 46001fa7b429..9b1175d8f4a6 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c @@ -381,8 +381,12 @@ static void sde_smmu_callback(struct mdss_smmu_intf *smmu) /* Copy mmu device info into sde private structure */ mdata->iommu_ctrl = smmu->iommu_ctrl; + mdata->vbif_reg_lock = smmu->reg_lock; + mdata->vbif_reg_unlock = smmu->reg_unlock; mdata->wait_for_transition = smmu->wait_for_transition; mdata->secure_session_ctrl = smmu->secure_session_ctrl; + mdata->handoff_pending = smmu->handoff_pending; + if (smmu->is_secure) { mdata->sde_smmu[SDE_IOMMU_DOMAIN_ROT_SECURE].dev = smmu->dev; mdata->sde_smmu[SDE_IOMMU_DOMAIN_ROT_SECURE].domain = diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c index 1365bec268cf..2aebc62d09d5 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c @@ -1087,8 +1087,7 @@ static void handle_event_change(enum hal_command_response cmd, void *data) rc = msm_comm_g_ctrl_for_id(inst, V4L2_CID_MPEG_VIDC_VIDEO_CONTINUE_DATA_TRANSFER); - if ((!IS_ERR_VALUE(rc) && rc == true) || - is_thumbnail_session(inst)) { + if (!IS_ERR_VALUE(rc) && rc == true) { event = V4L2_EVENT_SEQ_CHANGED_SUFFICIENT; if (msm_comm_get_stream_output_mode(inst) == diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index d3b41331faec..134995c9cd3c 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -562,7 +562,6 @@ static int qseecom_scm_call2(uint32_t svc_id, uint32_t tz_cmd_id, case QSEOS_REGISTER_LISTENER: { struct qseecom_register_listener_ireq *req; struct qseecom_register_listener_64bit_ireq *req_64bit; - smc_id = TZ_OS_REGISTER_LISTENER_ID; desc.arginfo = TZ_OS_REGISTER_LISTENER_ID_PARAM_ID; if (qseecom.qsee_version < QSEE_VERSION_40) { @@ -579,8 +578,15 @@ static int qseecom_scm_call2(uint32_t svc_id, uint32_t tz_cmd_id, desc.args[1] = req_64bit->sb_ptr; desc.args[2] = req_64bit->sb_len; } + smc_id = TZ_OS_REGISTER_LISTENER_SMCINVOKE_ID; __qseecom_reentrancy_check_if_no_app_blocked(smc_id); ret = scm_call2(smc_id, &desc); + if (ret) { + smc_id = TZ_OS_REGISTER_LISTENER_ID; + __qseecom_reentrancy_check_if_no_app_blocked( + smc_id); + ret = scm_call2(smc_id, &desc); + } break; } case QSEOS_DEREGISTER_LISTENER: { diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index 4fd8e260d80e..be8ec97574f0 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -48,8 +48,6 @@ const char *ce_name[WCN3990_MAX_IRQ] = { #define SNOC_HIF_POWER_DOWN_DELAY 30 static void ath10k_snoc_buffer_cleanup(struct ath10k *ar); -static int ath10k_snoc_init_irq(struct ath10k *ar); -static int ath10k_snoc_deinit_irq(struct ath10k *ar); static int ath10k_snoc_request_irq(struct ath10k *ar); static void ath10k_snoc_free_irq(struct ath10k *ar); static void ath10k_snoc_htc_tx_cb(struct ath10k_ce_pipe *ce_state); @@ -703,31 +701,6 @@ static void ath10k_snoc_hif_send_complete_check(struct ath10k *ar, u8 pipe, ath10k_ce_per_engine_service(ar, pipe); } -static void ath10k_snoc_kill_tasklet(struct ath10k *ar) -{ - struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); - int i; - - for (i = 0; i < CE_COUNT; i++) - tasklet_kill(&ar_snoc->pipe_info[i].intr); - - del_timer_sync(&ar_snoc->rx_post_retry); -} - -static void ath10k_snoc_ce_deinit(struct ath10k *ar) -{ - int i; - - for (i = 0; i < CE_COUNT; i++) - ath10k_ce_deinit_pipe(ar, i); -} - -static void ath10k_snoc_release_resource(struct ath10k *ar) -{ - netif_napi_del(&ar->napi); - ath10k_snoc_ce_deinit(ar); -} - static int ath10k_snoc_hif_map_service_to_pipe(struct ath10k *ar, u16 service_id, u8 *ul_pipe, u8 *dl_pipe) @@ -862,6 +835,7 @@ static void ath10k_snoc_buffer_cleanup(struct ath10k *ar) struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); int pipe_num; + del_timer_sync(&ar_snoc->rx_post_retry); for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) { struct ath10k_snoc_pipe *pipe_info; @@ -873,7 +847,6 @@ static void ath10k_snoc_buffer_cleanup(struct ath10k *ar) static void ath10k_snoc_flush(struct ath10k *ar) { - ath10k_snoc_kill_tasklet(ar); ath10k_snoc_buffer_cleanup(ar); } @@ -921,6 +894,12 @@ static void ath10k_snoc_free_pipes(struct ath10k *ar) ath10k_ce_free_pipe(ar, i); } +static void ath10k_snoc_release_resource(struct ath10k *ar) +{ + netif_napi_del(&ar->napi); + ath10k_snoc_free_pipes(ar); +} + static int ath10k_snoc_init_pipes(struct ath10k *ar) { int i, ret; @@ -944,14 +923,6 @@ static void ath10k_snoc_hif_power_down(struct ath10k *ar) icnss_wlan_disable(ICNSS_OFF); } -static void ath10k_snoc_ce_tasklet(unsigned long ptr) -{ - struct ath10k_snoc_pipe *pipe = (struct ath10k_snoc_pipe *)ptr; - struct ath10k_snoc *ar_snoc = pipe->ar_snoc; - - ath10k_ce_per_engine_service(ar_snoc->ar, pipe->pipe_num); -} - int ath10k_snoc_get_ce_id(struct ath10k *ar, int irq) { int i; @@ -1015,30 +986,6 @@ static void ath10k_snoc_free_irq(struct ath10k *ar) free_irq(ar_snoc->ce_irqs[id], ar); } -static void ath10k_snoc_init_irq_tasklets(struct ath10k *ar) -{ - struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); - int i; - - for (i = 0; i < CE_COUNT; i++) { - ar_snoc->pipe_info[i].ar_snoc = ar_snoc; - tasklet_init(&ar_snoc->pipe_info[i].intr, - ath10k_snoc_ce_tasklet, - (unsigned long)&ar_snoc->pipe_info[i]); - } -} - -static int ath10k_snoc_init_irq(struct ath10k *ar) -{ - ath10k_snoc_init_irq_tasklets(ar); - return 0; -} - -static int ath10k_snoc_deinit_irq(struct ath10k *ar) -{ - ath10k_snoc_irq_disable(ar); - return 0; -} static int ath10k_snoc_get_soc_info(struct ath10k *ar) { @@ -1282,16 +1229,10 @@ static int ath10k_snoc_probe(struct platform_device *pdev) netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_snoc_napi_poll, ATH10K_NAPI_BUDGET); - ret = ath10k_snoc_init_irq(ar); - if (ret) { - ath10k_err(ar, "failed to init irqs: %d\n", ret); - goto err_free_pipes; - } - ret = ath10k_snoc_request_irq(ar); if (ret) { ath10k_warn(ar, "failed to request irqs: %d\n", ret); - goto err_deinit_irq; + goto err_free_pipes; } chip_id = ar_snoc->target_info.soc_version; @@ -1307,10 +1248,6 @@ static int ath10k_snoc_probe(struct platform_device *pdev) err_free_irq: ath10k_snoc_free_irq(ar); - ath10k_snoc_kill_tasklet(ar); - -err_deinit_irq: - ath10k_snoc_deinit_irq(ar); err_free_pipes: ath10k_snoc_free_pipes(ar); @@ -1334,8 +1271,6 @@ static int ath10k_snoc_remove(struct platform_device *pdev) ath10k_core_unregister(ar); ath10k_snoc_free_irq(ar); - ath10k_snoc_kill_tasklet(ar); - ath10k_snoc_deinit_irq(ar); ath10k_snoc_release_resource(ar); ath10k_snoc_free_pipes(ar); ath10k_core_destroy(ar); diff --git a/drivers/net/wireless/ath/ath10k/snoc.h b/drivers/net/wireless/ath/ath10k/snoc.h index 7c8a3ca8fabf..1754a3e91a00 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.h +++ b/drivers/net/wireless/ath/ath10k/snoc.h @@ -35,7 +35,6 @@ struct snoc_state { * @buf_sz: buffer size * @pipe_lock: pipe lock * @ar_snoc: snoc private structure - * @intr: tasklet structure */ struct ath10k_snoc_pipe { @@ -46,7 +45,6 @@ struct ath10k_snoc_pipe { /* protect ce info */ spinlock_t pipe_lock; struct ath10k_snoc *ar_snoc; - struct tasklet_struct intr; }; /* struct ath10k_snoc_supp_chip: supported chip set @@ -97,7 +95,6 @@ struct ath10k_target_info { * @mem_pa: mem base physical address * @target_info: snoc target info * @mem_len: mempry map length - * @intr_tq: rx tasklet handle * @pipe_info: pipe info struct * @ce_lock: protect ce structures * @ce_states: maps ce id to ce state @@ -112,7 +109,6 @@ struct ath10k_snoc { dma_addr_t mem_pa; struct ath10k_target_info target_info; size_t mem_len; - struct tasklet_struct intr_tq; struct ath10k_snoc_pipe pipe_info[CE_COUNT_MAX]; /* protects CE info */ spinlock_t ce_lock; diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index 8e585aefebf2..c1448955d3ed 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2013, Sony Mobile Communications AB. - * 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 @@ -27,6 +27,7 @@ #include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/spinlock.h> +#include <linux/syscore_ops.h> #include <linux/reboot.h> #include <linux/irqchip/msm-mpm-irq.h> #include "../core.h" @@ -70,6 +71,8 @@ struct msm_pinctrl { void __iomem *regs; }; +static struct msm_pinctrl *msm_pinctrl_data; + static inline struct msm_pinctrl *to_msm_pinctrl(struct gpio_chip *gc) { return container_of(gc, struct msm_pinctrl, chip); @@ -896,6 +899,52 @@ static void msm_pinctrl_setup_pm_reset(struct msm_pinctrl *pctrl) } } +#ifdef CONFIG_PM +static int msm_pinctrl_suspend(void) +{ + return 0; +} + +static void msm_pinctrl_resume(void) +{ + int i, irq; + u32 val; + unsigned long flags; + struct irq_desc *desc; + const struct msm_pingroup *g; + const char *name = "null"; + struct msm_pinctrl *pctrl = msm_pinctrl_data; + + if (!msm_show_resume_irq_mask) + return; + + spin_lock_irqsave(&pctrl->lock, flags); + for_each_set_bit(i, pctrl->enabled_irqs, pctrl->chip.ngpio) { + g = &pctrl->soc->groups[i]; + val = readl_relaxed(pctrl->regs + g->intr_status_reg); + if (val & BIT(g->intr_status_bit)) { + irq = irq_find_mapping(pctrl->chip.irqdomain, i); + desc = irq_to_desc(irq); + if (desc == NULL) + name = "stray irq"; + else if (desc->action && desc->action->name) + name = desc->action->name; + + pr_warn("%s: %d triggered %s\n", __func__, irq, name); + } + } + spin_unlock_irqrestore(&pctrl->lock, flags); +} +#else +#define msm_pinctrl_suspend NULL +#define msm_pinctrl_resume NULL +#endif + +static struct syscore_ops msm_pinctrl_pm_ops = { + .suspend = msm_pinctrl_suspend, + .resume = msm_pinctrl_resume, +}; + int msm_pinctrl_probe(struct platform_device *pdev, const struct msm_pinctrl_soc_data *soc_data) { @@ -903,7 +952,8 @@ int msm_pinctrl_probe(struct platform_device *pdev, struct resource *res; int ret; - pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); + msm_pinctrl_data = pctrl = devm_kzalloc(&pdev->dev, + sizeof(*pctrl), GFP_KERNEL); if (!pctrl) { dev_err(&pdev->dev, "Can't allocate msm_pinctrl\n"); return -ENOMEM; @@ -944,6 +994,7 @@ int msm_pinctrl_probe(struct platform_device *pdev, pctrl->irq_chip_extn = &mpm_pinctrl_extn; platform_set_drvdata(pdev, pctrl); + register_syscore_ops(&msm_pinctrl_pm_ops); dev_dbg(&pdev->dev, "Probed Qualcomm pinctrl driver\n"); return 0; @@ -958,6 +1009,7 @@ int msm_pinctrl_remove(struct platform_device *pdev) pinctrl_unregister(pctrl->pctrl); unregister_restart_handler(&pctrl->restart_nb); + unregister_syscore_ops(&msm_pinctrl_pm_ops); return 0; } diff --git a/drivers/pinctrl/qcom/pinctrl-msm.h b/drivers/pinctrl/qcom/pinctrl-msm.h index 54fdd04ce9d5..e986fdacc0bf 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.h +++ b/drivers/pinctrl/qcom/pinctrl-msm.h @@ -121,4 +121,5 @@ int msm_pinctrl_probe(struct platform_device *pdev, const struct msm_pinctrl_soc_data *soc_data); int msm_pinctrl_remove(struct platform_device *pdev); +extern int msm_show_resume_irq_mask; #endif diff --git a/drivers/platform/msm/ipa/ipa_api.c b/drivers/platform/msm/ipa/ipa_api.c index 4dc3910737e1..1a6ba1a915a0 100644 --- a/drivers/platform/msm/ipa/ipa_api.c +++ b/drivers/platform/msm/ipa/ipa_api.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, 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 @@ -113,7 +113,8 @@ const char *ipa_clients_strings[IPA_CLIENT_MAX] = { __stringify(IPA_CLIENT_A5_WLAN_AMPDU_PROD), __stringify(IPA_CLIENT_A2_EMBEDDED_PROD), __stringify(IPA_CLIENT_A2_TETHERED_PROD), - __stringify(IPA_CLIENT_APPS_LAN_WAN_PROD), + __stringify(IPA_CLIENT_APPS_LAN_PROD), + __stringify(IPA_CLIENT_APPS_WAN_PROD), __stringify(IPA_CLIENT_APPS_CMD_PROD), __stringify(IPA_CLIENT_ODU_PROD), __stringify(IPA_CLIENT_MHI_PROD), diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c index 2cd8d5c975f4..9cab7e010c3a 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c @@ -86,6 +86,7 @@ static const int ep_mapping[3][IPA_CLIENT_MAX] = { [IPA_1_1][IPA_CLIENT_A5_WLAN_AMPDU_PROD] = 15, [IPA_1_1][IPA_CLIENT_A2_EMBEDDED_PROD] = 8, [IPA_1_1][IPA_CLIENT_A2_TETHERED_PROD] = 6, + [IPA_1_1][IPA_CLIENT_APPS_LAN_PROD] = -1, [IPA_1_1][IPA_CLIENT_APPS_LAN_WAN_PROD] = 2, [IPA_1_1][IPA_CLIENT_APPS_CMD_PROD] = 1, [IPA_1_1][IPA_CLIENT_ODU_PROD] = -1, @@ -133,6 +134,7 @@ static const int ep_mapping[3][IPA_CLIENT_MAX] = { [IPA_2_0][IPA_CLIENT_A5_WLAN_AMPDU_PROD] = -1, [IPA_2_0][IPA_CLIENT_A2_EMBEDDED_PROD] = -1, [IPA_2_0][IPA_CLIENT_A2_TETHERED_PROD] = -1, + [IPA_2_0][IPA_CLIENT_APPS_LAN_PROD] = -1, [IPA_2_0][IPA_CLIENT_APPS_LAN_WAN_PROD] = 4, [IPA_2_0][IPA_CLIENT_APPS_CMD_PROD] = 3, [IPA_2_0][IPA_CLIENT_ODU_PROD] = 12, @@ -207,6 +209,7 @@ static const int ep_mapping[3][IPA_CLIENT_MAX] = { [IPA_2_6L][IPA_CLIENT_A5_WLAN_AMPDU_PROD] = -1, [IPA_2_6L][IPA_CLIENT_A2_EMBEDDED_PROD] = -1, [IPA_2_6L][IPA_CLIENT_A2_TETHERED_PROD] = -1, + [IPA_2_6L][IPA_CLIENT_APPS_LAN_PROD] = -1, [IPA_2_6L][IPA_CLIENT_APPS_LAN_WAN_PROD] = 4, [IPA_2_6L][IPA_CLIENT_APPS_CMD_PROD] = 3, [IPA_2_6L][IPA_CLIENT_ODU_PROD] = -1, diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index 8a588d26e827..e90f69b466e1 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -2809,7 +2809,7 @@ static int ipa3_setup_apps_pipes(void) result = ipa_gsi_ch20_wa(); if (result) { IPAERR("ipa_gsi_ch20_wa failed %d\n", result); - goto fail_cmd; + goto fail_ch20_wa; } } @@ -2820,9 +2820,9 @@ static int ipa3_setup_apps_pipes(void) sys_in.ipa_ep_cfg.mode.mode = IPA_DMA; sys_in.ipa_ep_cfg.mode.dst = IPA_CLIENT_APPS_LAN_CONS; if (ipa3_setup_sys_pipe(&sys_in, &ipa3_ctx->clnt_hdl_cmd)) { - IPAERR(":setup sys pipe failed.\n"); + IPAERR(":setup sys pipe (APPS_CMD_PROD) failed.\n"); result = -EPERM; - goto fail_cmd; + goto fail_ch20_wa; } IPADBG("Apps to IPA cmd pipe is connected\n"); @@ -2847,32 +2847,32 @@ static int ipa3_setup_apps_pipes(void) if (ipa3_setup_flt_hash_tuple()) { IPAERR(":fail to configure flt hash tuple\n"); result = -EPERM; - goto fail_schedule_delayed_work; + goto fail_flt_hash_tuple; } IPADBG("flt hash tuple is configured\n"); if (ipa3_setup_rt_hash_tuple()) { IPAERR(":fail to configure rt hash tuple\n"); result = -EPERM; - goto fail_schedule_delayed_work; + goto fail_flt_hash_tuple; } IPADBG("rt hash tuple is configured\n"); if (ipa3_setup_exception_path()) { IPAERR(":fail to setup excp path\n"); result = -EPERM; - goto fail_schedule_delayed_work; + goto fail_flt_hash_tuple; } IPADBG("Exception path was successfully set"); if (ipa3_setup_dflt_rt_tables()) { IPAERR(":fail to setup dflt routes\n"); result = -EPERM; - goto fail_schedule_delayed_work; + goto fail_flt_hash_tuple; } IPADBG("default routing was set\n"); - /* LAN IN (IPA->A5) */ + /* LAN IN (IPA->AP) */ memset(&sys_in, 0, sizeof(struct ipa_sys_connect_params)); sys_in.client = IPA_CLIENT_APPS_LAN_CONS; sys_in.desc_fifo_sz = IPA_SYS_DESC_FIFO_SZ; @@ -2896,27 +2896,27 @@ static int ipa3_setup_apps_pipes(void) */ spin_lock_init(&ipa3_ctx->disconnect_lock); if (ipa3_setup_sys_pipe(&sys_in, &ipa3_ctx->clnt_hdl_data_in)) { - IPAERR(":setup sys pipe failed.\n"); + IPAERR(":setup sys pipe (LAN_CONS) failed.\n"); result = -EPERM; - goto fail_schedule_delayed_work; + goto fail_flt_hash_tuple; } - /* LAN-WAN OUT (AP->IPA) */ + /* LAN OUT (AP->IPA) */ memset(&sys_in, 0, sizeof(struct ipa_sys_connect_params)); - sys_in.client = IPA_CLIENT_APPS_LAN_WAN_PROD; + sys_in.client = IPA_CLIENT_APPS_LAN_PROD; sys_in.desc_fifo_sz = IPA_SYS_TX_DATA_DESC_FIFO_SZ; sys_in.ipa_ep_cfg.mode.mode = IPA_BASIC; if (ipa3_setup_sys_pipe(&sys_in, &ipa3_ctx->clnt_hdl_data_out)) { - IPAERR(":setup sys pipe failed.\n"); + IPAERR(":setup sys pipe (LAN_PROD) failed.\n"); result = -EPERM; - goto fail_data_out; + goto fail_lan_data_out; } return 0; -fail_data_out: +fail_lan_data_out: ipa3_teardown_sys_pipe(ipa3_ctx->clnt_hdl_data_in); -fail_schedule_delayed_work: +fail_flt_hash_tuple: if (ipa3_ctx->dflt_v6_rt_rule_hdl) __ipa3_del_rt_rule(ipa3_ctx->dflt_v6_rt_rule_hdl); if (ipa3_ctx->dflt_v4_rt_rule_hdl) @@ -2924,7 +2924,7 @@ fail_schedule_delayed_work: if (ipa3_ctx->excp_hdr_hdl) __ipa3_del_hdr(ipa3_ctx->excp_hdr_hdl, false); ipa3_teardown_sys_pipe(ipa3_ctx->clnt_hdl_cmd); -fail_cmd: +fail_ch20_wa: return result; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c index c2c017ac172c..5c678f1cfc28 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c @@ -62,7 +62,7 @@ #define IPA_SIZE_DL_CSUM_META_TRAILER 8 #define IPA_GSI_MAX_CH_LOW_WEIGHT 15 -#define IPA_GSI_EVT_RING_INT_MODT 3200 /* 0.1s under 32KHz clock */ +#define IPA_GSI_EVT_RING_INT_MODT (32 * 1) /* 1ms under 32KHz clock */ #define IPA_GSI_CH_20_WA_NUM_CH_TO_ALLOC 10 /* The below virtual channel cannot be used by any entity */ @@ -637,10 +637,12 @@ int ipa3_send(struct ipa3_sys_context *sys, if (i == (num_desc - 1)) { gsi_xfer_elem_array[i].flags |= GSI_XFER_FLAG_EOT; + if (sys->ep->client == IPA_CLIENT_APPS_WAN_PROD + && sys->policy == IPA_POLICY_INTR_MODE) + gsi_xfer_elem_array[i].flags |= + GSI_XFER_FLAG_BEI; gsi_xfer_elem_array[i].xfer_user_data = tx_pkt_first; - /* "mark" the last desc */ - tx_pkt->cnt = IPA_LAST_DESC_CNT; } else gsi_xfer_elem_array[i].flags |= GSI_XFER_FLAG_CHAIN; @@ -1262,37 +1264,12 @@ int ipa3_setup_sys_pipe(struct ipa_sys_connect_params *sys_in, u32 *clnt_hdl) } ep = &ipa3_ctx->ep[ipa_ep_idx]; - IPA_ACTIVE_CLIENTS_INC_EP(sys_in->client); - if (ep->valid == 1) { - if (sys_in->client != IPA_CLIENT_APPS_LAN_WAN_PROD) { - IPAERR("EP already allocated.\n"); - goto fail_and_disable_clocks; - } else { - if (ipa3_cfg_ep_hdr(ipa_ep_idx, - &sys_in->ipa_ep_cfg.hdr)) { - IPAERR("fail to configure hdr prop of EP.\n"); - result = -EFAULT; - goto fail_and_disable_clocks; - } - if (ipa3_cfg_ep_cfg(ipa_ep_idx, - &sys_in->ipa_ep_cfg.cfg)) { - IPAERR("fail to configure cfg prop of EP.\n"); - result = -EFAULT; - goto fail_and_disable_clocks; - } - IPADBG("client %d (ep: %d) overlay ok sys=%p\n", - sys_in->client, ipa_ep_idx, ep->sys); - ep->client_notify = sys_in->notify; - ep->priv = sys_in->priv; - *clnt_hdl = ipa_ep_idx; - if (!ep->keep_ipa_awake) - IPA_ACTIVE_CLIENTS_DEC_EP(sys_in->client); - - return 0; - } + IPAERR("EP %d already allocated.\n", ipa_ep_idx); + goto fail_gen; } + IPA_ACTIVE_CLIENTS_INC_EP(sys_in->client); memset(ep, 0, offsetof(struct ipa3_ep_context, sys)); if (!ep->sys) { @@ -1369,9 +1346,9 @@ int ipa3_setup_sys_pipe(struct ipa_sys_connect_params *sys_in, u32 *clnt_hdl) IPAERR("fail to configure status of EP.\n"); goto fail_gen2; } - IPADBG("ep configuration successful\n"); + IPADBG("ep %d configuration successful\n", ipa_ep_idx); } else { - IPADBG("skipping ep configuration\n"); + IPADBG("skipping ep %d configuration\n", ipa_ep_idx); } if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI) { @@ -1485,7 +1462,7 @@ int ipa3_setup_sys_pipe(struct ipa_sys_connect_params *sys_in, u32 *clnt_hdl) ipa3_ctx->skip_ep_cfg_shadow[ipa_ep_idx] = ep->skip_ep_cfg; if (!ep->skip_ep_cfg && IPA_CLIENT_IS_PROD(sys_in->client)) { if (ipa3_ctx->modem_cfg_emb_pipe_flt && - sys_in->client == IPA_CLIENT_APPS_LAN_WAN_PROD) + sys_in->client == IPA_CLIENT_APPS_WAN_PROD) IPADBG("modem cfg emb pipe flt\n"); else ipa3_install_dflt_flt_rules(ipa_ep_idx); @@ -1632,7 +1609,7 @@ int ipa3_teardown_sys_pipe(u32 clnt_hdl) if (!ep->skip_ep_cfg && IPA_CLIENT_IS_PROD(ep->client)) { if (ipa3_ctx->modem_cfg_emb_pipe_flt && - ep->client == IPA_CLIENT_APPS_LAN_WAN_PROD) + ep->client == IPA_CLIENT_APPS_WAN_PROD) IPADBG("modem cfg emb pipe flt\n"); else ipa3_delete_dflt_flt_rules(clnt_hdl); @@ -1723,6 +1700,7 @@ int ipa3_tx_dp(enum ipa_client_type dst, struct sk_buff *skb, struct ipa3_sys_context *sys; int src_ep_idx; int num_frags, f; + int data_idx; struct ipa_gsi_ep_config *gsi_ep; if (unlikely(!ipa3_ctx)) { @@ -1745,10 +1723,10 @@ int ipa3_tx_dp(enum ipa_client_type dst, struct sk_buff *skb, * */ if (IPA_CLIENT_IS_CONS(dst)) { - src_ep_idx = ipa3_get_ep_mapping(IPA_CLIENT_APPS_LAN_WAN_PROD); + src_ep_idx = ipa3_get_ep_mapping(IPA_CLIENT_APPS_LAN_PROD); if (-1 == src_ep_idx) { IPAERR("Client %u is not mapped\n", - IPA_CLIENT_APPS_LAN_WAN_PROD); + IPA_CLIENT_APPS_LAN_PROD); goto fail_gen; } dst_ep_idx = ipa3_get_ep_mapping(dst); @@ -1859,40 +1837,54 @@ int ipa3_tx_dp(enum ipa_client_type dst, struct sk_buff *skb, IPA_STATS_INC_CNT(ipa3_ctx->stats.tx_sw_pkts); } else { /* HW data path */ - desc[0].opcode = - ipahal_imm_cmd_get_opcode( - IPA_IMM_CMD_IP_PACKET_TAG_STATUS); - desc[0].type = IPA_IMM_CMD_DESC; - desc[0].callback = ipa3_tag_destroy_imm; - desc[1].pyld = skb->data; - desc[1].len = skb_headlen(skb); - desc[1].type = IPA_DATA_DESC_SKB; - desc[1].callback = ipa3_tx_comp_usr_notify_release; - desc[1].user1 = skb; - desc[1].user2 = src_ep_idx; + data_idx = 0; + if (sys->policy == IPA_POLICY_NOINTR_MODE) { + /* + * For non-interrupt mode channel (where there is no + * event ring) TAG STATUS are used for completion + * notification. IPA will generate a status packet with + * tag info as a result of the TAG STATUS command. + */ + desc[data_idx].opcode = + ipahal_imm_cmd_get_opcode( + IPA_IMM_CMD_IP_PACKET_TAG_STATUS); + desc[data_idx].type = IPA_IMM_CMD_DESC; + desc[data_idx].callback = ipa3_tag_destroy_imm; + data_idx++; + } + desc[data_idx].pyld = skb->data; + desc[data_idx].len = skb_headlen(skb); + desc[data_idx].type = IPA_DATA_DESC_SKB; + desc[data_idx].callback = ipa3_tx_comp_usr_notify_release; + desc[data_idx].user1 = skb; + desc[data_idx].user2 = src_ep_idx; if (meta && meta->dma_address_valid) { - desc[1].dma_address_valid = true; - desc[1].dma_address = meta->dma_address; + desc[data_idx].dma_address_valid = true; + desc[data_idx].dma_address = meta->dma_address; } if (num_frags == 0) { - if (ipa3_send(sys, 2, desc, true)) { + if (ipa3_send(sys, data_idx + 1, desc, true)) { IPAERR("fail to send skb %p HWP\n", skb); goto fail_mem; } } else { for (f = 0; f < num_frags; f++) { - desc[2+f].frag = &skb_shinfo(skb)->frags[f]; - desc[2+f].type = IPA_DATA_DESC_SKB_PAGED; - desc[2+f].len = skb_frag_size(desc[2+f].frag); + desc[data_idx+f+1].frag = + &skb_shinfo(skb)->frags[f]; + desc[data_idx+f+1].type = + IPA_DATA_DESC_SKB_PAGED; + desc[data_idx+f+1].len = + skb_frag_size(desc[data_idx+f+1].frag); } /* don't free skb till frag mappings are released */ - desc[2+f-1].callback = desc[1].callback; - desc[2+f-1].user1 = desc[1].user1; - desc[2+f-1].user2 = desc[1].user2; - desc[1].callback = NULL; + desc[data_idx+f].callback = desc[data_idx].callback; + desc[data_idx+f].user1 = desc[data_idx].user1; + desc[data_idx+f].user2 = desc[data_idx].user2; + desc[data_idx].callback = NULL; - if (ipa3_send(sys, num_frags + 2, desc, true)) { + if (ipa3_send(sys, num_frags + data_idx + 1, + desc, true)) { IPAERR("fail to send skb %p num_frags %u HWP\n", skb, num_frags); goto fail_mem; @@ -3234,7 +3226,8 @@ static void ipa3_free_rx_wrapper(struct ipa3_rx_pkt_wrapper *rk_pkt) static int ipa3_assign_policy(struct ipa_sys_connect_params *in, struct ipa3_sys_context *sys) { - if (in->client == IPA_CLIENT_APPS_CMD_PROD) { + if (in->client == IPA_CLIENT_APPS_CMD_PROD || + in->client == IPA_CLIENT_APPS_WAN_PROD) { sys->policy = IPA_POLICY_INTR_MODE; sys->sps_option = (SPS_O_AUTO_ENABLE | SPS_O_EOT); sys->sps_callback = ipa3_sps_irq_tx_no_aggr_notify; @@ -3665,7 +3658,7 @@ int ipa3_sys_setup(struct ipa_sys_connect_params *sys_in, IPA_ACTIVE_CLIENTS_INC_EP(sys_in->client); if (ep->valid == 1) { - if (sys_in->client != IPA_CLIENT_APPS_LAN_WAN_PROD) { + if (sys_in->client != IPA_CLIENT_APPS_WAN_PROD) { IPAERR("EP %d already allocated\n", ipa_ep_idx); goto fail_and_disable_clocks; } else { @@ -4002,7 +3995,15 @@ static int ipa_gsi_setup_channel(struct ipa_sys_connect_params *in, gsi_evt_ring_props.ring_base_vaddr; gsi_evt_ring_props.int_modt = IPA_GSI_EVT_RING_INT_MODT; - gsi_evt_ring_props.int_modc = 1; + if (ep->client == IPA_CLIENT_APPS_WAN_PROD) + gsi_evt_ring_props.int_modc = 248; + else + gsi_evt_ring_props.int_modc = 1; + + IPADBG("client=%d moderation threshold cycles=%u cnt=%u\n", + ep->client, + gsi_evt_ring_props.int_modt, + gsi_evt_ring_props.int_modc); gsi_evt_ring_props.rp_update_addr = 0; gsi_evt_ring_props.exclusive = true; gsi_evt_ring_props.err_cb = ipa_gsi_evt_ring_err_cb; @@ -4038,9 +4039,13 @@ static int ipa_gsi_setup_channel(struct ipa_sys_connect_params *in, * GSI ring length is calculated based on the desc_fifo_sz which was * meant to define the BAM desc fifo. GSI descriptors are 16B as opposed * to 8B for BAM. For PROD pipes there is also an additional descriptor - * for TAG STATUS immediate command. + * for TAG STATUS immediate command. APPS_WAN_PROD pipe is an exception + * as this pipe do not use TAG STATUS for completion. Instead it uses + * event ring based completions. */ - if (IPA_CLIENT_IS_PROD(ep->client)) + if (ep->client == IPA_CLIENT_APPS_WAN_PROD) + gsi_channel_props.ring_len = 2 * in->desc_fifo_sz; + else if (IPA_CLIENT_IS_PROD(ep->client)) gsi_channel_props.ring_len = 4 * in->desc_fifo_sz; else gsi_channel_props.ring_len = 2 * in->desc_fifo_sz; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c index df413c991a53..c3a12dd0b17c 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, 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 @@ -438,7 +438,7 @@ static bool ipa_flt_skip_pipe_config(int pipe) return true; } - if ((ipa3_get_ep_mapping(IPA_CLIENT_APPS_LAN_WAN_PROD) == pipe + if ((ipa3_get_ep_mapping(IPA_CLIENT_APPS_WAN_PROD) == pipe && ipa3_ctx->modem_cfg_emb_pipe_flt)) { IPADBG_LOW("skip %d\n", pipe); return true; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index 944c40eb03d8..9f38af1b520b 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -209,7 +209,12 @@ static const struct ipa_ep_configuration ipa3_ep_mapping [IPA_3_0][IPA_CLIENT_A5_WLAN_AMPDU_PROD] = IPA_CLIENT_NOT_USED, [IPA_3_0][IPA_CLIENT_A2_EMBEDDED_PROD] = IPA_CLIENT_NOT_USED, [IPA_3_0][IPA_CLIENT_A2_TETHERED_PROD] = IPA_CLIENT_NOT_USED, - [IPA_3_0][IPA_CLIENT_APPS_LAN_WAN_PROD] = {14, IPA_GROUP_UL, true, + [IPA_3_0][IPA_CLIENT_APPS_LAN_PROD] + = {14, IPA_GROUP_DL, false, + IPA_DPS_HPS_SEQ_TYPE_PKT_PROCESS_NO_DEC_UCP, + QMB_MASTER_SELECT_DDR}, + [IPA_3_0][IPA_CLIENT_APPS_WAN_PROD] + = {3, IPA_GROUP_UL, true, IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP, QMB_MASTER_SELECT_DDR}, [IPA_3_0][IPA_CLIENT_APPS_CMD_PROD] diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index 87743c98e0fa..6731150ce4e7 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -47,9 +47,9 @@ #define TAILROOM 0 /* for padding by mux layer */ #define MAX_NUM_OF_MUX_CHANNEL 10 /* max mux channels */ #define UL_FILTER_RULE_HANDLE_START 69 -#define DEFAULT_OUTSTANDING_HIGH_CTL 96 -#define DEFAULT_OUTSTANDING_HIGH 64 -#define DEFAULT_OUTSTANDING_LOW 32 +#define DEFAULT_OUTSTANDING_HIGH 128 +#define DEFAULT_OUTSTANDING_HIGH_CTL (DEFAULT_OUTSTANDING_HIGH+32) +#define DEFAULT_OUTSTANDING_LOW 64 #define IPA_WWAN_DEV_NAME "rmnet_ipa%d" #define IPA_UPSTEAM_WLAN_IFACE_NAME "wlan0" @@ -140,7 +140,7 @@ struct rmnet_ipa3_context { void *subsys_notify_handle; u32 apps_to_ipa3_hdl; u32 ipa3_to_apps_hdl; - struct mutex ipa_to_apps_pipe_handle_guard; + struct mutex pipe_handle_guard; }; static struct rmnet_ipa3_context *rmnet_ipa3_ctx; @@ -661,7 +661,7 @@ static int ipa3_wwan_add_ul_flt_rule_to_ipa(void) } param->commit = 1; - param->ep = IPA_CLIENT_APPS_LAN_WAN_PROD; + param->ep = IPA_CLIENT_APPS_WAN_PROD; param->global = false; param->num_rules = (uint8_t)1; @@ -700,7 +700,7 @@ static int ipa3_wwan_add_ul_flt_rule_to_ipa(void) /* send ipa_fltr_installed_notif_req_msg_v01 to Q6*/ req->source_pipe_index = - ipa3_get_ep_mapping(IPA_CLIENT_APPS_LAN_WAN_PROD); + ipa3_get_ep_mapping(IPA_CLIENT_APPS_WAN_PROD); req->install_status = QMI_RESULT_SUCCESS_V01; req->rule_id_valid = 1; req->rule_id_len = rmnet_ipa3_ctx->num_q6_rules; @@ -850,14 +850,14 @@ static int ipa3_wwan_register_to_ipa(int index) rx_ipv4_property->attrib.meta_data = rmnet_ipa3_ctx->mux_channel[index].mux_id << WWAN_METADATA_SHFT; rx_ipv4_property->attrib.meta_data_mask = WWAN_METADATA_MASK; - rx_ipv4_property->src_pipe = IPA_CLIENT_APPS_LAN_WAN_PROD; + rx_ipv4_property->src_pipe = IPA_CLIENT_APPS_WAN_PROD; rx_ipv6_property = &rx_properties.prop[1]; rx_ipv6_property->ip = IPA_IP_v6; rx_ipv6_property->attrib.attrib_mask |= IPA_FLT_META_DATA; rx_ipv6_property->attrib.meta_data = rmnet_ipa3_ctx->mux_channel[index].mux_id << WWAN_METADATA_SHFT; rx_ipv6_property->attrib.meta_data_mask = WWAN_METADATA_MASK; - rx_ipv6_property->src_pipe = IPA_CLIENT_APPS_LAN_WAN_PROD; + rx_ipv6_property->src_pipe = IPA_CLIENT_APPS_WAN_PROD; rx_properties.num_props = 2; pyld_sz = rmnet_ipa3_ctx->num_q6_rules * @@ -1139,9 +1139,10 @@ send: memset(&meta, 0, sizeof(meta)); meta.pkt_init_dst_ep_valid = true; meta.pkt_init_dst_ep_remote = true; - ret = ipa3_tx_dp(IPA_CLIENT_Q6_LAN_CONS, skb, &meta); + meta.pkt_init_dst_ep = IPA_CLIENT_Q6_LAN_CONS; + ret = ipa3_tx_dp(IPA_CLIENT_APPS_WAN_PROD, skb, &meta); } else { - ret = ipa3_tx_dp(IPA_CLIENT_APPS_LAN_WAN_PROD, skb, NULL); + ret = ipa3_tx_dp(IPA_CLIENT_APPS_WAN_PROD, skb, NULL); } if (ret) { @@ -1283,7 +1284,7 @@ static int handle3_ingress_format(struct net_device *dev, IPA_ENABLE_CS_OFFLOAD_DL; if ((in->u.data) & RMNET_IOCTL_INGRESS_FORMAT_AGG_DATA) { - IPAWANERR("get AGG size %d count %d\n", + IPAWANDBG("get AGG size %d count %d\n", in->u.ingress_format.agg_size, in->u.ingress_format.agg_count); @@ -1328,17 +1329,17 @@ static int handle3_ingress_format(struct net_device *dev, ipa_wan_ep_cfg->desc_fifo_sz = ipa3_rmnet_res.wan_rx_desc_size * sizeof(struct sps_iovec); - mutex_lock(&rmnet_ipa3_ctx->ipa_to_apps_pipe_handle_guard); + mutex_lock(&rmnet_ipa3_ctx->pipe_handle_guard); if (atomic_read(&rmnet_ipa3_ctx->is_ssr)) { IPAWANDBG("In SSR sequence/recovery\n"); - mutex_unlock(&rmnet_ipa3_ctx->ipa_to_apps_pipe_handle_guard); + mutex_unlock(&rmnet_ipa3_ctx->pipe_handle_guard); return -EFAULT; } ret = ipa3_setup_sys_pipe(&rmnet_ipa3_ctx->ipa_to_apps_ep_cfg, &rmnet_ipa3_ctx->ipa3_to_apps_hdl); - mutex_unlock(&rmnet_ipa3_ctx->ipa_to_apps_pipe_handle_guard); + mutex_unlock(&rmnet_ipa3_ctx->pipe_handle_guard); if (ret) IPAWANERR("failed to configure ingress\n"); @@ -1347,6 +1348,104 @@ static int handle3_ingress_format(struct net_device *dev, } /** + * handle3_egress_format() - Egress data format configuration + * + * Setup IPA egress system pipe and Configure: + * header handling, checksum, de-aggregation and fifo size + * + * @dev: network device + * @e: egress configuration + */ +static int handle3_egress_format(struct net_device *dev, + struct rmnet_ioctl_extended_s *e) +{ + int rc; + struct ipa_sys_connect_params *ipa_wan_ep_cfg; + + IPAWANDBG("get RMNET_IOCTL_SET_EGRESS_DATA_FORMAT\n"); + ipa_wan_ep_cfg = &rmnet_ipa3_ctx->apps_to_ipa_ep_cfg; + if ((e->u.data) & RMNET_IOCTL_EGRESS_FORMAT_CHECKSUM) { + ipa_wan_ep_cfg->ipa_ep_cfg.hdr.hdr_len = 8; + ipa_wan_ep_cfg->ipa_ep_cfg.cfg.cs_offload_en = + IPA_ENABLE_CS_OFFLOAD_UL; + ipa_wan_ep_cfg->ipa_ep_cfg.cfg.cs_metadata_hdr_offset = 1; + } else { + ipa_wan_ep_cfg->ipa_ep_cfg.hdr.hdr_len = 4; + } + + if ((e->u.data) & RMNET_IOCTL_EGRESS_FORMAT_AGGREGATION) { + IPAWANERR("WAN UL Aggregation not supported!!\n"); + WARN_ON(1); + return -EINVAL; + ipa_wan_ep_cfg->ipa_ep_cfg.aggr.aggr_en = IPA_ENABLE_DEAGGR; + ipa_wan_ep_cfg->ipa_ep_cfg.aggr.aggr = IPA_QCMAP; + + ipa_wan_ep_cfg->ipa_ep_cfg.deaggr.packet_offset_valid = false; + + ipa_wan_ep_cfg->ipa_ep_cfg.hdr.hdr_ofst_pkt_size = 2; + + ipa_wan_ep_cfg->ipa_ep_cfg.hdr_ext.hdr_total_len_or_pad_valid = + true; + ipa_wan_ep_cfg->ipa_ep_cfg.hdr_ext.hdr_total_len_or_pad = + IPA_HDR_PAD; + ipa_wan_ep_cfg->ipa_ep_cfg.hdr_ext.hdr_pad_to_alignment = + 2; + ipa_wan_ep_cfg->ipa_ep_cfg.hdr_ext.hdr_payload_len_inc_padding = + true; + ipa_wan_ep_cfg->ipa_ep_cfg.hdr_ext.hdr_total_len_or_pad_offset = + 0; + ipa_wan_ep_cfg->ipa_ep_cfg.hdr_ext.hdr_little_endian = + false; + } else { + IPAWANDBG("WAN UL Aggregation disabled\n"); + ipa_wan_ep_cfg->ipa_ep_cfg.aggr.aggr_en = IPA_BYPASS_AGGR; + } + + ipa_wan_ep_cfg->ipa_ep_cfg.hdr.hdr_ofst_metadata_valid = 1; + /* modem want offset at 0! */ + ipa_wan_ep_cfg->ipa_ep_cfg.hdr.hdr_ofst_metadata = 0; + + ipa_wan_ep_cfg->ipa_ep_cfg.mode.dst = IPA_CLIENT_APPS_WAN_PROD; + ipa_wan_ep_cfg->ipa_ep_cfg.mode.mode = IPA_BASIC; + + ipa_wan_ep_cfg->client = IPA_CLIENT_APPS_WAN_PROD; + ipa_wan_ep_cfg->notify = apps_ipa_tx_complete_notify; + ipa_wan_ep_cfg->desc_fifo_sz = IPA_SYS_TX_DATA_DESC_FIFO_SZ; + ipa_wan_ep_cfg->priv = dev; + + mutex_lock(&rmnet_ipa3_ctx->pipe_handle_guard); + if (atomic_read(&rmnet_ipa3_ctx->is_ssr)) { + IPAWANDBG("In SSR sequence/recovery\n"); + mutex_unlock(&rmnet_ipa3_ctx->pipe_handle_guard); + return -EFAULT; + } + rc = ipa3_setup_sys_pipe( + ipa_wan_ep_cfg, &rmnet_ipa3_ctx->apps_to_ipa3_hdl); + if (rc) { + IPAWANERR("failed to config egress endpoint\n"); + mutex_unlock(&rmnet_ipa3_ctx->pipe_handle_guard); + return rc; + } + mutex_unlock(&rmnet_ipa3_ctx->pipe_handle_guard); + + if (rmnet_ipa3_ctx->num_q6_rules != 0) { + /* already got Q6 UL filter rules*/ + if (ipa3_qmi_ctx->modem_cfg_emb_pipe_flt == false) + rc = ipa3_wwan_add_ul_flt_rule_to_ipa(); + if (rc) + IPAWANERR("install UL rules failed\n"); + else + rmnet_ipa3_ctx->a7_ul_flt_set = true; + } else { + /* wait Q6 UL filter rules*/ + IPAWANDBG("no UL-rules\n"); + } + rmnet_ipa3_ctx->egress_set = true; + + return rc; +} + +/** * ipa3_wwan_ioctl() - I/O control for wwan network driver. * * @dev: network device @@ -1507,7 +1606,7 @@ static int ipa3_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) case RMNET_IOCTL_GET_EP_PAIR: IPAWANDBG("get ioctl: RMNET_IOCTL_GET_EP_PAIR\n"); extend_ioctl_data.u.ipa_ep_pair.consumer_pipe_num = - ipa3_get_ep_mapping(IPA_CLIENT_APPS_LAN_WAN_PROD); + ipa3_get_ep_mapping(IPA_CLIENT_APPS_WAN_PROD); extend_ioctl_data.u.ipa_ep_pair.producer_pipe_num = ipa3_get_ep_mapping(IPA_CLIENT_APPS_WAN_CONS); if (copy_to_user((u8 *)ifr->ifr_ifru.ifru_data, @@ -1593,72 +1692,7 @@ static int ipa3_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) rmnet_ipa3_ctx->rmnet_index++; break; case RMNET_IOCTL_SET_EGRESS_DATA_FORMAT: - IPAWANDBG("get RMNET_IOCTL_SET_EGRESS_DATA_FORMAT\n"); - if ((extend_ioctl_data.u.data) & - RMNET_IOCTL_EGRESS_FORMAT_CHECKSUM) { - rmnet_ipa3_ctx->apps_to_ipa_ep_cfg. - ipa_ep_cfg.hdr.hdr_len = 8; - rmnet_ipa3_ctx->apps_to_ipa_ep_cfg. - ipa_ep_cfg.cfg.cs_offload_en = - IPA_ENABLE_CS_OFFLOAD_UL; - rmnet_ipa3_ctx->apps_to_ipa_ep_cfg. - ipa_ep_cfg.cfg.cs_metadata_hdr_offset - = 1; - } else { - rmnet_ipa3_ctx->apps_to_ipa_ep_cfg. - ipa_ep_cfg.hdr.hdr_len = 4; - } - if ((extend_ioctl_data.u.data) & - RMNET_IOCTL_EGRESS_FORMAT_AGGREGATION) - rmnet_ipa3_ctx->apps_to_ipa_ep_cfg. - ipa_ep_cfg.aggr.aggr_en = - IPA_ENABLE_AGGR; - else - rmnet_ipa3_ctx->apps_to_ipa_ep_cfg. - ipa_ep_cfg.aggr.aggr_en = - IPA_BYPASS_AGGR; - rmnet_ipa3_ctx->apps_to_ipa_ep_cfg.ipa_ep_cfg.hdr. - hdr_ofst_metadata_valid = 1; - /* modem want offset at 0! */ - rmnet_ipa3_ctx->apps_to_ipa_ep_cfg.ipa_ep_cfg.hdr. - hdr_ofst_metadata = 0; - rmnet_ipa3_ctx->apps_to_ipa_ep_cfg.ipa_ep_cfg.mode. - dst = IPA_CLIENT_APPS_LAN_WAN_PROD; - rmnet_ipa3_ctx->apps_to_ipa_ep_cfg.ipa_ep_cfg.mode. - mode = IPA_BASIC; - - rmnet_ipa3_ctx->apps_to_ipa_ep_cfg.client = - IPA_CLIENT_APPS_LAN_WAN_PROD; - rmnet_ipa3_ctx->apps_to_ipa_ep_cfg.notify = - apps_ipa_tx_complete_notify; - rmnet_ipa3_ctx->apps_to_ipa_ep_cfg.desc_fifo_sz = - IPA_SYS_TX_DATA_DESC_FIFO_SZ; - rmnet_ipa3_ctx->apps_to_ipa_ep_cfg.priv = dev; - - rc = ipa3_setup_sys_pipe( - &rmnet_ipa3_ctx->apps_to_ipa_ep_cfg, - &rmnet_ipa3_ctx->apps_to_ipa3_hdl); - if (rc) - IPAWANERR("failed to config egress endpoint\n"); - - if (rmnet_ipa3_ctx->num_q6_rules != 0) { - /* already got Q6 UL filter rules*/ - if (ipa3_qmi_ctx->modem_cfg_emb_pipe_flt - == false) - rc = ipa3_wwan_add_ul_flt_rule_to_ipa(); - else - rc = 0; - rmnet_ipa3_ctx->egress_set = true; - if (rc) - IPAWANERR("install UL rules failed\n"); - else - rmnet_ipa3_ctx->a7_ul_flt_set = true; - } else { - /* wait Q6 UL filter rules*/ - rmnet_ipa3_ctx->egress_set = true; - IPAWANDBG("no UL-rules, egress_set(%d)\n", - rmnet_ipa3_ctx->egress_set); - } + rc = handle3_egress_format(dev, &extend_ioctl_data); break; case RMNET_IOCTL_SET_INGRESS_DATA_FORMAT:/* Set IDF */ rc = handle3_ingress_format(dev, &extend_ioctl_data); @@ -2263,15 +2297,20 @@ static int ipa3_wwan_remove(struct platform_device *pdev) int ret; pr_info("rmnet_ipa started deinitialization\n"); - mutex_lock(&rmnet_ipa3_ctx->ipa_to_apps_pipe_handle_guard); + mutex_lock(&rmnet_ipa3_ctx->pipe_handle_guard); ret = ipa3_teardown_sys_pipe(rmnet_ipa3_ctx->ipa3_to_apps_hdl); if (ret < 0) IPAWANERR("Failed to teardown IPA->APPS pipe\n"); else rmnet_ipa3_ctx->ipa3_to_apps_hdl = -1; + ret = ipa3_teardown_sys_pipe(rmnet_ipa3_ctx->apps_to_ipa3_hdl); + if (ret < 0) + IPAWANERR("Failed to teardown APPS->IPA pipe\n"); + else + rmnet_ipa3_ctx->apps_to_ipa3_hdl = -1; if (ipa3_rmnet_res.ipa_napi_enable) netif_napi_del(&(rmnet_ipa3_ctx->wwan_priv->napi)); - mutex_unlock(&rmnet_ipa3_ctx->ipa_to_apps_pipe_handle_guard); + mutex_unlock(&rmnet_ipa3_ctx->pipe_handle_guard); unregister_netdev(IPA_NETDEV()); ret = ipa_rm_delete_dependency(IPA_RM_RESOURCE_WWAN_0_PROD, IPA_RM_RESOURCE_Q6_CONS); @@ -3160,8 +3199,9 @@ static int __init ipa3_wwan_init(void) atomic_set(&rmnet_ipa3_ctx->is_initialized, 0); atomic_set(&rmnet_ipa3_ctx->is_ssr, 0); - mutex_init(&rmnet_ipa3_ctx->ipa_to_apps_pipe_handle_guard); + mutex_init(&rmnet_ipa3_ctx->pipe_handle_guard); rmnet_ipa3_ctx->ipa3_to_apps_hdl = -1; + rmnet_ipa3_ctx->apps_to_ipa3_hdl = -1; /* Register for Modem SSR */ rmnet_ipa3_ctx->subsys_notify_handle = subsys_notif_register_notifier( SUBSYS_MODEM, @@ -3175,7 +3215,7 @@ static int __init ipa3_wwan_init(void) static void __exit ipa3_wwan_cleanup(void) { int ret; - mutex_destroy(&rmnet_ipa3_ctx->ipa_to_apps_pipe_handle_guard); + mutex_destroy(&rmnet_ipa3_ctx->pipe_handle_guard); ret = subsys_notif_unregister_notifier( rmnet_ipa3_ctx->subsys_notify_handle, &ipa3_ssr_notifier); if (ret) diff --git a/drivers/power/supply/qcom/qpnp-fg.c b/drivers/power/supply/qcom/qpnp-fg.c index e4a8ade80d4f..cfd2f64a9bb8 100644 --- a/drivers/power/supply/qcom/qpnp-fg.c +++ b/drivers/power/supply/qcom/qpnp-fg.c @@ -549,6 +549,7 @@ struct fg_trans { struct fg_chip *chip; struct fg_log_buffer *log; /* log buffer */ u8 *data; /* fg data that is read */ + struct mutex memif_dfs_lock; /* Prevent thread concurrency */ }; struct fg_dbgfs { @@ -5730,6 +5731,7 @@ static int fg_memif_data_open(struct inode *inode, struct file *file) trans->addr = dbgfs_data.addr; trans->chip = dbgfs_data.chip; trans->offset = trans->addr; + mutex_init(&trans->memif_dfs_lock); file->private_data = trans; return 0; @@ -5741,6 +5743,7 @@ static int fg_memif_dfs_close(struct inode *inode, struct file *file) if (trans && trans->log && trans->data) { file->private_data = NULL; + mutex_destroy(&trans->memif_dfs_lock); kfree(trans->log); kfree(trans->data); kfree(trans); @@ -5898,10 +5901,13 @@ static ssize_t fg_memif_dfs_reg_read(struct file *file, char __user *buf, size_t ret; size_t len; + mutex_lock(&trans->memif_dfs_lock); /* Is the the log buffer empty */ if (log->rpos >= log->wpos) { - if (get_log_data(trans) <= 0) - return 0; + if (get_log_data(trans) <= 0) { + len = 0; + goto unlock_mutex; + } } len = min(count, log->wpos - log->rpos); @@ -5909,7 +5915,8 @@ static ssize_t fg_memif_dfs_reg_read(struct file *file, char __user *buf, ret = copy_to_user(buf, &log->data[log->rpos], len); if (ret == len) { pr_err("error copy sram register values to user\n"); - return -EFAULT; + len = -EFAULT; + goto unlock_mutex; } /* 'ret' is the number of bytes not copied */ @@ -5917,6 +5924,9 @@ static ssize_t fg_memif_dfs_reg_read(struct file *file, char __user *buf, *ppos += len; log->rpos += len; + +unlock_mutex: + mutex_unlock(&trans->memif_dfs_lock); return len; } @@ -5937,14 +5947,20 @@ static ssize_t fg_memif_dfs_reg_write(struct file *file, const char __user *buf, int cnt = 0; u8 *values; size_t ret = 0; + char *kbuf; + u32 offset; struct fg_trans *trans = file->private_data; - u32 offset = trans->offset; + + mutex_lock(&trans->memif_dfs_lock); + offset = trans->offset; /* Make a copy of the user data */ - char *kbuf = kmalloc(count + 1, GFP_KERNEL); - if (!kbuf) - return -ENOMEM; + kbuf = kmalloc(count + 1, GFP_KERNEL); + if (!kbuf) { + ret = -ENOMEM; + goto unlock_mutex; + } ret = copy_from_user(kbuf, buf, count); if (ret == count) { @@ -5995,6 +6011,8 @@ static ssize_t fg_memif_dfs_reg_write(struct file *file, const char __user *buf, free_buf: kfree(kbuf); +unlock_mutex: + mutex_unlock(&trans->memif_dfs_lock); return ret; } diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c index 244ed77c4318..b51ce758a16b 100644 --- a/drivers/power/supply/qcom/qpnp-smb2.c +++ b/drivers/power/supply/qcom/qpnp-smb2.c @@ -839,6 +839,7 @@ static enum power_supply_property smb2_batt_props[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_MAX, POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED, @@ -908,6 +909,10 @@ static int smb2_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_CURRENT_QNOVO: val->intval = chg->qnovo_fcc_ua; break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + val->intval = get_client_vote(chg->fcc_votable, + DEFAULT_VOTER); + break; case POWER_SUPPLY_PROP_TEMP: rc = smblib_get_prop_batt_temp(chg, val); break; @@ -972,6 +977,9 @@ static int smb2_batt_set_prop(struct power_supply *psy, chg->qnovo_fcc_ua = val->intval; rc = rerun_election(chg->fcc_votable); break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + vote(chg->fcc_votable, DEFAULT_VOTER, true, val->intval); + break; case POWER_SUPPLY_PROP_SET_SHIP_MODE: /* Not in ship mode as long as the device is active */ if (!val->intval) @@ -1382,7 +1390,8 @@ static int smb2_init_hw(struct smb2 *chip) } /* votes must be cast before configuring software control */ - vote(chg->usb_suspend_votable, + /* vote 0mA on usb_icl for non battery platforms */ + vote(chg->usb_icl_votable, DEFAULT_VOTER, chip->dt.no_battery, 0); vote(chg->dc_suspend_votable, DEFAULT_VOTER, chip->dt.no_battery, 0); diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index 143c6f740c46..33339588599e 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -629,7 +629,7 @@ static void smblib_uusb_removal(struct smb_charger *chg) /* reset both usbin current and voltage votes */ vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0); vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0); - vote(chg->pl_disable_votable, PL_DISABLE_HVDCP_VOTER, true, 0); + vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, true, 0); cancel_delayed_work_sync(&chg->hvdcp_detect_work); @@ -698,7 +698,7 @@ void smblib_suspend_on_debug_battery(struct smb_charger *chg) return; } - vote(chg->usb_suspend_votable, DEBUG_BOARD_VOTER, val.intval, 0); + vote(chg->usb_icl_votable, DEBUG_BOARD_VOTER, val.intval, 0); vote(chg->dc_suspend_votable, DEBUG_BOARD_VOTER, val.intval, 0); if (val.intval) pr_info("Input suspended: Fake battery\n"); @@ -741,18 +741,6 @@ int smblib_rerun_apsd_if_required(struct smb_charger *chg) * VOTABLE CALLBACKS * *********************/ -static int smblib_usb_suspend_vote_callback(struct votable *votable, void *data, - int suspend, const char *client) -{ - struct smb_charger *chg = data; - - /* resume input if suspend is invalid */ - if (suspend < 0) - suspend = 0; - - return smblib_set_usb_suspend(chg, (bool)suspend); -} - static int smblib_dc_suspend_vote_callback(struct votable *votable, void *data, int suspend, const char *client) { @@ -770,44 +758,12 @@ static int smblib_dc_suspend_vote_callback(struct votable *votable, void *data, #define USBIN_150MA 150000 #define USBIN_500MA 500000 #define USBIN_900MA 900000 -static int smblib_usb_icl_vote_callback(struct votable *votable, void *data, - int icl_ua, const char *client) -{ - struct smb_charger *chg = data; - int rc = 0; - bool suspend, override; - u8 icl_options = 0; - - override = true; - /* remove override if no voters or type = SDP or CDP */ - if (client == NULL - || chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB - || chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB_CDP) - override = false; - suspend = false; - if (client && (icl_ua < USBIN_25MA)) - suspend = true; - - if (suspend) - goto out; - - if (chg->usb_psy_desc.type != POWER_SUPPLY_TYPE_USB) { - if (client) { - rc = smblib_set_charge_param(chg, &chg->param.usb_icl, - icl_ua - chg->icl_reduction_ua); - if (rc < 0) { - smblib_err(chg, "Couldn't set HC ICL rc=%d\n", - rc); - return rc; - } - } - smblib_dbg(chg, PR_PARALLEL, - "icl_ua=%d icl_reduction=%d\n", - icl_ua, chg->icl_reduction_ua); - goto out; - } +static int set_sdp_current(struct smb_charger *chg, int icl_ua) +{ + int rc; + u8 icl_options; /* power source is SDP */ switch (icl_ua) { @@ -829,35 +785,97 @@ static int smblib_usb_icl_vote_callback(struct votable *votable, void *data, break; default: smblib_err(chg, "ICL %duA isn't supported for SDP\n", icl_ua); - icl_options = 0; - break; + return -EINVAL; } -out: - if (override) - icl_options |= USBIN_MODE_CHG_BIT; - rc = smblib_masked_write(chg, USBIN_ICL_OPTIONS_REG, - CFG_USB3P0_SEL_BIT | USB51_MODE_BIT | USBIN_MODE_CHG_BIT, - icl_options); + CFG_USB3P0_SEL_BIT | USB51_MODE_BIT, icl_options); if (rc < 0) { smblib_err(chg, "Couldn't set ICL options rc=%d\n", rc); return rc; } - rc = vote(chg->usb_suspend_votable, PD_VOTER, suspend, 0); + return rc; +} + +static int smblib_usb_icl_vote_callback(struct votable *votable, void *data, + int icl_ua, const char *client) +{ + struct smb_charger *chg = data; + int rc = 0; + bool override; + union power_supply_propval pval; + + /* suspend and return if 25mA or less is requested */ + if (client && (icl_ua < USBIN_25MA)) + return smblib_set_usb_suspend(chg, true); + + disable_irq_nosync(chg->irq_info[USBIN_ICL_CHANGE_IRQ].irq); + if (!client) + goto override_suspend_config; + + rc = smblib_get_prop_typec_mode(chg, &pval); if (rc < 0) { - smblib_err(chg, "Couldn't %s input rc=%d\n", - suspend ? "suspend" : "resume", rc); - return rc; + smblib_err(chg, "Couldn't get typeC mode rc = %d\n", rc); + goto enable_icl_changed_interrupt; + } + + /* configure current */ + if (pval.intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT + && (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB)) { + rc = set_sdp_current(chg, icl_ua); + if (rc < 0) { + smblib_err(chg, "Couldn't set SDP ICL rc=%d\n", rc); + goto enable_icl_changed_interrupt; + } + } else { + rc = smblib_set_charge_param(chg, &chg->param.usb_icl, + icl_ua - chg->icl_reduction_ua); + if (rc < 0) { + smblib_err(chg, "Couldn't set HC ICL rc=%d\n", rc); + goto enable_icl_changed_interrupt; + } + } + +override_suspend_config: + /* determine if override needs to be enforced */ + override = true; + if (client == NULL) { + /* remove override if no voters - hw defaults is desired */ + override = false; + } else if (pval.intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) { + if (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB) + /* For std cable with type = SDP never override */ + override = false; + else if (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB_CDP + && icl_ua - chg->icl_reduction_ua == 1500000) + /* + * For std cable with type = CDP override only if + * current is not 1500mA + */ + override = false; } + /* enforce override */ + rc = smblib_masked_write(chg, USBIN_ICL_OPTIONS_REG, + USBIN_MODE_CHG_BIT, override ? USBIN_MODE_CHG_BIT : 0); + rc = smblib_icl_override(chg, override); if (rc < 0) { smblib_err(chg, "Couldn't set ICL override rc=%d\n", rc); - return rc; + goto enable_icl_changed_interrupt; } + /* unsuspend after configuring current and override */ + rc = smblib_set_usb_suspend(chg, false); + if (rc < 0) { + smblib_err(chg, "Couldn't resume input rc=%d\n", rc); + goto enable_icl_changed_interrupt; + } + +enable_icl_changed_interrupt: + enable_irq(chg->irq_info[USBIN_ICL_CHANGE_IRQ].irq); + return rc; } @@ -1269,8 +1287,9 @@ int smblib_vbus_regulator_is_enabled(struct regulator_dev *rdev) int smblib_get_prop_input_suspend(struct smb_charger *chg, union power_supply_propval *val) { - val->intval = get_client_vote(chg->usb_suspend_votable, USER_VOTER) && - get_client_vote(chg->dc_suspend_votable, USER_VOTER); + val->intval + = (get_client_vote(chg->usb_icl_votable, USER_VOTER) == 0) + && get_client_vote(chg->dc_suspend_votable, USER_VOTER); return 0; } @@ -1567,7 +1586,8 @@ int smblib_set_prop_input_suspend(struct smb_charger *chg, { int rc; - rc = vote(chg->usb_suspend_votable, USER_VOTER, (bool)val->intval, 0); + /* vote 0mA when suspended */ + rc = vote(chg->usb_icl_votable, USER_VOTER, (bool)val->intval, 0); if (rc < 0) { smblib_err(chg, "Couldn't vote to %s USB rc=%d\n", (bool)val->intval ? "suspend" : "resume", rc); @@ -1713,7 +1733,7 @@ int smblib_get_prop_usb_online(struct smb_charger *chg, int rc = 0; u8 stat; - if (get_client_vote(chg->usb_suspend_votable, USER_VOTER)) { + if (get_client_vote(chg->usb_icl_votable, USER_VOTER) == 0) { val->intval = false; return rc; } @@ -2288,6 +2308,16 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, smblib_err(chg, "Couldn't unvote USB_PSY rc=%d\n", rc); return rc; } + + /* pd active set, parallel charger can be enabled now */ + rc = vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, + false, 0); + if (rc < 0) { + smblib_err(chg, + "Couldn't unvote PL_DELAY_HVDCP_VOTER rc=%d\n", + rc); + return rc; + } } /* CC pin selection s/w override in PD session; h/w otherwise. */ @@ -2856,8 +2886,7 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) } } else { if (chg->wa_flags & BOOST_BACK_WA) - vote(chg->usb_suspend_votable, - BOOST_BACK_VOTER, false, 0); + vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0); if (chg->dpdm_reg && regulator_is_enabled(chg->dpdm_reg)) { smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n"); @@ -3003,7 +3032,7 @@ static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg, vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, true, 0); /* QC authentication done, parallel charger can be enabled now */ - vote(chg->pl_disable_votable, PL_DISABLE_HVDCP_VOTER, false, 0); + vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, false, 0); /* the APSD done handler will set the USB supply type */ apsd_result = smblib_get_apsd_result(chg); @@ -3039,7 +3068,7 @@ static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg, * to complete. */ if (!qc_charger) - vote(chg->pl_disable_votable, PL_DISABLE_HVDCP_VOTER, + vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, false, 0); } @@ -3077,9 +3106,13 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) true); case OCP_CHARGER_BIT: case FLOAT_CHARGER_BIT: - /* if not DCP then no hvdcp timeout happens. Enable pd here */ + /* + * if not DCP then no hvdcp timeout happens. Enable + * pd/parallel here. + */ vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, false, 0); + vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, false, 0); break; case DCP_CHARGER_BIT: if (chg->wa_flags & QC_CHARGER_DETECTION_WA_BIT) @@ -3131,6 +3164,13 @@ irqreturn_t smblib_handle_usb_source_change(int irq, void *data) power_supply_changed(chg->usb_psy); + rc = smblib_read(chg, APSD_STATUS_REG, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read APSD_STATUS rc=%d\n", rc); + return IRQ_HANDLED; + } + smblib_dbg(chg, PR_REGISTER, "APSD_STATUS = 0x%02x\n", stat); + return IRQ_HANDLED; } @@ -3211,7 +3251,7 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, true, 0); vote(chg->pd_disallowed_votable_indirect, LEGACY_CABLE_VOTER, true, 0); vote(chg->pd_disallowed_votable_indirect, VBUS_CC_SHORT_VOTER, true, 0); - vote(chg->pl_disable_votable, PL_DISABLE_HVDCP_VOTER, true, 0); + vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, true, 0); /* reset votes from vbus_cc_short */ vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, @@ -3226,12 +3266,13 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) chg->vconn_attempts = 0; chg->otg_attempts = 0; - typec_source_removal(chg); - typec_sink_removal(chg); chg->usb_ever_removed = true; smblib_update_usb_type(chg); + + typec_source_removal(chg); + typec_sink_removal(chg); } static void smblib_handle_typec_insertion(struct smb_charger *chg, @@ -3414,15 +3455,15 @@ irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data) } if ((stat & USE_USBIN_BIT) && - get_effective_result(chg->usb_suspend_votable)) + get_effective_result(chg->usb_icl_votable) < USBIN_25MA) return IRQ_HANDLED; if (stat & USE_DCIN_BIT) return IRQ_HANDLED; if (is_storming(&irq_data->storm_data)) { - smblib_err(chg, "Reverse boost detected: suspending input\n"); - vote(chg->usb_suspend_votable, BOOST_BACK_VOTER, true, 0); + smblib_err(chg, "Reverse boost detected: voting 0mA to suspend input\n"); + vote(chg->usb_icl_votable, BOOST_BACK_VOTER, true, 0); } return IRQ_HANDLED; @@ -3769,15 +3810,7 @@ static int smblib_create_votables(struct smb_charger *chg) return rc; } vote(chg->pl_disable_votable, PL_INDIRECT_VOTER, true, 0); - vote(chg->pl_disable_votable, PL_DISABLE_HVDCP_VOTER, true, 0); - - chg->usb_suspend_votable = create_votable("USB_SUSPEND", VOTE_SET_ANY, - smblib_usb_suspend_vote_callback, - chg); - if (IS_ERR(chg->usb_suspend_votable)) { - rc = PTR_ERR(chg->usb_suspend_votable); - return rc; - } + vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, true, 0); chg->dc_suspend_votable = create_votable("DC_SUSPEND", VOTE_SET_ANY, smblib_dc_suspend_vote_callback, @@ -3876,8 +3909,6 @@ static int smblib_create_votables(struct smb_charger *chg) static void smblib_destroy_votables(struct smb_charger *chg) { - if (chg->usb_suspend_votable) - destroy_votable(chg->usb_suspend_votable); if (chg->dc_suspend_votable) destroy_votable(chg->dc_suspend_votable); if (chg->usb_icl_votable) diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h index 2a1f0d8e0eb3..b2cfeeda5792 100644 --- a/drivers/power/supply/qcom/smb-lib.h +++ b/drivers/power/supply/qcom/smb-lib.h @@ -54,7 +54,7 @@ enum print_reason { #define MICRO_USB_VOTER "MICRO_USB_VOTER" #define DEBUG_BOARD_VOTER "DEBUG_BOARD_VOTER" #define PD_SUSPEND_SUPPORTED_VOTER "PD_SUSPEND_SUPPORTED_VOTER" -#define PL_DISABLE_HVDCP_VOTER "PL_DISABLE_HVDCP_VOTER" +#define PL_DELAY_HVDCP_VOTER "PL_DELAY_HVDCP_VOTER" #define CTM_VOTER "CTM_VOTER" #define VCONN_MAX_ATTEMPTS 3 @@ -252,7 +252,6 @@ struct smb_charger { struct regulator *dpdm_reg; /* votables */ - struct votable *usb_suspend_votable; struct votable *dc_suspend_votable; struct votable *fcc_votable; struct votable *fv_votable; diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c index 554625adda7d..4180edc89a4c 100644 --- a/drivers/power/supply/qcom/smb138x-charger.c +++ b/drivers/power/supply/qcom/smb138x-charger.c @@ -155,7 +155,7 @@ static int smb138x_parse_dt(struct smb138x *chip) rc = of_property_read_u32(node, "qcom,connector-temp-max-mdegc", - &chip->dt.chg_temp_max_mdegc); + &chip->dt.connector_temp_max_mdegc); if (rc < 0) chip->dt.connector_temp_max_mdegc = 105000; @@ -827,8 +827,6 @@ static int smb138x_init_hw(struct smb138x *chip) int rc = 0; /* votes must be cast before configuring software control */ - vote(chg->usb_suspend_votable, - DEFAULT_VOTER, chip->dt.suspend_input, 0); vote(chg->dc_suspend_votable, DEFAULT_VOTER, chip->dt.suspend_input, 0); vote(chg->fcc_votable, diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c index c51ed182ba96..3ffe094fe53c 100644 --- a/drivers/regulator/cpr3-regulator.c +++ b/drivers/regulator/cpr3-regulator.c @@ -174,6 +174,7 @@ #define CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN BIT(27) #define CPR4_REG_MISC 0x700 +#define CPR4_MISC_RESET_STEP_QUOT_LOOP_EN BIT(2) #define CPR4_MISC_MARGIN_TABLE_ROW_SELECT_MASK GENMASK(23, 20) #define CPR4_MISC_MARGIN_TABLE_ROW_SELECT_SHIFT 20 #define CPR4_MISC_TEMP_SENSOR_ID_START_MASK GENMASK(27, 24) @@ -698,6 +699,11 @@ static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl) int thread_id = 0; u64 temp; + if (ctrl->reset_step_quot_loop_en) + cpr3_masked_write(ctrl, CPR4_REG_MISC, + CPR4_MISC_RESET_STEP_QUOT_LOOP_EN, + CPR4_MISC_RESET_STEP_QUOT_LOOP_EN); + if (ctrl->supports_hw_closed_loop) { if (ctrl->saw_use_unit_mV) pmic_step_size = ctrl->step_volt / 1000; @@ -1310,6 +1316,11 @@ static int cpr3_regulator_init_cprh(struct cpr3_controller *ctrl) return rc; } + if (ctrl->reset_step_quot_loop_en) + cpr3_masked_write(ctrl, CPR4_REG_MISC, + CPR4_MISC_RESET_STEP_QUOT_LOOP_EN, + CPR4_MISC_RESET_STEP_QUOT_LOOP_EN); + if (ctrl->saw_use_unit_mV) pmic_step_size = ctrl->step_volt / 1000; cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, diff --git a/drivers/regulator/cpr3-regulator.h b/drivers/regulator/cpr3-regulator.h index 7dae23ca0e70..1ac0f7b816b3 100644 --- a/drivers/regulator/cpr3-regulator.h +++ b/drivers/regulator/cpr3-regulator.h @@ -735,6 +735,12 @@ struct cpr3_panic_regs_info { * @panic_notifier: Notifier block registered to global panic notifier list. * @support_ldo300_vreg: Boolean value which indicates that this CPR controller * manages an underlying LDO regulator of type LDO300. + * @reset_step_quot_loop_en: Boolean value which indicates that this CPR + * controller should be configured to reset step_quot on + * each loop_en = 0 transition. This configuration allows + * the CPR controller to first use the default step_quot + * and then later switch to the run-time calibrated + * step_quot. * * This structure contains both configuration and runtime state data. The * elements cpr_allowed_sw, use_hw_closed_loop, aggr_corner, cpr_enabled, @@ -836,6 +842,7 @@ struct cpr3_controller { struct cpr3_panic_regs_info *panic_regs_info; struct notifier_block panic_notifier; bool support_ldo300_vreg; + bool reset_step_quot_loop_en; }; /* Used for rounding voltages to the closest physically available set point. */ diff --git a/drivers/regulator/cpr3-util.c b/drivers/regulator/cpr3-util.c index 0a1a1c56bd16..fcbaf9f5f51f 100644 --- a/drivers/regulator/cpr3-util.c +++ b/drivers/regulator/cpr3-util.c @@ -1221,6 +1221,14 @@ int cpr3_parse_common_ctrl_data(struct cpr3_controller *ctrl) } /* + * Reset step_quot to default on each loop_en = 0 transition is + * optional. + */ + ctrl->reset_step_quot_loop_en + = of_property_read_bool(ctrl->dev->of_node, + "qcom,cpr-reset-step-quot-loop-en"); + + /* * Regulator device handles are not necessary for CPRh controllers * since communication with the regulators is completely managed * in hardware. diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index bf357b50e798..f5d7cd0f4701 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4332,13 +4332,9 @@ int ufshcd_uic_hibern8_exit(struct ufs_hba *hba) ret = ufshcd_uic_pwr_ctrl(hba, &uic_cmd); trace_ufshcd_profile_hibern8(dev_name(hba->dev), "exit", ktime_to_us(ktime_sub(ktime_get(), start)), ret); - /* - * Do full reinit if exit failed or if LINERESET was detected during - * Hibern8 operation. After LINERESET, link moves to default PWM-G1 - * mode hence full reinit is required to move link to HS speeds. - */ - if (ret || hba->full_init_linereset) { - hba->full_init_linereset = false; + + /* Do full reinit if exit failed */ + if (ret) { ufshcd_update_error_stats(hba, UFS_ERR_HIBERN8_EXIT); dev_err(hba->dev, "%s: hibern8 exit failed. ret = %d", __func__, ret); @@ -6083,14 +6079,16 @@ static irqreturn_t ufshcd_update_uic_error(struct ufs_hba *hba) __func__, reg); ufshcd_update_uic_reg_hist(&hba->ufs_stats.pa_err, reg); - /* Don't ignore LINERESET indication during hibern8 operation */ + /* + * Don't ignore LINERESET indication during hibern8 + * enter operation. + */ if (reg & UIC_PHY_ADAPTER_LAYER_GENERIC_ERROR) { struct uic_command *cmd = hba->active_uic_cmd; if (cmd) { - if ((cmd->command == UIC_CMD_DME_HIBER_ENTER) - || (cmd->command == UIC_CMD_DME_HIBER_EXIT)) { - dev_err(hba->dev, "%s: LINERESET during hibern8, reg 0x%x\n", + if (cmd->command == UIC_CMD_DME_HIBER_ENTER) { + dev_err(hba->dev, "%s: LINERESET during hibern8 enter, reg 0x%x\n", __func__, reg); hba->full_init_linereset = true; } @@ -7292,13 +7290,6 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) if (ufshcd_scsi_add_wlus(hba)) goto out; - /* Enable auto hibern8 if supported, after full host and - * device initialization. - */ - if (ufshcd_is_auto_hibern8_supported(hba)) - ufshcd_set_auto_hibern8_timer(hba, - hba->hibern8_on_idle.delay_ms); - /* Initialize devfreq after UFS device is detected */ if (ufshcd_is_clkscaling_supported(hba)) { memcpy(&hba->clk_scaling.saved_pwr_info.info, @@ -7327,6 +7318,13 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) if (!hba->is_init_prefetch) hba->is_init_prefetch = true; + /* + * Enable auto hibern8 if supported, after full host and + * device initialization. + */ + if (ufshcd_is_auto_hibern8_supported(hba)) + ufshcd_set_auto_hibern8_timer(hba, + hba->hibern8_on_idle.delay_ms); out: /* * If we failed to initialize the device or the device is not diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 623e389aff1f..2605107e2dbd 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -95,7 +95,7 @@ obj-$(CONFIG_QCOM_REMOTEQDSS) += remoteqdss.o obj-$(CONFIG_MSM_SERVICE_LOCATOR) += service-locator.o obj-$(CONFIG_MSM_QBT1000) += qbt1000.o obj-$(CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG) += rpm_rbcpr_stats_v2.o -obj-$(CONFIG_MSM_RPM_STATS_LOG) += rpm_stats.o rpm_master_stat.o system_stats.o +obj-$(CONFIG_MSM_RPM_STATS_LOG) += rpm_stats.o rpm_master_stat.o rpm_rail_stats.o system_stats.o obj-$(CONFIG_MSM_RPM_LOG) += rpm_log.o obj-$(CONFIG_QSEE_IPC_IRQ_BRIDGE) += qsee_ipc_irq_bridge.o obj-$(CONFIG_WCD_DSP_GLINK) += wcd-dsp-glink.o diff --git a/drivers/soc/qcom/glink_spi_xprt.c b/drivers/soc/qcom/glink_spi_xprt.c index 5de6e7eac7ea..6794c30605d7 100644 --- a/drivers/soc/qcom/glink_spi_xprt.c +++ b/drivers/soc/qcom/glink_spi_xprt.c @@ -891,7 +891,7 @@ static void __rx_worker(struct edge_info *einfo) } glink_spi_xprt_set_poll_mode(einfo); - while (inactive_cycles < MAX_INACTIVE_CYCLES) { + do { if (einfo->tx_resume_needed && glink_spi_xprt_write_avail(einfo)) { einfo->tx_resume_needed = false; @@ -926,7 +926,7 @@ static void __rx_worker(struct edge_info *einfo) } process_rx_cmd(einfo, rx_data, rx_avail); kfree(rx_data); - } + } while (inactive_cycles < MAX_INACTIVE_CYCLES && !einfo->in_ssr); glink_spi_xprt_set_irq_mode(einfo); srcu_read_unlock(&einfo->use_ref, rcu_id); } diff --git a/drivers/soc/qcom/pil-msa.c b/drivers/soc/qcom/pil-msa.c index bbd77d8e0650..53bddc5987df 100644 --- a/drivers/soc/qcom/pil-msa.c +++ b/drivers/soc/qcom/pil-msa.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, 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 @@ -332,6 +332,7 @@ int __pil_mss_deinit_image(struct pil_desc *pil, bool err_path) struct modem_data *drv = dev_get_drvdata(pil->dev); struct q6v5_data *q6_drv = container_of(pil, struct q6v5_data, desc); int ret = 0; + struct device *dma_dev = drv->mba_mem_dev_fixed ?: &drv->mba_mem_dev; s32 status; u64 val = is_timeout_disabled() ? 0 : pbl_mba_boot_timeout_ms * 1000; @@ -360,7 +361,7 @@ int __pil_mss_deinit_image(struct pil_desc *pil, bool err_path) if (pil->subsys_vmid > 0) pil_assign_mem_to_linux(pil, drv->q6->mba_dp_phys, drv->q6->mba_dp_size); - dma_free_attrs(&drv->mba_mem_dev, drv->q6->mba_dp_size, + dma_free_attrs(dma_dev, drv->q6->mba_dp_size, drv->q6->mba_dp_virt, drv->q6->mba_dp_phys, &drv->attrs_dma); drv->q6->mba_dp_virt = NULL; @@ -552,6 +553,7 @@ int pil_mss_reset_load_mba(struct pil_desc *pil) dma_addr_t mba_dp_phys, mba_dp_phys_end; int ret, count; const u8 *data; + struct device *dma_dev = md->mba_mem_dev_fixed ?: &md->mba_mem_dev; fw_name_p = drv->non_elf_image ? fw_name_legacy : fw_name; ret = request_firmware(&fw, fw_name_p, pil->dev); @@ -570,11 +572,12 @@ int pil_mss_reset_load_mba(struct pil_desc *pil) drv->mba_dp_size = SZ_1M; - arch_setup_dma_ops(&md->mba_mem_dev, 0, 0, NULL, 0); + arch_setup_dma_ops(dma_dev, 0, 0, NULL, 0); + + dma_dev->coherent_dma_mask = DMA_BIT_MASK(sizeof(dma_addr_t) * 8); - md->mba_mem_dev.coherent_dma_mask = - DMA_BIT_MASK(sizeof(dma_addr_t) * 8); init_dma_attrs(&md->attrs_dma); + dma_set_attr(DMA_ATTR_SKIP_ZEROING, &md->attrs_dma); dma_set_attr(DMA_ATTR_STRONGLY_ORDERED, &md->attrs_dma); ret = request_firmware(&dp_fw, dp_name, pil->dev); @@ -591,10 +594,10 @@ int pil_mss_reset_load_mba(struct pil_desc *pil) drv->mba_dp_size += drv->dp_size; } - mba_dp_virt = dma_alloc_attrs(&md->mba_mem_dev, drv->mba_dp_size, - &mba_dp_phys, GFP_KERNEL, &md->attrs_dma); + mba_dp_virt = dma_alloc_attrs(dma_dev, drv->mba_dp_size, &mba_dp_phys, + GFP_KERNEL, &md->attrs_dma); if (!mba_dp_virt) { - dev_err(pil->dev, "%s MBA metadata buffer allocation %zx bytes failed\n", + dev_err(pil->dev, "%s MBA/DP buffer allocation %zx bytes failed\n", __func__, drv->mba_dp_size); ret = -ENOMEM; goto err_invalid_fw; @@ -651,7 +654,7 @@ err_mss_reset: pil_assign_mem_to_linux(pil, drv->mba_dp_phys, drv->mba_dp_size); err_mba_data: - dma_free_attrs(&md->mba_mem_dev, drv->mba_dp_size, drv->mba_dp_virt, + dma_free_attrs(dma_dev, drv->mba_dp_size, drv->mba_dp_virt, drv->mba_dp_phys, &md->attrs_dma); err_invalid_fw: if (dp_fw) @@ -670,14 +673,16 @@ static int pil_msa_auth_modem_mdt(struct pil_desc *pil, const u8 *metadata, s32 status; int ret; u64 val = is_timeout_disabled() ? 0 : modem_auth_timeout_ms * 1000; + struct device *dma_dev = drv->mba_mem_dev_fixed ?: &drv->mba_mem_dev; DEFINE_DMA_ATTRS(attrs); - drv->mba_mem_dev.coherent_dma_mask = - DMA_BIT_MASK(sizeof(dma_addr_t) * 8); + + dma_dev->coherent_dma_mask = DMA_BIT_MASK(sizeof(dma_addr_t) * 8); + dma_set_attr(DMA_ATTR_SKIP_ZEROING, &attrs); dma_set_attr(DMA_ATTR_STRONGLY_ORDERED, &attrs); /* Make metadata physically contiguous and 4K aligned. */ - mdata_virt = dma_alloc_attrs(&drv->mba_mem_dev, size, &mdata_phys, - GFP_KERNEL, &attrs); + mdata_virt = dma_alloc_attrs(dma_dev, size, &mdata_phys, GFP_KERNEL, + &attrs); if (!mdata_virt) { dev_err(pil->dev, "%s MBA metadata buffer allocation %zx bytes failed\n", __func__, size); @@ -694,8 +699,8 @@ static int pil_msa_auth_modem_mdt(struct pil_desc *pil, const u8 *metadata, if (ret) { pr_err("scm_call to unprotect modem metadata mem failed(rc:%d)\n", ret); - dma_free_attrs(&drv->mba_mem_dev, size, mdata_virt, - mdata_phys, &attrs); + dma_free_attrs(dma_dev, size, mdata_virt, mdata_phys, + &attrs); goto fail; } } @@ -721,7 +726,7 @@ static int pil_msa_auth_modem_mdt(struct pil_desc *pil, const u8 *metadata, if (pil->subsys_vmid > 0) pil_assign_mem_to_linux(pil, mdata_phys, ALIGN(size, SZ_4K)); - dma_free_attrs(&drv->mba_mem_dev, size, mdata_virt, mdata_phys, &attrs); + dma_free_attrs(dma_dev, size, mdata_virt, mdata_phys, &attrs); if (!ret) return ret; @@ -733,7 +738,7 @@ fail: if (pil->subsys_vmid > 0) pil_assign_mem_to_linux(pil, drv->q6->mba_dp_phys, drv->q6->mba_dp_size); - dma_free_attrs(&drv->mba_mem_dev, drv->q6->mba_dp_size, + dma_free_attrs(dma_dev, drv->q6->mba_dp_size, drv->q6->mba_dp_virt, drv->q6->mba_dp_phys, &drv->attrs_dma); drv->q6->mba_dp_virt = NULL; @@ -785,6 +790,7 @@ static int pil_msa_mba_auth(struct pil_desc *pil) struct modem_data *drv = dev_get_drvdata(pil->dev); struct q6v5_data *q6_drv = container_of(pil, struct q6v5_data, desc); int ret; + struct device *dma_dev = drv->mba_mem_dev_fixed ?: &drv->mba_mem_dev; s32 status; u64 val = is_timeout_disabled() ? 0 : modem_auth_timeout_ms * 1000; @@ -806,9 +812,9 @@ static int pil_msa_mba_auth(struct pil_desc *pil) pil_assign_mem_to_linux(pil, drv->q6->mba_dp_phys, drv->q6->mba_dp_size); - dma_free_attrs(&drv->mba_mem_dev, drv->q6->mba_dp_size, - drv->q6->mba_dp_virt, - drv->q6->mba_dp_phys, &drv->attrs_dma); + dma_free_attrs(dma_dev, drv->q6->mba_dp_size, + drv->q6->mba_dp_virt, drv->q6->mba_dp_phys, + &drv->attrs_dma); drv->q6->mba_dp_virt = NULL; } diff --git a/drivers/soc/qcom/pil-msa.h b/drivers/soc/qcom/pil-msa.h index fea8e1f9db37..896f0c7c232b 100644 --- a/drivers/soc/qcom/pil-msa.h +++ b/drivers/soc/qcom/pil-msa.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, 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 @@ -32,6 +32,7 @@ struct modem_data { struct clk *xo; struct pil_desc desc; struct device mba_mem_dev; + struct device *mba_mem_dev_fixed; struct dma_attrs attrs_dma; }; diff --git a/drivers/soc/qcom/pil-q6v5-mss.c b/drivers/soc/qcom/pil-q6v5-mss.c index 5f01d30de8d9..0e023a019280 100644 --- a/drivers/soc/qcom/pil-q6v5-mss.c +++ b/drivers/soc/qcom/pil-q6v5-mss.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2017, 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 @@ -14,6 +14,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/of_platform.h> #include <linux/io.h> #include <linux/iopoll.h> #include <linux/ioport.h> @@ -394,6 +395,11 @@ static int pil_mss_driver_probe(struct platform_device *pdev) } init_completion(&drv->stop_ack); + /* Probe the MBA mem device if present */ + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (ret) + return ret; + return pil_subsys_init(drv, pdev); } @@ -407,6 +413,33 @@ static int pil_mss_driver_exit(struct platform_device *pdev) return 0; } +static int pil_mba_mem_driver_probe(struct platform_device *pdev) +{ + struct modem_data *drv; + + if (!pdev->dev.parent) { + pr_err("No parent found.\n"); + return -EINVAL; + } + drv = dev_get_drvdata(pdev->dev.parent); + drv->mba_mem_dev_fixed = &pdev->dev; + return 0; +} + +static const struct of_device_id mba_mem_match_table[] = { + { .compatible = "qcom,pil-mba-mem" }, + {} +}; + +static struct platform_driver pil_mba_mem_driver = { + .probe = pil_mba_mem_driver_probe, + .driver = { + .name = "pil-mba-mem", + .of_match_table = mba_mem_match_table, + .owner = THIS_MODULE, + }, +}; + static struct of_device_id mss_match_table[] = { { .compatible = "qcom,pil-q6v5-mss" }, { .compatible = "qcom,pil-q6v55-mss" }, @@ -426,7 +459,12 @@ static struct platform_driver pil_mss_driver = { static int __init pil_mss_init(void) { - return platform_driver_register(&pil_mss_driver); + int ret; + + ret = platform_driver_register(&pil_mba_mem_driver); + if (!ret) + ret = platform_driver_register(&pil_mss_driver); + return ret; } module_init(pil_mss_init); diff --git a/drivers/soc/qcom/qbt1000.c b/drivers/soc/qcom/qbt1000.c index dd543daca1b4..4ba92436bd06 100644 --- a/drivers/soc/qcom/qbt1000.c +++ b/drivers/soc/qcom/qbt1000.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 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 @@ -90,6 +90,7 @@ struct qbt1000_drvdata { struct finger_detect_gpio fd_gpio; DECLARE_KFIFO(fw_events, struct fw_event_desc, MAX_FW_EVENTS); wait_queue_head_t read_wait_queue; + struct qseecom_handle *app_handle; struct qseecom_handle *fp_app_handle; }; @@ -147,7 +148,8 @@ static int get_cmd_rsp_buffers(struct qseecom_handle *hdl, *cmd_len = ALIGN(*cmd_len, 64); *rsp_len = ALIGN(*rsp_len, 64); - if ((*rsp_len + *cmd_len) > g_app_buf_size) { + if (((uint64_t)*rsp_len + (uint64_t)*cmd_len) + > (uint64_t)g_app_buf_size) { pr_err("buffer too small to hold cmd=%d and rsp=%d\n", *cmd_len, *rsp_len); return -ENOMEM; @@ -261,6 +263,13 @@ static int send_tz_cmd(struct qbt1000_drvdata *drvdata, if (rc != 0) goto end; + if (!aligned_cmd) { + dev_err(drvdata->dev, "%s: Null command buffer\n", + __func__); + rc = -EINVAL; + goto end; + } + if (aligned_cmd - cmd + cmd_len > g_app_buf_size) { rc = -ENOMEM; goto end; @@ -392,13 +401,26 @@ static long qbt1000_ioctl(struct file *file, unsigned cmd, unsigned long arg) goto end; } + if (drvdata->app_handle) { + dev_err(drvdata->dev, "%s: LOAD app already loaded, unloading first\n", + __func__); + drvdata->fp_app_handle = 0; + rc = qseecom_shutdown_app(&drvdata->app_handle); + if (rc != 0) { + dev_err(drvdata->dev, "%s: LOAD current app failed to shutdown\n", + __func__); + goto end; + } + } + pr_debug("app %s load before\n", app.name); /* start the TZ app */ - rc = qseecom_start_app(&app_handle, app.name, app.size); + rc = qseecom_start_app( + &drvdata->app_handle, app.name, app.size); if (rc == 0) { g_app_buf_size = app.size; - rc = qseecom_set_bandwidth(app_handle, + rc = qseecom_set_bandwidth(drvdata->app_handle, app.high_band_width == 1 ? true : false); if (rc != 0) { /* log error, allow to continue */ @@ -409,7 +431,9 @@ static long qbt1000_ioctl(struct file *file, unsigned cmd, unsigned long arg) goto end; } - /* copy the app handle to user */ + /* copy a fake app handle to user */ + app_handle = drvdata->app_handle ? + (struct qseecom_handle *)123456 : 0; rc = copy_to_user((void __user *)app.app_handle, &app_handle, sizeof(*app.app_handle)); @@ -424,14 +448,14 @@ static long qbt1000_ioctl(struct file *file, unsigned cmd, unsigned long arg) pr_debug("app %s load after\n", app.name); if (!strcmp(app.name, FP_APP_NAME)) - drvdata->fp_app_handle = app_handle; + drvdata->fp_app_handle = drvdata->app_handle; break; } case QBT1000_UNLOAD_APP: { struct qbt1000_app app; - struct qseecom_handle *app_handle; + struct qseecom_handle *app_handle = 0; if (copy_from_user(&app, priv_arg, sizeof(app)) != 0) { @@ -459,19 +483,19 @@ static long qbt1000_ioctl(struct file *file, unsigned cmd, unsigned long arg) } /* if the app hasn't been loaded already, return err */ - if (!app_handle) { + if (!drvdata->app_handle) { pr_err("app not loaded\n"); rc = -EINVAL; goto end; } - if (drvdata->fp_app_handle == app_handle) + if (drvdata->fp_app_handle == drvdata->app_handle) drvdata->fp_app_handle = 0; /* set bw & shutdown the TZ app */ - qseecom_set_bandwidth(app_handle, + qseecom_set_bandwidth(drvdata->app_handle, app.high_band_width == 1 ? true : false); - rc = qseecom_shutdown_app(&app_handle); + rc = qseecom_shutdown_app(&drvdata->app_handle); if (rc != 0) { pr_err("app failed to shutdown\n"); goto end; @@ -513,14 +537,14 @@ static long qbt1000_ioctl(struct file *file, unsigned cmd, unsigned long arg) } /* if the app hasn't been loaded already, return err */ - if (!tzcmd.app_handle) { + if (!drvdata->app_handle) { pr_err("app not loaded\n"); rc = -EINVAL; goto end; } rc = send_tz_cmd(drvdata, - tzcmd.app_handle, 1, + drvdata->app_handle, 1, tzcmd.req_buf, tzcmd.req_buf_len, &rsp_buf, tzcmd.rsp_buf_len); @@ -1112,6 +1136,7 @@ static int qbt1000_read_device_tree(struct platform_device *pdev, } /* read finger detect GPIO configuration */ + gpio = of_get_named_gpio_flags(pdev->dev.of_node, "qcom,finger-detect-gpio", 0, &flags); if (gpio < 0) { diff --git a/drivers/soc/qcom/rpm_rail_stats.c b/drivers/soc/qcom/rpm_rail_stats.c new file mode 100644 index 000000000000..d80a9fc5146a --- /dev/null +++ b/drivers/soc/qcom/rpm_rail_stats.c @@ -0,0 +1,329 @@ +/* Copyright (c) 2015,2017, 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/debugfs.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/of.h> +#include <linux/uaccess.h> + +#include "rpm_stats.h" + +#define RPM_RAIL_BUF_LEN 600 + +#define SNPRINTF(buf, size, format, ...) \ +{ \ + if (size > 0) { \ + int ret; \ + ret = snprintf(buf, size, format, ## __VA_ARGS__); \ + if (ret > size) { \ + buf += size; \ + size = 0; \ + } else { \ + buf += ret; \ + size -= ret; \ + } \ + } \ +} + +#define NAMELEN (sizeof(uint32_t)+1) + +struct msm_rpm_rail_stats_platform_data { + phys_addr_t phys_addr_base; + u32 phys_size; +}; + +struct msm_rpm_rail_corner { + uint64_t time; + uint32_t corner; + uint32_t reserved; +}; + +struct msm_rpm_rail_type { + uint32_t rail; + uint32_t num_corners; + uint32_t current_corner; + uint32_t last_entered; +}; + +struct msm_rpm_rail_stats { + uint32_t num_rails; + uint32_t reserved; +}; + +struct msm_rpm_rail_stats_private_data { + void __iomem *reg_base; + u32 len; + char buf[RPM_RAIL_BUF_LEN]; + struct msm_rpm_rail_stats_platform_data *platform_data; +}; + +int msm_rpm_rail_stats_file_close(struct inode *inode, struct file *file) +{ + struct msm_rpm_rail_stats_private_data *private = file->private_data; + + if (private->reg_base) + iounmap(private->reg_base); + kfree(file->private_data); + + return 0; +} + +static int msm_rpm_rail_corner_copy(void __iomem **base, char **buf, + int count) +{ + struct msm_rpm_rail_corner rc; + char corner[NAMELEN]; + + memset(&rc, 0, sizeof(rc)); + memcpy_fromio(&rc, *base, sizeof(rc)); + + corner[NAMELEN - 1] = '\0'; + memcpy(corner, &rc.corner, NAMELEN - 1); + SNPRINTF(*buf, count, "\t\tcorner:%-5s time:%-16llu\n", + corner, rc.time); + + *base += sizeof(rc); + + return count; +} + +static int msm_rpm_rail_type_copy(void __iomem **base, char **buf, int count) +{ + struct msm_rpm_rail_type rt; + char rail[NAMELEN]; + int i; + + memset(&rt, 0, sizeof(rt)); + memcpy_fromio(&rt, *base, sizeof(rt)); + + rail[NAMELEN - 1] = '\0'; + memcpy(rail, &rt.rail, NAMELEN - 1); + SNPRINTF(*buf, count, + "\trail:%-2s num_corners:%-2u current_corner:%-2u last_entered:%-8u\n", + rail, rt.num_corners, rt.current_corner, rt.last_entered); + + *base += sizeof(rt); + + for (i = 0; i < rt.num_corners; i++) + count = msm_rpm_rail_corner_copy(base, buf, count); + + return count; +} + +static int msm_rpm_rail_stats_copy( + struct msm_rpm_rail_stats_private_data *prvdata) +{ + struct msm_rpm_rail_stats rs; + void __iomem *base = prvdata->reg_base; + char *buf = prvdata->buf; + int count = RPM_RAIL_BUF_LEN; + int i; + + memset(&rs, 0, sizeof(rs)); + memcpy_fromio(&rs, base, sizeof(rs)); + + SNPRINTF(buf, count, "Number of Rails:%u\n", rs.num_rails); + + base = prvdata->reg_base + sizeof(rs); + + for (i = 0; i < rs.num_rails; i++) + count = msm_rpm_rail_type_copy(&base, &buf, count); + + return RPM_RAIL_BUF_LEN - count; +} + +static ssize_t msm_rpm_rail_stats_file_read(struct file *file, + char __user *bufu, size_t count, loff_t *ppos) +{ + struct msm_rpm_rail_stats_private_data *prvdata = + file->private_data; + struct msm_rpm_rail_stats_platform_data *pdata; + + if (!prvdata) + return -EINVAL; + + if (!prvdata->platform_data) + return -EINVAL; + + if (!bufu || count == 0) + return -EINVAL; + + pdata = prvdata->platform_data; + + if (*ppos <= pdata->phys_size) { + prvdata->len = msm_rpm_rail_stats_copy(prvdata); + *ppos = 0; + } + + return simple_read_from_buffer(bufu, count, ppos, + prvdata->buf, prvdata->len); +} + +static int msm_rpm_rail_stats_file_open(struct inode *inode, + struct file *file) +{ + struct msm_rpm_rail_stats_private_data *prvdata; + struct msm_rpm_rail_stats_platform_data *pdata = inode->i_private; + + file->private_data = + kzalloc(sizeof(struct msm_rpm_rail_stats_private_data), + GFP_KERNEL); + + if (!file->private_data) + return -ENOMEM; + prvdata = file->private_data; + + prvdata->reg_base = ioremap(pdata->phys_addr_base, + pdata->phys_size); + if (!prvdata->reg_base) { + kfree(file->private_data); + prvdata = NULL; + pr_err("%s: ERROR could not ioremap start=%pa, len=%u\n", + __func__, &pdata->phys_addr_base, + pdata->phys_size); + return -EBUSY; + } + + prvdata->len = 0; + prvdata->platform_data = pdata; + return 0; +} + + +static const struct file_operations msm_rpm_rail_stats_fops = { + .owner = THIS_MODULE, + .open = msm_rpm_rail_stats_file_open, + .read = msm_rpm_rail_stats_file_read, + .release = msm_rpm_rail_stats_file_close, + .llseek = no_llseek, +}; + +static int msm_rpm_rail_stats_probe(struct platform_device *pdev) +{ + struct dentry *dent; + struct msm_rpm_rail_stats_platform_data *pdata; + struct resource *res; + struct resource *offset; + struct device_node *node; + uint32_t offset_addr; + void __iomem *phys_ptr; + + if (!pdev) + return -EINVAL; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "phys_addr_base"); + if (!res) + return -EINVAL; + + offset = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "offset_addr"); + if (!offset) + return -EINVAL; + + phys_ptr = ioremap_nocache(offset->start, SZ_4); + if (!phys_ptr) { + dev_err(&pdev->dev, "%s: Failed to ioremap address.\n", + __func__); + return -ENODEV; + } + offset_addr = readl_relaxed(phys_ptr); + iounmap(phys_ptr); + + if (!offset_addr) { + dev_err(&pdev->dev, "%s: RPM Rail Stats not available: Exit\n", + __func__); + return 0; + } + + node = pdev->dev.of_node; + + if (pdev->dev.platform_data) { + pdata = pdev->dev.platform_data; + if (!pdata) + return -ENOMEM; + } else if (node) { + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + } else { + dev_err(&pdev->dev, "%s: pdata is not available: Exit\n", + __func__); + return 0; + } + + pdata->phys_addr_base = res->start + offset_addr; + pdata->phys_size = resource_size(res); + + dent = debugfs_create_file("rpm_rail_stats", S_IRUGO, NULL, + pdata, &msm_rpm_rail_stats_fops); + + if (!dent) { + dev_err(&pdev->dev, "%s: ERROR debugfs_create_file failed\n", + __func__); + return -ENOMEM; + } + + platform_set_drvdata(pdev, dent); + return 0; +} + +static int msm_rpm_rail_stats_remove(struct platform_device *pdev) +{ + struct dentry *dent = platform_get_drvdata(pdev); + + debugfs_remove(dent); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static const struct of_device_id rpm_rail_table[] = { + {.compatible = "qcom,rpm-rail-stats"}, + {}, +}; + +static struct platform_driver msm_rpm_rail_stats_driver = { + .probe = msm_rpm_rail_stats_probe, + .remove = msm_rpm_rail_stats_remove, + .driver = { + .name = "msm_rpm_rail_stats", + .owner = THIS_MODULE, + .of_match_table = rpm_rail_table, + }, +}; + +static int __init msm_rpm_rail_stats_init(void) +{ + return platform_driver_register(&msm_rpm_rail_stats_driver); +} + +static void __exit msm_rpm_rail_stats_exit(void) +{ + platform_driver_unregister(&msm_rpm_rail_stats_driver); +} + +module_init(msm_rpm_rail_stats_init); +module_exit(msm_rpm_rail_stats_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MSM RPM rail Statistics driver"); +MODULE_ALIAS("platform:msm_rail_stat_log"); diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c index 35ccc57a2dea..6a60e3624420 100644 --- a/drivers/soc/qcom/spcom.c +++ b/drivers/soc/qcom/spcom.c @@ -359,6 +359,11 @@ static void spcom_link_state_notif_cb(struct glink_link_state_cb_info *cb_info, struct spcom_channel *ch = NULL; const char *ch_name = "sp_kernel"; + if (!spcom_is_ready()) { + pr_err("spcom is not ready.\n"); + return; + } + spcom_dev->link_state = cb_info->link_state; pr_debug("spcom_link_state_notif_cb called. transport = %s edge = %s\n", @@ -1017,7 +1022,10 @@ static void spcom_rx_abort_pending_server(void) */ bool spcom_is_sp_subsystem_link_up(void) { - return (spcom_dev->link_state == GLINK_LINK_STATE_UP); + if (spcom_dev == NULL) + return false; + + return (spcom_dev->link_state == GLINK_LINK_STATE_UP); } EXPORT_SYMBOL(spcom_is_sp_subsystem_link_up); @@ -1039,6 +1047,11 @@ struct spcom_client *spcom_register_client(struct spcom_client_info *info) struct spcom_channel *ch; struct spcom_client *client; + if (!spcom_is_ready()) { + pr_err("spcom is not ready.\n"); + return NULL; + } + if (!info) { pr_err("Invalid parameter.\n"); return NULL; @@ -1080,6 +1093,11 @@ int spcom_unregister_client(struct spcom_client *client) { struct spcom_channel *ch; + if (!spcom_is_ready()) { + pr_err("spcom is not ready.\n"); + return -ENODEV; + } + if (!client) { pr_err("Invalid parameter.\n"); return -EINVAL; @@ -1118,6 +1136,11 @@ int spcom_client_send_message_sync(struct spcom_client *client, int ret; struct spcom_channel *ch; + if (!spcom_is_ready()) { + pr_err("spcom is not ready.\n"); + return -ENODEV; + } + if (!client || !req_ptr || !resp_ptr) { pr_err("Invalid parameter.\n"); return -EINVAL; @@ -1159,9 +1182,14 @@ bool spcom_client_is_server_connected(struct spcom_client *client) { bool connected; + if (!spcom_is_ready()) { + pr_err("spcom is not ready.\n"); + return false; + } + if (!client) { pr_err("Invalid parameter.\n"); - return -EINVAL; + return false; } connected = spcom_is_channel_connected(client->ch); @@ -1188,6 +1216,11 @@ struct spcom_server *spcom_register_service(struct spcom_service_info *info) struct spcom_channel *ch; struct spcom_server *server; + if (!spcom_is_ready()) { + pr_err("spcom is not ready.\n"); + return NULL; + } + if (!info) { pr_err("Invalid parameter.\n"); return NULL; @@ -1226,6 +1259,11 @@ int spcom_unregister_service(struct spcom_server *server) { struct spcom_channel *ch; + if (!spcom_is_ready()) { + pr_err("spcom is not ready.\n"); + return -ENODEV; + } + if (!server) { pr_err("Invalid parameter.\n"); return -EINVAL; @@ -1290,6 +1328,11 @@ int spcom_server_wait_for_request(struct spcom_server *server, int ret; struct spcom_channel *ch; + if (!spcom_is_ready()) { + pr_err("spcom is not ready.\n"); + return -ENODEV; + } + if (!server || !req_ptr) { pr_err("Invalid parameter.\n"); return -EINVAL; @@ -1323,6 +1366,11 @@ int spcom_server_send_response(struct spcom_server *server, int ret; struct spcom_channel *ch; + if (!spcom_is_ready()) { + pr_err("spcom is not ready.\n"); + return -ENODEV; + } + if (!server || !resp_ptr) { pr_err("Invalid parameter.\n"); return -EINVAL; @@ -2373,7 +2421,7 @@ static int spcom_create_channel_chardev(const char *name) devt = spcom_dev->device_no + spcom_dev->channel_count; priv = ch; dev = device_create(cls, parent, devt, priv, name); - if (!dev) { + if (IS_ERR(dev)) { pr_err("device_create failed.\n"); kfree(cdev); return -ENODEV; @@ -2426,7 +2474,7 @@ static int __init spcom_register_chardev(void) spcom_dev->device_no, priv, DEVICE_NAME); - if (!spcom_dev->class_dev) { + if (IS_ERR(spcom_dev->class_dev)) { pr_err("class_device_create failed %d\n", ret); ret = -ENOMEM; goto exit_destroy_class; @@ -2479,6 +2527,11 @@ static int spcom_parse_dt(struct device_node *np) pr_debug("num of predefined channels [%d].\n", num_ch); + if (num_ch > ARRAY_SIZE(spcom_dev->predefined_ch_name)) { + pr_err("too many predefined channels [%d].\n", num_ch); + return -EINVAL; + } + for (i = 0; i < num_ch; i++) { ret = of_property_read_string_index(np, propname, i, &name); if (ret) { @@ -2544,21 +2597,23 @@ static int spcom_probe(struct platform_device *pdev) pr_debug("register_link_state_cb(), transport [%s] edge [%s]\n", link_info.transport, link_info.edge); notif_handle = glink_register_link_state_cb(&link_info, spcom_dev); - if (!notif_handle) { + if (IS_ERR(notif_handle)) { pr_err("glink_register_link_state_cb(), err [%d]\n", ret); goto fail_reg_chardev; } spcom_dev->ion_client = msm_ion_client_create(DEVICE_NAME); - if (spcom_dev->ion_client == NULL) { + if (IS_ERR(spcom_dev->ion_client)) { pr_err("fail to create ion client.\n"); - goto fail_reg_chardev; + goto fail_ion_client; } pr_info("Driver Initialization ok.\n"); return 0; +fail_ion_client: + glink_unregister_link_state_cb(notif_handle); fail_reg_chardev: pr_err("Failed to init driver.\n"); spcom_unregister_chrdev(); @@ -2596,7 +2651,7 @@ static int __init spcom_init(void) if (ret) pr_err("spcom_driver register failed %d\n", ret); - return 0; + return ret; } module_init(spcom_init); diff --git a/drivers/soc/qcom/spss_utils.c b/drivers/soc/qcom/spss_utils.c index e17a1370d1f0..cbf44be7fbab 100644 --- a/drivers/soc/qcom/spss_utils.c +++ b/drivers/soc/qcom/spss_utils.c @@ -140,7 +140,7 @@ static ssize_t spss_debug_reg_show(struct device *dev, { int ret; void __iomem *spss_debug_reg = NULL; - int val1, val2; + u32 val1, val2; if (!dev || !attr || !buf) { pr_err("invalid param.\n"); @@ -149,7 +149,7 @@ static ssize_t spss_debug_reg_show(struct device *dev, pr_debug("spss_debug_reg_addr [0x%x].\n", spss_debug_reg_addr); - spss_debug_reg = ioremap_nocache(spss_debug_reg_addr, 0x16); + spss_debug_reg = ioremap_nocache(spss_debug_reg_addr, sizeof(u32)*2); if (!spss_debug_reg) { pr_err("can't map debug reg addr.\n"); @@ -157,7 +157,7 @@ static ssize_t spss_debug_reg_show(struct device *dev, } val1 = readl_relaxed(spss_debug_reg); - val2 = readl_relaxed(((char *) spss_debug_reg) + 0x04); + val2 = readl_relaxed(((char *) spss_debug_reg) + sizeof(u32)); ret = snprintf(buf, PAGE_SIZE, "val1 [0x%x] val2 [0x%x]", val1, val2); @@ -192,12 +192,15 @@ static int spss_create_sysfs(struct device *dev) ret = device_create_file(dev, &dev_attr_test_fuse_state); if (ret < 0) { pr_err("failed to create sysfs file for test_fuse_state.\n"); + device_remove_file(dev, &dev_attr_firmware_name); return ret; } ret = device_create_file(dev, &dev_attr_spss_debug_reg); if (ret < 0) { pr_err("failed to create sysfs file for spss_debug_reg.\n"); + device_remove_file(dev, &dev_attr_firmware_name); + device_remove_file(dev, &dev_attr_test_fuse_state); return ret; } @@ -284,13 +287,16 @@ static int spss_parse_dt(struct device_node *node) (int) spss_fuse2_addr, (int) spss_fuse2_bit); spss_fuse1_reg = ioremap_nocache(spss_fuse1_addr, sizeof(u32)); - spss_fuse2_reg = ioremap_nocache(spss_fuse2_addr, sizeof(u32)); if (!spss_fuse1_reg) { pr_err("can't map fuse1 addr.\n"); return -EFAULT; } + + spss_fuse2_reg = ioremap_nocache(spss_fuse2_addr, sizeof(u32)); + if (!spss_fuse2_reg) { + iounmap(spss_fuse1_reg); pr_err("can't map fuse2 addr.\n"); return -EFAULT; } @@ -380,7 +386,11 @@ static int spss_probe(struct platform_device *pdev) return -EFAULT; } - spss_create_sysfs(dev); + ret = spss_create_sysfs(dev); + if (ret < 0) { + pr_err("fail to create sysfs.\n"); + return -EFAULT; + } pr_info("Initialization completed ok, firmware_name [%s].\n", firmware_name); @@ -415,7 +425,7 @@ static int __init spss_init(void) if (ret) pr_err("register platform driver failed, ret [%d]\n", ret); - return 0; + return ret; } late_initcall(spss_init); /* start after PIL driver */ diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c index 4e1e12969b8d..2d52fcaf954a 100644 --- a/drivers/spi/spi_qsd.c +++ b/drivers/spi/spi_qsd.c @@ -2484,6 +2484,7 @@ static int msm_spi_probe(struct platform_device *pdev) dd->use_dma = 1; } + spi_dma_mask(&pdev->dev); skip_dma_resources: spin_lock_init(&dd->queue_lock); diff --git a/drivers/spi/spi_qsd.h b/drivers/spi/spi_qsd.h index 53ec1e600594..7a5cfadaa5a0 100644 --- a/drivers/spi/spi_qsd.h +++ b/drivers/spi/spi_qsd.h @@ -165,6 +165,13 @@ enum msm_spi_state { /* Data Mover commands should be aligned to 64 bit(8 bytes) */ #define DM_BYTE_ALIGN 8 +#if defined(CONFIG_ARM64) || defined(CONFIG_LPAE) +#define spi_dma_mask(dev) (dma_set_mask((dev), DMA_BIT_MASK(36))) +#else +#define spi_dma_mask(dev) (dma_set_mask((dev), DMA_BIT_MASK(32))) +#endif + + enum msm_spi_qup_version { SPI_QUP_VERSION_NONE = 0x0, SPI_QUP_VERSION_BFAM = 0x2, diff --git a/drivers/thermal/lmh_lite.c b/drivers/thermal/lmh_lite.c index 44ceb723d34c..8dd7f654e1d3 100644 --- a/drivers/thermal/lmh_lite.c +++ b/drivers/thermal/lmh_lite.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, 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 @@ -83,6 +83,8 @@ } \ /* Have barrier before reading from TZ data */ \ mb(); \ + dmac_inv_range(payload, (void *)payload + \ + sizeof(uint32_t) * LMH_SCM_PAYLOAD_SIZE);\ trace_lmh_event_call("GET_TYPE exit"); \ if (ret) { \ pr_err("Error in SCM v%d get type. cmd:%x err:%d\n", \ @@ -332,6 +334,8 @@ static void lmh_read_and_update(struct lmh_driver_data *lmh_dat) /* Have memory barrier before we access the TZ data */ mb(); trace_lmh_event_call("GET_INTENSITY exit"); + dmac_inv_range(&payload, (void *)&payload + + sizeof(struct lmh_sensor_packet)); if (ret) { pr_err("Error in SCM v%d read call. err:%d\n", (is_scm_armv8()) ? 8 : 7, ret); @@ -677,6 +681,7 @@ static int lmh_get_sensor_list(void) /* Have memory barrier before we access the TZ data */ mb(); trace_lmh_event_call("GET_SENSORS exit"); + dmac_inv_range(payload, (void *)payload + buf_size); if (ret < 0) { pr_err("Error in SCM v%d call. err:%d\n", (is_scm_armv8()) ? 8 : 7, ret); @@ -911,6 +916,7 @@ static int lmh_debug_read(struct lmh_debug_ops *ops, uint32_t **buf) } /* Have memory barrier before we access the TZ data */ mb(); + dmac_inv_range(payload, (void *)payload + curr_size); trace_lmh_event_call("GET_DEBUG_READ exit"); if (ret) { pr_err("Error in SCM v%d get debug read. err:%d\n", @@ -977,6 +983,7 @@ static int lmh_debug_config_write(uint32_t cmd_id, uint32_t *buf, int size) ret = scm_call2(SCM_SIP_FNID(SCM_SVC_LMH, cmd_id), &desc_arg); /* Have memory barrier before we access the TZ data */ mb(); + dmac_inv_range(payload, (void *)payload + size_bytes); trace_lmh_event_call("CONFIG_DEBUG_WRITE exit"); if (ret) { pr_err("Error in SCM v%d config debug read. err:%d\n", diff --git a/drivers/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c index cc232ff9be9a..c8cbd078bb07 100644 --- a/drivers/thermal/msm_thermal.c +++ b/drivers/thermal/msm_thermal.c @@ -2857,6 +2857,9 @@ static void msm_thermal_bite(int zone_id, int temp) pr_err("Tsens:%d reached temperature:%d. System reset\n", tsens_id, temp); } + /* If it is a secure device ignore triggering the thermal bite. */ + if (scm_is_secure_device()) + return; if (!is_scm_armv8()) { scm_call_atomic1(SCM_SVC_BOOT, THERM_SECURE_BITE_CMD, 0); } else { diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 2b49608756a0..04cf31c119ef 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -4,7 +4,7 @@ * Copyright (C) 2008 Intel Corp * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com> * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com> - * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -382,9 +382,11 @@ static __ref int sensor_sysfs_notify(void *data) struct sensor_info *sensor = (struct sensor_info *)data; while (!kthread_should_stop()) { - while (wait_for_completion_interruptible( - &sensor->sysfs_notify_complete) != 0) - ; + if (wait_for_completion_interruptible( + &sensor->sysfs_notify_complete) != 0) + continue; + if (sensor->deregister_active) + return ret; reinit_completion(&sensor->sysfs_notify_complete); sysfs_notify(&sensor->tz->device.kobj, NULL, THERMAL_UEVENT_DATA); @@ -580,6 +582,7 @@ int sensor_init(struct thermal_zone_device *tz) sensor->threshold_max = INT_MAX; sensor->max_idx = -1; sensor->min_idx = -1; + sensor->deregister_active = false; mutex_init(&sensor->lock); INIT_LIST_HEAD_RCU(&sensor->sensor_list); INIT_LIST_HEAD_RCU(&sensor->threshold_list); @@ -2471,6 +2474,8 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) thermal_remove_hwmon_sysfs(tz); flush_work(&tz->sensor.work); + tz->sensor.deregister_active = true; + complete(&tz->sensor.sysfs_notify_complete); kthread_stop(tz->sensor.sysfs_notify_thread); mutex_lock(&thermal_list_lock); list_del_rcu(&tz->sensor.sensor_list); diff --git a/drivers/uio/msm_sharedmem/sharedmem_qmi.c b/drivers/uio/msm_sharedmem/sharedmem_qmi.c index 48fb17ecdd95..fd95deeb6133 100644 --- a/drivers/uio/msm_sharedmem/sharedmem_qmi.c +++ b/drivers/uio/msm_sharedmem/sharedmem_qmi.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2015, 2017, 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 @@ -223,6 +223,7 @@ static int sharedmem_qmi_req_cb(struct qmi_handle *handle, void *conn_h, #define DEBUG_BUF_SIZE (2048) static char *debug_buffer; static u32 debug_data_size; +static struct mutex dbg_buf_lock; /* mutex for debug_buffer */ static ssize_t debug_read(struct file *file, char __user *buf, size_t count, loff_t *file_pos) @@ -279,21 +280,29 @@ static int debug_open(struct inode *inode, struct file *file) { u32 buffer_size; - if (debug_buffer != NULL) + mutex_lock(&dbg_buf_lock); + if (debug_buffer != NULL) { + mutex_unlock(&dbg_buf_lock); return -EBUSY; + } buffer_size = DEBUG_BUF_SIZE; debug_buffer = kzalloc(buffer_size, GFP_KERNEL); - if (debug_buffer == NULL) + if (debug_buffer == NULL) { + mutex_unlock(&dbg_buf_lock); return -ENOMEM; + } debug_data_size = fill_debug_info(debug_buffer, buffer_size); + mutex_unlock(&dbg_buf_lock); return 0; } static int debug_close(struct inode *inode, struct file *file) { + mutex_lock(&dbg_buf_lock); kfree(debug_buffer); debug_buffer = NULL; debug_data_size = 0; + mutex_unlock(&dbg_buf_lock); return 0; } @@ -324,6 +333,7 @@ static void debugfs_init(void) { struct dentry *f_ent; + mutex_init(&dbg_buf_lock); dir_ent = debugfs_create_dir("rmt_storage", NULL); if (IS_ERR(dir_ent)) { pr_err("Failed to create debug_fs directory\n"); @@ -352,6 +362,7 @@ static void debugfs_init(void) static void debugfs_exit(void) { debugfs_remove_recursive(dir_ent); + mutex_destroy(&dbg_buf_lock); } static void sharedmem_qmi_svc_recv_msg(struct work_struct *work) diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c index 8c80a8d80ac9..6481ee96f3e4 100644 --- a/drivers/usb/gadget/function/f_gsi.c +++ b/drivers/usb/gadget/function/f_gsi.c @@ -2537,7 +2537,7 @@ static int gsi_bind(struct usb_configuration *c, struct usb_function *f) /* export host's Ethernet address in CDC format */ random_ether_addr(gsi->d_port.ipa_init_params.device_ethaddr); random_ether_addr(gsi->d_port.ipa_init_params.host_ethaddr); - log_event_dbg("setting host_ethaddr=%pKM, device_ethaddr = %pKM", + log_event_dbg("setting host_ethaddr=%pM, device_ethaddr = %pM", gsi->d_port.ipa_init_params.host_ethaddr, gsi->d_port.ipa_init_params.device_ethaddr); memcpy(gsi->ethaddr, &gsi->d_port.ipa_init_params.host_ethaddr, @@ -2669,7 +2669,7 @@ static int gsi_bind(struct usb_configuration *c, struct usb_function *f) /* export host's Ethernet address in CDC format */ random_ether_addr(gsi->d_port.ipa_init_params.device_ethaddr); random_ether_addr(gsi->d_port.ipa_init_params.host_ethaddr); - log_event_dbg("setting host_ethaddr=%pKM, device_ethaddr = %pKM", + log_event_dbg("setting host_ethaddr=%pM, device_ethaddr = %pM", gsi->d_port.ipa_init_params.host_ethaddr, gsi->d_port.ipa_init_params.device_ethaddr); diff --git a/drivers/usb/gadget/function/f_qc_ecm.c b/drivers/usb/gadget/function/f_qc_ecm.c index 005a8d1f8747..d96f727b2da4 100644 --- a/drivers/usb/gadget/function/f_qc_ecm.c +++ b/drivers/usb/gadget/function/f_qc_ecm.c @@ -1134,7 +1134,7 @@ ecm_qc_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], if (ecm->xport != USB_GADGET_XPORT_BAM2BAM_IPA) return status; - pr_debug("setting ecm_ipa, host_ethaddr=%pKM, device_ethaddr=%pKM", + pr_debug("setting ecm_ipa, host_ethaddr=%pM, device_ethaddr=%pM", ipa_params.host_ethaddr, ipa_params.device_ethaddr); status = ecm_ipa_init(&ipa_params); if (status) { diff --git a/drivers/usb/gadget/function/f_qc_rndis.c b/drivers/usb/gadget/function/f_qc_rndis.c index baffff7e52e9..ac17d0aa46ae 100644 --- a/drivers/usb/gadget/function/f_qc_rndis.c +++ b/drivers/usb/gadget/function/f_qc_rndis.c @@ -1198,7 +1198,7 @@ usb_function *rndis_qc_bind_config_vendor(struct usb_function_instance *fi, /* export host's Ethernet address in CDC format */ random_ether_addr(rndis_ipa_params.host_ethaddr); random_ether_addr(rndis_ipa_params.device_ethaddr); - pr_debug("setting host_ethaddr=%pKM, device_ethaddr=%pKM\n", + pr_debug("setting host_ethaddr=%pM, device_ethaddr=%pM\n", rndis_ipa_params.host_ethaddr, rndis_ipa_params.device_ethaddr); rndis_ipa_supported = true; diff --git a/drivers/usb/gadget/function/u_data_ipa.c b/drivers/usb/gadget/function/u_data_ipa.c index 83a98f1196f8..4975ba474f97 100644 --- a/drivers/usb/gadget/function/u_data_ipa.c +++ b/drivers/usb/gadget/function/u_data_ipa.c @@ -95,6 +95,7 @@ static void ipa_data_start_endless_xfer(struct ipa_data_ch_info *port, bool in) { unsigned long flags; int status; + struct usb_ep *ep; spin_lock_irqsave(&port->port_lock, flags); if (!port->port_usb || (in && !port->tx_req) @@ -103,18 +104,22 @@ static void ipa_data_start_endless_xfer(struct ipa_data_ch_info *port, bool in) pr_err("%s(): port_usb/req is NULL.\n", __func__); return; } + + if (in) + ep = port->port_usb->in; + else + ep = port->port_usb->out; + spin_unlock_irqrestore(&port->port_lock, flags); if (in) { pr_debug("%s: enqueue endless TX_REQ(IN)\n", __func__); - status = usb_ep_queue(port->port_usb->in, - port->tx_req, GFP_ATOMIC); + status = usb_ep_queue(ep, port->tx_req, GFP_ATOMIC); if (status) pr_err("error enqueuing endless TX_REQ, %d\n", status); } else { pr_debug("%s: enqueue endless RX_REQ(OUT)\n", __func__); - status = usb_ep_queue(port->port_usb->out, - port->rx_req, GFP_ATOMIC); + status = usb_ep_queue(ep, port->rx_req, GFP_ATOMIC); if (status) pr_err("error enqueuing endless RX_REQ, %d\n", status); } @@ -132,6 +137,7 @@ static void ipa_data_stop_endless_xfer(struct ipa_data_ch_info *port, bool in) { unsigned long flags; int status; + struct usb_ep *ep; spin_lock_irqsave(&port->port_lock, flags); if (!port->port_usb || (in && !port->tx_req) @@ -140,16 +146,22 @@ static void ipa_data_stop_endless_xfer(struct ipa_data_ch_info *port, bool in) pr_err("%s(): port_usb/req is NULL.\n", __func__); return; } + + if (in) + ep = port->port_usb->in; + else + ep = port->port_usb->out; + spin_unlock_irqrestore(&port->port_lock, flags); if (in) { pr_debug("%s: dequeue endless TX_REQ(IN)\n", __func__); - status = usb_ep_dequeue(port->port_usb->in, port->tx_req); + status = usb_ep_dequeue(ep, port->tx_req); if (status) pr_err("error dequeueing endless TX_REQ, %d\n", status); } else { pr_debug("%s: dequeue endless RX_REQ(OUT)\n", __func__); - status = usb_ep_dequeue(port->port_usb->out, port->rx_req); + status = usb_ep_dequeue(ep, port->rx_req); if (status) pr_err("error dequeueing endless RX_REQ, %d\n", status); } @@ -164,6 +176,7 @@ void ipa_data_start_rx_tx(enum ipa_func_type func) { struct ipa_data_ch_info *port; unsigned long flags; + struct usb_ep *epin, *epout; pr_debug("%s: Triggered: starting tx, rx", __func__); /* queue in & out requests */ @@ -194,15 +207,17 @@ void ipa_data_start_rx_tx(enum ipa_func_type func) return; } + epout = port->port_usb->out; + epin = port->port_usb->in; spin_unlock_irqrestore(&port->port_lock, flags); /* queue in & out requests */ pr_debug("%s: Starting rx", __func__); - if (port->port_usb->out) + if (epout) ipa_data_start_endless_xfer(port, false); pr_debug("%s: Starting tx", __func__); - if (port->port_usb->in) + if (epin) ipa_data_start_endless_xfer(port, true); } /** diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 70c95d0df581..7244dc9419f3 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -971,8 +971,8 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g, free_netdev(net); dev = ERR_PTR(status); } else { - INFO(dev, "MAC %pKM\n", net->dev_addr); - INFO(dev, "HOST MAC %pKM\n", dev->host_mac); + INFO(dev, "MAC %pM\n", net->dev_addr); + INFO(dev, "HOST MAC %pM\n", dev->host_mac); /* * two kinds of host-initiated state changes: @@ -1040,7 +1040,7 @@ int gether_register_netdev(struct net_device *net) dev_dbg(&g->dev, "register_netdev failed, %d\n", status); return status; } else { - INFO(dev, "HOST MAC %pKM\n", dev->host_mac); + INFO(dev, "HOST MAC %pM\n", dev->host_mac); /* two kinds of host-initiated state changes: * - iff DATA transfer is active, carrier is "on" @@ -1056,7 +1056,7 @@ int gether_register_netdev(struct net_device *net) if (status) pr_warn("cannot set self ethernet address: %d\n", status); else - INFO(dev, "MAC %pKM\n", dev->dev_mac); + INFO(dev, "MAC %pM\n", dev->dev_mac); return status; } @@ -1124,7 +1124,7 @@ int gether_get_host_addr_cdc(struct net_device *net, char *host_addr, int len) return -EINVAL; dev = netdev_priv(net); - snprintf(host_addr, len, "%pKm", dev->host_mac); + snprintf(host_addr, len, "%pM", dev->host_mac); return strlen(host_addr); } diff --git a/drivers/usb/gadget/function/u_qc_ether.c b/drivers/usb/gadget/function/u_qc_ether.c index 2fde4850f8cd..bacaf52f42d9 100644 --- a/drivers/usb/gadget/function/u_qc_ether.c +++ b/drivers/usb/gadget/function/u_qc_ether.c @@ -317,8 +317,8 @@ int gether_qc_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], dev_dbg(&g->dev, "register_netdev failed, %d\n", status); free_netdev(net); } else { - INFO(dev, "MAC %pKM\n", net->dev_addr); - INFO(dev, "HOST MAC %pKM\n", dev->host_mac); + INFO(dev, "MAC %pM\n", net->dev_addr); + INFO(dev, "HOST MAC %pM\n", dev->host_mac); } diff --git a/drivers/usb/phy/phy-msm-qusb.c b/drivers/usb/phy/phy-msm-qusb.c index 170cbf0a853f..6430e419fc9c 100644 --- a/drivers/usb/phy/phy-msm-qusb.c +++ b/drivers/usb/phy/phy-msm-qusb.c @@ -151,6 +151,7 @@ struct qusb_phy { int *emu_dcm_reset_seq; int emu_dcm_reset_seq_len; bool put_into_high_z_state; + struct mutex phy_lock; }; static void qusb_phy_enable_clocks(struct qusb_phy *qphy, bool on) @@ -326,6 +327,7 @@ static int qusb_phy_update_dpdm(struct usb_phy *phy, int value) switch (value) { case POWER_SUPPLY_DP_DM_DPF_DMF: dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DPF_DMF\n"); + mutex_lock(&qphy->phy_lock); if (!qphy->rm_pulldown) { ret = qusb_phy_enable_power(qphy, true); if (ret >= 0) { @@ -372,14 +374,17 @@ static int qusb_phy_update_dpdm(struct usb_phy *phy, int value) /* Make sure that above write is completed */ wmb(); - qusb_phy_enable_clocks(qphy, false); + if (qphy->suspended) + qusb_phy_enable_clocks(qphy, false); } } + mutex_unlock(&qphy->phy_lock); break; case POWER_SUPPLY_DP_DM_DPR_DMR: dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DPR_DMR\n"); + mutex_lock(&qphy->phy_lock); if (qphy->rm_pulldown) { if (!qphy->cable_connected) { if (qphy->tcsr_clamp_dig_n) @@ -394,6 +399,7 @@ static int qusb_phy_update_dpdm(struct usb_phy *phy, int value) qphy->rm_pulldown); } } + mutex_unlock(&qphy->phy_lock); break; default: @@ -707,6 +713,7 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend) qusb_phy_enable_clocks(qphy, false); } else { /* Disconnect case */ + mutex_lock(&qphy->phy_lock); /* Disable all interrupts */ writel_relaxed(0x00, qphy->base + QUSB2PHY_PORT_INTR_CTRL); @@ -723,6 +730,7 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend) qusb_phy_enable_power(qphy, false); else dev_dbg(phy->dev, "race with rm_pulldown. Keep ldo ON\n"); + mutex_unlock(&qphy->phy_lock); /* * Set put_into_high_z_state to true so next USB @@ -1081,6 +1089,7 @@ static int qusb_phy_probe(struct platform_device *pdev) return PTR_ERR(qphy->vdda18); } + mutex_init(&qphy->phy_lock); platform_set_drvdata(pdev, qphy); qphy->phy.label = "msm-qusb-phy"; diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h index 0ab6ae4bb38f..6182aa2c626e 100644 --- a/drivers/video/fbdev/msm/mdss.h +++ b/drivers/video/fbdev/msm/mdss.h @@ -576,12 +576,15 @@ struct mdss_util_intf { int (*iommu_ctrl)(int enable); void (*iommu_lock)(void); void (*iommu_unlock)(void); + void (*vbif_reg_lock)(void); + void (*vbif_reg_unlock)(void); int (*secure_session_ctrl)(int enable); void (*bus_bandwidth_ctrl)(int enable); int (*bus_scale_set_quota)(int client, u64 ab_quota, u64 ib_quota); int (*panel_intf_status)(u32 disp_num, u32 intf_type); struct mdss_panel_cfg* (*panel_intf_type)(int intf_val); int (*dyn_clk_gating_ctrl)(int enable); + bool (*mdp_handoff_pending)(void); }; struct mdss_util_intf *mdss_get_util_intf(void); diff --git a/drivers/video/fbdev/msm/mdss_debug.c b/drivers/video/fbdev/msm/mdss_debug.c index 8d06edf01d1d..3e1dbba7c9ae 100644 --- a/drivers/video/fbdev/msm/mdss_debug.c +++ b/drivers/video/fbdev/msm/mdss_debug.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2009-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2009-2017, 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 @@ -1362,7 +1362,9 @@ static inline struct mdss_mdp_misr_map *mdss_misr_get_map(u32 block_id, (mdata->mdp_rev == MDSS_MDP_HW_REV_301) || (mdata->mdp_rev == - MDSS_MDP_HW_REV_320)) { + MDSS_MDP_HW_REV_320) || + (mdata->mdp_rev == + MDSS_MDP_HW_REV_330)) { ctrl_reg += 0x8; value_reg += 0x8; } diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index aa74a2ec27b2..bd8e710870f7 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -132,7 +132,7 @@ static int mdss_dp_is_clk_prefix(const char *clk_prefix, const char *clk_name) static int mdss_dp_parse_prop(struct platform_device *pdev, struct mdss_dp_drv_pdata *dp_drv) { - int len = 0, i = 0; + int len = 0, i = 0, rc = 0; const char *data; data = of_get_property(pdev->dev.of_node, @@ -160,6 +160,11 @@ static int mdss_dp_parse_prop(struct platform_device *pdev, dp_drv->l_map[i] = data[i]; } + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,max-pclk-frequency-khz", &dp_drv->max_pclk_khz); + if (rc) + dp_drv->max_pclk_khz = DP_MAX_PIXEL_CLK_KHZ; + return 0; } @@ -1476,8 +1481,7 @@ int mdss_dp_on_hpd(struct mdss_dp_drv_pdata *dp_drv) if (ret) goto exit; - if (dp_drv->new_vic && (dp_drv->new_vic != dp_drv->vic)) - dp_init_panel_info(dp_drv, dp_drv->new_vic); + dp_init_panel_info(dp_drv, dp_drv->new_vic); dp_drv->link_rate = mdss_dp_gen_link_clk(dp_drv); if (!dp_drv->link_rate) { @@ -1683,7 +1687,6 @@ static int mdss_dp_edid_init(struct mdss_panel_data *pdata) dp_drv = container_of(pdata, struct mdss_dp_drv_pdata, panel_data); - dp_drv->max_pclk_khz = DP_MAX_PIXEL_CLK_KHZ; edid_init_data.kobj = dp_drv->kobj; edid_init_data.max_pclk_khz = dp_drv->max_pclk_khz; diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c index e75e4e187de0..d57558a1b52d 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_host.c +++ b/drivers/video/fbdev/msm/mdss_dsi_host.c @@ -1437,6 +1437,7 @@ int mdss_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata) { int ret = 0; unsigned long flag; + int ignore_underflow = 0; if (ctrl_pdata == NULL) { pr_err("%s: Invalid input data\n", __func__); @@ -1451,6 +1452,9 @@ int mdss_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata) mutex_lock(&ctrl_pdata->cmd_mutex); + if (ctrl_pdata->panel_mode == DSI_VIDEO_MODE) + ignore_underflow = 1; + pr_debug("%s: Checking BTA status\n", __func__); mdss_dsi_clk_ctrl(ctrl_pdata, ctrl_pdata->dsi_clk_handle, @@ -1459,6 +1463,9 @@ int mdss_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata) reinit_completion(&ctrl_pdata->bta_comp); mdss_dsi_enable_irq(ctrl_pdata, DSI_BTA_TERM); spin_unlock_irqrestore(&ctrl_pdata->mdp_lock, flag); + /* mask out overflow errors */ + if (ignore_underflow) + mdss_dsi_set_reg(ctrl_pdata, 0x10c, 0x0f0000, 0x0f0000); MIPI_OUTP(ctrl_pdata->ctrl_base + 0x098, 0x01); /* trigger */ wmb(); @@ -1469,6 +1476,13 @@ int mdss_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata) pr_err("%s: DSI BTA error: %i\n", __func__, ret); } + if (ignore_underflow) { + /* clear pending overflow status */ + mdss_dsi_set_reg(ctrl_pdata, 0xc, 0xffffffff, 0x44440000); + /* restore overflow isr */ + mdss_dsi_set_reg(ctrl_pdata, 0x10c, 0x0f0000, 0); + } + mdss_dsi_clk_ctrl(ctrl_pdata, ctrl_pdata->dsi_clk_handle, MDSS_DSI_ALL_CLKS, MDSS_DSI_CLK_OFF); pr_debug("%s: BTA done with ret: %d\n", __func__, ret); diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c index 5804d88e5af5..f05d4cb2922a 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c @@ -1251,7 +1251,6 @@ static ssize_t hdmi_tx_sysfs_wta_hdr_stream(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int ret = 0; - u32 const hdr_param_count = 13; struct hdmi_tx_ctrl *ctrl = NULL; ctrl = hdmi_tx_get_drvdata_from_sysfs_dev(dev); @@ -1267,26 +1266,14 @@ static ssize_t hdmi_tx_sysfs_wta_hdr_stream(struct device *dev, goto end; } - if (sscanf(buf, "%u %u %u %u %u %u %u %u %u %u %u %u %u", - &ctrl->hdr_data.eotf, - &ctrl->hdr_data.display_primaries_x[0], - &ctrl->hdr_data.display_primaries_y[0], - &ctrl->hdr_data.display_primaries_x[1], - &ctrl->hdr_data.display_primaries_y[1], - &ctrl->hdr_data.display_primaries_x[2], - &ctrl->hdr_data.display_primaries_y[2], - &ctrl->hdr_data.white_point_x, - &ctrl->hdr_data.white_point_y, - &ctrl->hdr_data.max_luminance, - &ctrl->hdr_data.min_luminance, - &ctrl->hdr_data.max_content_light_level, - &ctrl->hdr_data.max_average_light_level) - != hdr_param_count) { - pr_err("%s: Invalid HDR stream data\n", __func__); + if (buf == NULL) { + pr_err("%s: hdr stream is NULL\n", __func__); ret = -EINVAL; goto end; } + memcpy(&ctrl->hdr_data, buf, sizeof(struct mdp_hdr_stream)); + pr_debug("%s: 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n", __func__, ctrl->hdr_data.eotf, @@ -1306,6 +1293,14 @@ static ssize_t hdmi_tx_sysfs_wta_hdr_stream(struct device *dev, ctrl->hdr_data.max_content_light_level, ctrl->hdr_data.max_average_light_level); + pr_debug("%s: 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n", + __func__, + ctrl->hdr_data.pixel_encoding, + ctrl->hdr_data.colorimetry, + ctrl->hdr_data.range, + ctrl->hdr_data.bits_per_component, + ctrl->hdr_data.content_type); + hdmi_panel_set_hdr_infoframe(ctrl); ret = strnlen(buf, PAGE_SIZE); diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.h b/drivers/video/fbdev/msm/mdss_hdmi_tx.h index ca316a350238..3469b8a5819f 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2017, 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 @@ -14,13 +14,13 @@ #define __MDSS_HDMI_TX_H__ #include <linux/switch.h> +#include <linux/msm_mdp_ext.h> #include "mdss_hdmi_util.h" #include "mdss_hdmi_panel.h" #include "mdss_cec_core.h" #include "mdss_hdmi_audio.h" #define MAX_SWITCH_NAME_SIZE 5 -#define HDR_PRIMARIES_COUNT 3 enum hdmi_tx_io_type { HDMI_TX_CORE_IO, @@ -62,30 +62,6 @@ struct hdmi_tx_pinctrl { struct hdmi_tx_ctrl; typedef int (*hdmi_tx_evt_handler) (struct hdmi_tx_ctrl *); -/* - * struct hdmi_tx_hdr_stream - HDR video stream characteristics - * @eotf: Electro-Optical Transfer Function - * @display_primaries_x: display primaries data for x-coordinate - * @display_primaries_y: display primaries data for y-coordinate - * @white_point_x: white point data for x-coordinate - * @white_point_y: white point data for y-coordinate - * @max_luminance: content maximum luminance - * @min_luminance: content minimum luminance - * @max_content_light_level: content maximum light level - * @max_average_light_level: content average light level - */ -struct hdmi_tx_hdr_stream_data { - u32 eotf; - u32 display_primaries_x[HDR_PRIMARIES_COUNT]; - u32 display_primaries_y[HDR_PRIMARIES_COUNT]; - u32 white_point_x; - u32 white_point_y; - u32 max_luminance; - u32 min_luminance; - u32 max_content_light_level; - u32 max_average_light_level; -}; - struct hdmi_tx_ctrl { struct platform_device *pdev; struct platform_device *ext_pdev; @@ -114,7 +90,7 @@ struct hdmi_tx_ctrl { struct msm_ext_disp_audio_setup_params audio_params; struct msm_ext_disp_init_data ext_audio_data; struct work_struct fps_work; - struct hdmi_tx_hdr_stream_data hdr_data; + struct mdp_hdr_stream hdr_data; spinlock_t hpd_state_lock; diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index 4ee22abda4a2..d8d11f21f3b2 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -784,6 +784,26 @@ int mdss_update_reg_bus_vote(struct reg_bus_client *bus_client, u32 usecase_ndx) } #endif +void mdss_mdp_vbif_reg_lock(void) +{ + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + + mutex_lock(&mdata->reg_lock); +} + +void mdss_mdp_vbif_reg_unlock(void) +{ + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + + mutex_unlock(&mdata->reg_lock); +} + +bool mdss_mdp_handoff_pending(void) +{ + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + + return mdata->handoff_pending; +} static int mdss_mdp_intr2index(u32 intr_type, u32 intf_num) { @@ -2110,6 +2130,7 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata) mdss_set_quirk(mdata, MDSS_QUIRK_HDR_SUPPORT_ENABLED); break; case MDSS_MDP_HW_REV_320: + case MDSS_MDP_HW_REV_330: mdata->max_target_zorder = 7; /* excluding base layer */ mdata->max_cursor_size = 512; mdata->per_pipe_ib_factor.numer = 8; @@ -2118,7 +2139,9 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata) mdata->hflip_buffer_reused = false; mdata->min_prefill_lines = 25; mdata->has_ubwc = true; - mdata->pixel_ram_size = 50 * 1024; + mdata->pixel_ram_size = + (mdata->mdp_rev == MDSS_MDP_HW_REV_320) ? 50 : 40; + mdata->pixel_ram_size *= 1024; mdata->rects_per_sspp[MDSS_MDP_PIPE_TYPE_DMA] = 2; mem_protect_sd_ctrl_id = MEM_PROTECT_SD_CTRL_SWITCH; @@ -2869,6 +2892,9 @@ static int mdss_mdp_probe(struct platform_device *pdev) mdss_res->mdss_util->bus_bandwidth_ctrl = mdss_bus_bandwidth_ctrl; mdss_res->mdss_util->panel_intf_type = mdss_panel_intf_type; mdss_res->mdss_util->panel_intf_status = mdss_panel_get_intf_status; + mdss_res->mdss_util->vbif_reg_lock = mdss_mdp_vbif_reg_lock; + mdss_res->mdss_util->vbif_reg_unlock = mdss_mdp_vbif_reg_unlock; + mdss_res->mdss_util->mdp_handoff_pending = mdss_mdp_handoff_pending; rc = msm_dss_ioremap_byname(pdev, &mdata->mdss_io, "mdp_phys"); if (rc) { @@ -4853,6 +4879,7 @@ static void apply_dynamic_ot_limit(u32 *ot_lim, *ot_lim = 6; break; case MDSS_MDP_HW_REV_320: + case MDSS_MDP_HW_REV_330: if ((res <= RES_1080p) && (params->frame_rate <= 30)) *ot_lim = 2; else if ((res <= RES_1080p) && (params->frame_rate <= 60)) diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index 3542573b72ed..5e98de043e55 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -1299,7 +1299,9 @@ static inline int mdss_mdp_panic_signal_support_mode( IS_MDSS_MAJOR_MINOR_SAME(mdata->mdp_rev, MDSS_MDP_HW_REV_300) || IS_MDSS_MAJOR_MINOR_SAME(mdata->mdp_rev, - MDSS_MDP_HW_REV_320)) + MDSS_MDP_HW_REV_320) || + IS_MDSS_MAJOR_MINOR_SAME(mdata->mdp_rev, + MDSS_MDP_HW_REV_330)) signal_mode = MDSS_MDP_PANIC_PER_PIPE_CFG; return signal_mode; diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c index 2a62ae5881b3..c97a58c86c29 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c @@ -1981,22 +1981,34 @@ int mdss_mdp_cmd_reconfigure_splash_done(struct mdss_mdp_ctl *ctl, bool handoff) { struct mdss_panel_data *pdata; - struct mdss_mdp_ctl *sctl = mdss_mdp_get_split_ctl(ctl); + struct mdss_mdp_ctl *sctl = NULL; + struct mdss_mdp_cmd_ctx *sctx = NULL; struct dsi_panel_clk_ctrl clk_ctrl; int ret = 0; + /* Get both controllers in the correct order for dual displays */ + mdss_mdp_get_split_display_ctls(&ctl, &sctl); + + if (sctl) + sctx = (struct mdss_mdp_cmd_ctx *) sctl->intf_ctx[MASTER_CTX]; + + /* In pingpong split we have single controller, dual context */ + if (is_pingpong_split(ctl->mfd)) + sctx = (struct mdss_mdp_cmd_ctx *) ctl->intf_ctx[SLAVE_CTX]; + pdata = ctl->panel_data; clk_ctrl.state = MDSS_DSI_CLK_OFF; clk_ctrl.client = DSI_CLK_REQ_MDP_CLIENT; - if (sctl) { + + if (sctx) { /* then slave */ u32 flags = CTL_INTF_EVENT_FLAG_SKIP_BROADCAST; - if (is_pingpong_split(sctl->mfd)) + if (sctx->pingpong_split_slave) flags |= CTL_INTF_EVENT_FLAG_SLAVE_INTF; - mdss_mdp_ctl_intf_event(sctl, MDSS_EVENT_PANEL_CLK_CTRL, - (void *)&clk_ctrl, flags); + mdss_mdp_ctl_intf_event(sctx->ctl, MDSS_EVENT_PANEL_CLK_CTRL, + (void *)&clk_ctrl, flags); } mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_PANEL_CLK_CTRL, diff --git a/drivers/video/fbdev/msm/mdss_mdp_pipe.c b/drivers/video/fbdev/msm/mdss_mdp_pipe.c index bd41cb9e025c..965a6533dfcb 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pipe.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pipe.c @@ -2410,9 +2410,10 @@ bool mdss_mdp_is_amortizable_pipe(struct mdss_mdp_pipe *pipe, (mixer->type == MDSS_MDP_MIXER_TYPE_INTF))) return false; - /* do not apply for sdm660 in command mode */ - if ((IS_MDSS_MAJOR_MINOR_SAME(mdata->mdp_rev, - MDSS_MDP_HW_REV_320)) && !mixer->ctl->is_video_mode) + /* do not apply for sdm660 & sdm630 in command mode */ + if ((IS_MDSS_MAJOR_MINOR_SAME(mdata->mdp_rev, MDSS_MDP_HW_REV_320) || + IS_MDSS_MAJOR_MINOR_SAME(mdata->mdp_rev, MDSS_MDP_HW_REV_330)) + && !mixer->ctl->is_video_mode) return false; return true; diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c index 7212de8a43f9..38bff6b8edee 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c @@ -1124,6 +1124,7 @@ static int pp_vig_pipe_setup(struct mdss_mdp_pipe *pipe, u32 *op) mdata = mdss_mdp_get_mdata(); if (IS_MDSS_MAJOR_MINOR_SAME(mdata->mdp_rev, MDSS_MDP_HW_REV_320) || + IS_MDSS_MAJOR_MINOR_SAME(mdata->mdp_rev, MDSS_MDP_HW_REV_330) || IS_MDSS_MAJOR_MINOR_SAME(mdata->mdp_rev, MDSS_MDP_HW_REV_301) || IS_MDSS_MAJOR_MINOR_SAME(mdata->mdp_rev, MDSS_MDP_HW_REV_300)) { if (pipe->src_fmt->is_yuv) { @@ -7666,6 +7667,7 @@ static int pp_get_driver_ops(struct mdp_pp_driver_ops *ops) case MDSS_MDP_HW_REV_300: case MDSS_MDP_HW_REV_301: case MDSS_MDP_HW_REV_320: + case MDSS_MDP_HW_REV_330: /* * Some of the REV_300 PP features are same as REV_107. * Get the driver ops for both the versions and update the diff --git a/drivers/video/fbdev/msm/mdss_rotator.c b/drivers/video/fbdev/msm/mdss_rotator.c index d001f148b443..0307d570ff64 100644 --- a/drivers/video/fbdev/msm/mdss_rotator.c +++ b/drivers/video/fbdev/msm/mdss_rotator.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, 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 @@ -1034,6 +1034,12 @@ static int mdss_rotator_calc_perf(struct mdss_rot_perf *perf) pr_err("invalid output format\n"); return -EINVAL; } + if (!config->input.width || + (0xffffffff/config->input.width < config->input.height)) + return -EINVAL; + if (!perf->clk_rate || + (0xffffffff/perf->clk_rate < config->frame_rate)) + return -EINVAL; perf->clk_rate = config->input.width * config->input.height; perf->clk_rate *= config->frame_rate; diff --git a/drivers/video/fbdev/msm/mdss_smmu.c b/drivers/video/fbdev/msm/mdss_smmu.c index 7ca0cd37d43c..6930444118b6 100644 --- a/drivers/video/fbdev/msm/mdss_smmu.c +++ b/drivers/video/fbdev/msm/mdss_smmu.c @@ -845,9 +845,12 @@ int mdss_smmu_probe(struct platform_device *pdev) } mdss_smmu->base.iommu_ctrl = mdata->mdss_util->iommu_ctrl; + mdss_smmu->base.reg_lock = mdata->mdss_util->vbif_reg_lock; + mdss_smmu->base.reg_unlock = mdata->mdss_util->vbif_reg_unlock; mdss_smmu->base.secure_session_ctrl = mdata->mdss_util->secure_session_ctrl; mdss_smmu->base.wait_for_transition = mdss_smmu_secure_wait; + mdss_smmu->base.handoff_pending = mdata->mdss_util->mdp_handoff_pending; list_add(&mdss_smmu->_client, &prv->smmu_device_list); @@ -256,6 +256,7 @@ static int __init aio_setup(void) aio_mnt = kern_mount(&aio_fs); if (IS_ERR(aio_mnt)) panic("Failed to create aio fs mount."); + aio_mnt->mnt_flags |= MNT_NOEXEC; kiocb_cachep = KMEM_CACHE(aio_kiocb, SLAB_HWCACHE_ALIGN|SLAB_PANIC); kioctx_cachep = KMEM_CACHE(kioctx,SLAB_HWCACHE_ALIGN|SLAB_PANIC); diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 5f83efb824a3..0443e06d1902 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -421,6 +421,10 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req) static void queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req) { spin_lock(&fiq->waitq.lock); + if (test_bit(FR_FINISHED, &req->flags)) { + spin_unlock(&fiq->waitq.lock); + return; + } if (list_empty(&req->intr_entry)) { list_add_tail(&req->intr_entry, &fiq->interrupts); wake_up_locked(&fiq->waitq); diff --git a/fs/proc/base.c b/fs/proc/base.c index fed79c45b54c..67c6dd28d59e 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1018,15 +1018,20 @@ static ssize_t oom_adj_read(struct file *file, char __user *buf, size_t count, int oom_adj = OOM_ADJUST_MIN; size_t len; unsigned long flags; + int mult = 1; if (!task) return -ESRCH; if (lock_task_sighand(task, &flags)) { - if (task->signal->oom_score_adj == OOM_SCORE_ADJ_MAX) + if (task->signal->oom_score_adj == OOM_SCORE_ADJ_MAX) { oom_adj = OOM_ADJUST_MAX; - else - oom_adj = (task->signal->oom_score_adj * -OOM_DISABLE) / - OOM_SCORE_ADJ_MAX; + } else { + if (task->signal->oom_score_adj < 0) + mult = -1; + oom_adj = roundup(mult * task->signal->oom_score_adj * + -OOM_DISABLE, OOM_SCORE_ADJ_MAX) / + OOM_SCORE_ADJ_MAX * mult; + } unlock_task_sighand(task, &flags); } put_task_struct(task); diff --git a/include/linux/mdss_smmu_ext.h b/include/linux/mdss_smmu_ext.h index 414ab055595a..12ad4305f145 100644 --- a/include/linux/mdss_smmu_ext.h +++ b/include/linux/mdss_smmu_ext.h @@ -22,6 +22,7 @@ * @iommu_ctrl: iommu ctrl function for enable/disable attach. * @secure_session_ctrl: ctrl function for enable/disable session. * @wait_for_transition:function to wait till secure transtion is complete. + * @reg_lock /reg_unlock: Lock to access shared registers. */ struct mdss_smmu_intf { struct device *dev; @@ -30,6 +31,9 @@ struct mdss_smmu_intf { int (*iommu_ctrl)(int); int (*secure_session_ctrl)(int); int (*wait_for_transition)(int state, int request); + void (*reg_lock)(void); + void (*reg_unlock)(void); + bool (*handoff_pending)(void); }; typedef void (*msm_smmu_handler_t) (struct mdss_smmu_intf *smmu); diff --git a/include/linux/qcom_tspp.h b/include/linux/qcom_tspp.h index 28e6695fb057..1b34c389d7f0 100644 --- a/include/linux/qcom_tspp.h +++ b/include/linux/qcom_tspp.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, 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 @@ -70,6 +70,11 @@ struct tspp_select_source { int enable_inverse; }; +enum tsif_tts_source { + TSIF_TTS_TCR = 0, /* Time stamps from TCR counter */ + TSIF_TTS_LPASS_TIMER /* Time stamps from AV/Qtimer Timer */ +}; + typedef void (tspp_notifier)(int channel_id, void *user); typedef void* (tspp_allocator)(int channel_id, u32 size, phys_addr_t *phys_base, void *user); @@ -96,4 +101,8 @@ int tspp_allocate_buffers(u32 dev, u32 channel_id, u32 count, u32 size, u32 int_freq, tspp_allocator *alloc, tspp_memfree *memfree, void *user); +int tspp_get_tts_source(u32 dev, int *tts_source); +int tspp_get_lpass_time_counter(u32 dev, enum tspp_source source, + u64 *lpass_time_counter); + #endif /* _MSM_TSPP_H_ */ diff --git a/include/linux/thermal.h b/include/linux/thermal.h index b90f8f5c663d..4d2cf47aba27 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -178,6 +178,7 @@ struct sensor_info { struct work_struct work; struct task_struct *sysfs_notify_thread; struct completion sysfs_notify_complete; + bool deregister_active; }; /** diff --git a/include/soc/qcom/qseecomi.h b/include/soc/qcom/qseecomi.h index e33fd9fc1841..6497d962e347 100644 --- a/include/soc/qcom/qseecomi.h +++ b/include/soc/qcom/qseecomi.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2017, 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 @@ -511,6 +511,9 @@ __packed struct qseecom_continue_blocked_request_ireq { #define TZ_OS_REGISTER_LISTENER_ID \ TZ_SYSCALL_CREATE_SMC_ID(TZ_OWNER_QSEE_OS, TZ_SVC_LISTENER, 0x01) +#define TZ_OS_REGISTER_LISTENER_SMCINVOKE_ID \ + TZ_SYSCALL_CREATE_SMC_ID(TZ_OWNER_QSEE_OS, TZ_SVC_LISTENER, 0x06) + #define TZ_OS_REGISTER_LISTENER_ID_PARAM_ID \ TZ_SYSCALL_CREATE_PARAM_ID_3( \ TZ_SYSCALL_PARAM_TYPE_VAL, TZ_SYSCALL_PARAM_TYPE_BUF_RW, \ diff --git a/include/uapi/linux/dvb/dmx.h b/include/uapi/linux/dvb/dmx.h index a768696c90f8..175534a26792 100644 --- a/include/uapi/linux/dvb/dmx.h +++ b/include/uapi/linux/dvb/dmx.h @@ -148,6 +148,9 @@ enum dmx_video_codec { #define DMX_IDX_VC1_FRAME_END 0x02000000 #define DMX_IDX_H264_ACCESS_UNIT_DEL 0x04000000 #define DMX_IDX_H264_SEI 0x08000000 +#define DMX_IDX_H264_IDR_ISLICE_START 0x10000000 +#define DMX_IDX_H264_NON_IDR_PSLICE_START 0x20000000 +#define DMX_IDX_H264_NON_IDR_BSLICE_START 0x40000000 struct dmx_pes_filter_params { diff --git a/include/uapi/linux/msm_ipa.h b/include/uapi/linux/msm_ipa.h index 6aa021e12930..16e4b8e30b07 100644 --- a/include/uapi/linux/msm_ipa.h +++ b/include/uapi/linux/msm_ipa.h @@ -147,7 +147,9 @@ enum ipa_client_type { IPA_CLIENT_A5_WLAN_AMPDU_PROD, IPA_CLIENT_A2_EMBEDDED_PROD, IPA_CLIENT_A2_TETHERED_PROD, - IPA_CLIENT_APPS_LAN_WAN_PROD, + IPA_CLIENT_APPS_LAN_PROD, + IPA_CLIENT_APPS_WAN_PROD, + IPA_CLIENT_APPS_LAN_WAN_PROD = IPA_CLIENT_APPS_WAN_PROD, IPA_CLIENT_APPS_CMD_PROD, IPA_CLIENT_ODU_PROD, IPA_CLIENT_MHI_PROD, diff --git a/include/uapi/linux/msm_mdp.h b/include/uapi/linux/msm_mdp.h index fca2a3c2d494..481814cb8498 100644 --- a/include/uapi/linux/msm_mdp.h +++ b/include/uapi/linux/msm_mdp.h @@ -119,6 +119,7 @@ #define MDSS_MDP_HW_REV_300 MDSS_MDP_REV(3, 0, 0) /* msm8998 */ #define MDSS_MDP_HW_REV_301 MDSS_MDP_REV(3, 0, 1) /* msm8998 v1.0 */ #define MDSS_MDP_HW_REV_320 MDSS_MDP_REV(3, 2, 0) /* sdm660 */ +#define MDSS_MDP_HW_REV_330 MDSS_MDP_REV(3, 3, 0) /* sdm630 */ enum { NOTIFY_UPDATE_INIT, diff --git a/include/uapi/linux/msm_mdp_ext.h b/include/uapi/linux/msm_mdp_ext.h index 3bd8bea8ae0d..ee68675bfe13 100644 --- a/include/uapi/linux/msm_mdp_ext.h +++ b/include/uapi/linux/msm_mdp_ext.h @@ -179,6 +179,77 @@ VALIDATE/COMMIT FLAG CONFIGURATION #define OUT_LAYER_COLOR_SPACE +/* From CEA.861.3 */ +#define MDP_HDR_EOTF_SMTPE_ST2084 0x2 +#define MDP_HDR_EOTF_HLG 0x3 + +/* From Vesa DPv1.4 - Pixel Encoding - Table 2-120 */ +#define MDP_PIXEL_ENCODING_RGB 0x0 +#define MDP_PIXEL_ENCODING_YCBCR_444 0x1 +#define MDP_PIXEL_ENCODING_YCBCR_422 0x2 +#define MDP_PIXEL_ENCODING_YCBCR_420 0x3 +#define MDP_PIXEL_ENCODING_Y_ONLY 0x4 +#define MDP_PIXEL_ENCODING_RAW 0x5 + +/* From Vesa DPv1.4 - Colorimetry Formats - Table 2-120 */ +/* RGB - used with MDP_DP_PIXEL_ENCODING_RGB */ +#define MDP_COLORIMETRY_RGB_SRGB 0x0 +#define MDP_COLORIMETRY_RGB_WIDE_FIXED_POINT 0x1 +#define MDP_COLORIMETRY_RGB_WIDE_FLOAT_POINT 0x2 +#define MDP_COLORIMETRY_RGB_ADOBE 0x3 +#define MDP_COLORIMETRY_RGB_DPI_P3 0x4 +#define MDP_COLORIMETRY_RGB_CUSTOM 0x5 +#define MDP_COLORIMETRY_RGB_ITU_R_BT_2020 0x6 + +/* YUV - used with MDP_DP_PIXEL_ENCODING_YCBCR(444 or 422 or 420) */ +#define MDP_COLORIMETRY_YCBCR_ITU_R_BT_601 0x0 +#define MDP_COLORIMETRY_YCBCR_ITU_R_BT_709 0x1 +#define MDP_COLORIMETRY_YCBCR_XV_YCC_601 0x2 +#define MDP_COLORIMETRY_YCBCR_XV_YCC_709 0x3 +#define MDP_COLORIMETRY_YCBCR_S_YCC_601 0x4 +#define MDP_COLORIMETRY_YCBCR_ADOBE_YCC_601 0x5 +#define MDP_COLORIMETRY_YCBCR_ITU_R_BT_2020_YCBCR_CONST 0x6 +#define MDP_COLORIMETRY_YCBCR_ITU_R_BT_2020_YCBCR 0x7 + +/* Dynamic Range - Table 2-120 */ +/* Full range */ +#define MDP_DYNAMIC_RANGE_VESA 0x0 +/* Limited range */ +#define MDP_DYNAMIC_RANGE_CEA 0x1 + +/* Bits per component(bpc) for Pixel encoding format RGB from Table 2-120 */ +#define MDP_RGB_6_BPC 0x0 +#define MDP_RGB_8_BPC 0x1 +#define MDP_RGB_10_BPC 0x2 +#define MDP_RGB_12_BPC 0x3 +#define MDP_RGB_16_BPC 0x4 + +/* + * Bits per component(bpc) for Pixel encoding format YCbCr444, YCbCr422, + * YCbCr420 and Y only + * from Table 2-120 + */ +#define MDP_YUV_8_BPC 0x1 +#define MDP_YUV_10_BPC 0x2 +#define MDP_YUV_12_BPC 0x3 +#define MDP_YUV_16_BPC 0x4 + +/* Bits per component(bpc) for Pixel encoding format RAW from Table 2-120 */ +#define MDP_RAW_6_BPC 0x1 +#define MDP_RAW_7_BPC 0x2 +#define MDP_RAW_8_BPC 0x3 +#define MDP_RAW_10_BPC 0x4 +#define MDP_RAW_12_BPC 0x5 +#define MDP_RAW_14_BPC 0x6 +#define MDP_RAW16_BPC 0x7 + +/* Content Type - Table 2-120 */ +#define MDP_CONTENT_TYPE_NOT_DEFINED 0x0 +#define MDP_CONTENT_TYPE_GRAPHICS 0x1 +#define MDP_CONTENT_TYPE_PHOTO 0x2 +#define MDP_CONTENT_TYPE_VIDEO 0x3 +#define MDP_CONTENT_TYPE_GAME 0x4 + /********************************************************************** Configuration structures All parameters are input to driver unless mentioned output parameter @@ -709,4 +780,27 @@ struct mdp_set_cfg { uint32_t len; uint64_t __user payload; }; + +#define HDR_PRIMARIES_COUNT 3 + +#define MDP_HDR_STREAM + +struct mdp_hdr_stream { + uint32_t eotf; + uint32_t display_primaries_x[HDR_PRIMARIES_COUNT]; + uint32_t display_primaries_y[HDR_PRIMARIES_COUNT]; + uint32_t white_point_x; + uint32_t white_point_y; + uint32_t max_luminance; + uint32_t min_luminance; + uint32_t max_content_light_level; + uint32_t max_average_light_level; + /* DP related */ + uint32_t pixel_encoding; + uint32_t colorimetry; + uint32_t range; + uint32_t bits_per_component; + uint32_t content_type; + uint32_t reserved[5]; +}; #endif diff --git a/kernel/power/qos.c b/kernel/power/qos.c index 582b66e882ce..e6eceb0aa496 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -597,7 +597,11 @@ void pm_qos_add_request(struct pm_qos_request *req, case PM_QOS_REQ_AFFINE_IRQ: if (irq_can_set_affinity(req->irq)) { struct irq_desc *desc = irq_to_desc(req->irq); - struct cpumask *mask = desc->irq_data.common->affinity; + struct cpumask *mask; + + if (!desc) + return; + mask = desc->irq_data.common->affinity; /* Get the current affinity */ cpumask_copy(&req->cpus_affine, mask); diff --git a/kernel/sched/core_ctl.c b/kernel/sched/core_ctl.c index aac12bfc2ae6..133b412eabc7 100644 --- a/kernel/sched/core_ctl.c +++ b/kernel/sched/core_ctl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, 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 @@ -36,7 +36,7 @@ struct cluster_data { cpumask_t cpu_mask; unsigned int need_cpus; unsigned int task_thres; - s64 last_isolate_ts; + s64 need_ts; struct list_head lru; bool pending; spinlock_t pending_lock; @@ -549,6 +549,7 @@ static bool eval_need(struct cluster_data *cluster) bool need_flag = false; unsigned int active_cpus; unsigned int new_need; + s64 now; if (unlikely(!cluster->inited)) return 0; @@ -573,9 +574,10 @@ static bool eval_need(struct cluster_data *cluster) need_flag = adjustment_possible(cluster, new_need); last_need = cluster->need_cpus; - cluster->need_cpus = new_need; + now = ktime_to_ms(ktime_get()); - if (!need_flag) { + if (new_need == last_need) { + cluster->need_ts = now; spin_unlock_irqrestore(&state_lock, flags); return 0; } @@ -583,12 +585,15 @@ static bool eval_need(struct cluster_data *cluster) if (need_cpus > cluster->active_cpus) { ret = 1; } else if (need_cpus < cluster->active_cpus) { - s64 now = ktime_to_ms(ktime_get()); - s64 elapsed = now - cluster->last_isolate_ts; + s64 elapsed = now - cluster->need_ts; ret = elapsed >= cluster->offline_delay_ms; } + if (ret) { + cluster->need_ts = now; + cluster->need_cpus = new_need; + } trace_core_ctl_eval_need(cluster->first_cpu, last_need, need_cpus, ret && need_flag); spin_unlock_irqrestore(&state_lock, flags); @@ -746,7 +751,6 @@ static void try_to_isolate(struct cluster_data *cluster, unsigned int need) if (!sched_isolate_cpu(c->cpu)) { c->isolated_by_us = true; move_cpu_lru(c); - cluster->last_isolate_ts = ktime_to_ms(ktime_get()); } else { pr_debug("Unable to isolate CPU%u\n", c->cpu); } @@ -779,7 +783,6 @@ static void try_to_isolate(struct cluster_data *cluster, unsigned int need) if (!sched_isolate_cpu(c->cpu)) { c->isolated_by_us = true; move_cpu_lru(c); - cluster->last_isolate_ts = ktime_to_ms(ktime_get()); } else { pr_debug("Unable to isolate CPU%u\n", c->cpu); } diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 6f68b0e19c4a..00bbd91d6767 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -2620,7 +2620,6 @@ struct cpu_select_env { u8 need_idle:1; u8 need_waker_cluster:1; u8 sync:1; - u8 ignore_prev_cpu:1; enum sched_boost_policy boost_policy; u8 pack_task:1; int prev_cpu; @@ -2630,6 +2629,7 @@ struct cpu_select_env { u64 cpu_load; u32 sbc_best_flag; u32 sbc_best_cluster_flag; + struct cpumask search_cpus; }; struct cluster_cpu_stats { @@ -2834,11 +2834,14 @@ struct cpu_select_env *env, struct cluster_cpu_stats *stats) { struct sched_cluster *next = NULL; int i; + struct cpumask search_cpus; while (!bitmap_empty(env->backup_list, num_clusters)) { next = next_candidate(env->backup_list, 0, num_clusters); __clear_bit(next->id, env->backup_list); - for_each_cpu_and(i, &env->p->cpus_allowed, &next->cpus) { + + cpumask_and(&search_cpus, &env->search_cpus, &next->cpus); + for_each_cpu(i, &search_cpus) { trace_sched_cpu_load_wakeup(cpu_rq(i), idle_cpu(i), sched_irqload(i), power_cost(i, task_load(env->p) + cpu_cravg_sync(i, env->sync)), 0); @@ -3010,11 +3013,7 @@ static void find_best_cpu_in_cluster(struct sched_cluster *c, int i; struct cpumask search_cpus; - cpumask_and(&search_cpus, tsk_cpus_allowed(env->p), &c->cpus); - cpumask_andnot(&search_cpus, &search_cpus, cpu_isolated_mask); - - if (env->ignore_prev_cpu) - cpumask_clear_cpu(env->prev_cpu, &search_cpus); + cpumask_and(&search_cpus, &env->search_cpus, &c->cpus); env->need_idle = wake_to_idle(env->p) || c->wake_up_idle; @@ -3026,7 +3025,7 @@ static void find_best_cpu_in_cluster(struct sched_cluster *c, power_cost(i, task_load(env->p) + cpu_cravg_sync(i, env->sync)), 0); - if (unlikely(!cpu_active(i)) || skip_cpu(i, env)) + if (skip_cpu(i, env)) continue; update_spare_capacity(stats, env, i, c->capacity, @@ -3081,9 +3080,7 @@ bias_to_prev_cpu(struct cpu_select_env *env, struct cluster_cpu_stats *stats) return false; prev_cpu = env->prev_cpu; - if (!cpumask_test_cpu(prev_cpu, tsk_cpus_allowed(task)) || - unlikely(!cpu_active(prev_cpu)) || - cpu_isolated(prev_cpu)) + if (!cpumask_test_cpu(prev_cpu, &env->search_cpus)) return false; if (task->ravg.mark_start - task->last_cpu_selected_ts >= @@ -3116,7 +3113,7 @@ bias_to_prev_cpu(struct cpu_select_env *env, struct cluster_cpu_stats *stats) spill_threshold_crossed(env, cpu_rq(prev_cpu))) { update_spare_capacity(stats, env, prev_cpu, cluster->capacity, env->cpu_load); - env->ignore_prev_cpu = 1; + cpumask_clear_cpu(prev_cpu, &env->search_cpus); return false; } @@ -3132,23 +3129,17 @@ wake_to_waker_cluster(struct cpu_select_env *env) } static inline bool -bias_to_waker_cpu(struct task_struct *p, int cpu) +bias_to_waker_cpu(struct cpu_select_env *env, int cpu) { return sysctl_sched_prefer_sync_wakee_to_waker && cpu_rq(cpu)->nr_running == 1 && - cpumask_test_cpu(cpu, tsk_cpus_allowed(p)) && - cpu_active(cpu) && !cpu_isolated(cpu); + cpumask_test_cpu(cpu, &env->search_cpus); } static inline int -cluster_allowed(struct task_struct *p, struct sched_cluster *cluster) +cluster_allowed(struct cpu_select_env *env, struct sched_cluster *cluster) { - cpumask_t tmp_mask; - - cpumask_and(&tmp_mask, &cluster->cpus, cpu_active_mask); - cpumask_and(&tmp_mask, &tmp_mask, &p->cpus_allowed); - - return !cpumask_empty(&tmp_mask); + return cpumask_intersects(&env->search_cpus, &cluster->cpus); } /* return cheapest cpu that can fit this task */ @@ -3169,7 +3160,6 @@ static int select_best_cpu(struct task_struct *p, int target, int reason, .need_waker_cluster = 0, .sync = sync, .prev_cpu = target, - .ignore_prev_cpu = 0, .rtg = NULL, .sbc_best_flag = 0, .sbc_best_cluster_flag = 0, @@ -3182,6 +3172,9 @@ static int select_best_cpu(struct task_struct *p, int target, int reason, bitmap_copy(env.candidate_list, all_cluster_ids, NR_CPUS); bitmap_zero(env.backup_list, NR_CPUS); + cpumask_and(&env.search_cpus, tsk_cpus_allowed(p), cpu_active_mask); + cpumask_andnot(&env.search_cpus, &env.search_cpus, cpu_isolated_mask); + init_cluster_cpu_stats(&stats); special = env_has_special_flags(&env); @@ -3191,19 +3184,19 @@ static int select_best_cpu(struct task_struct *p, int target, int reason, if (grp && grp->preferred_cluster) { pref_cluster = grp->preferred_cluster; - if (!cluster_allowed(p, pref_cluster)) + if (!cluster_allowed(&env, pref_cluster)) clear_bit(pref_cluster->id, env.candidate_list); else env.rtg = grp; } else if (!special) { cluster = cpu_rq(cpu)->cluster; if (wake_to_waker_cluster(&env)) { - if (bias_to_waker_cpu(p, cpu)) { + if (bias_to_waker_cpu(&env, cpu)) { target = cpu; sbc_flag = SBC_FLAG_WAKER_CLUSTER | SBC_FLAG_WAKER_CPU; goto out; - } else if (cluster_allowed(p, cluster)) { + } else if (cluster_allowed(&env, cluster)) { env.need_waker_cluster = 1; bitmap_zero(env.candidate_list, NR_CPUS); __set_bit(cluster->id, env.candidate_list); @@ -3499,8 +3492,15 @@ static inline int migration_needed(struct task_struct *p, int cpu) nice = task_nice(p); rcu_read_lock(); grp = task_related_thread_group(p); + /* + * Don't assume higher capacity means higher power. If the task + * is running on the power efficient CPU, avoid migrating it + * to a lower capacity cluster. + */ if (!grp && (nice > SCHED_UPMIGRATE_MIN_NICE || - upmigrate_discouraged(p)) && cpu_capacity(cpu) > min_capacity) { + upmigrate_discouraged(p)) && + cpu_capacity(cpu) > min_capacity && + cpu_max_power_cost(cpu) == max_power_cost) { rcu_read_unlock(); return DOWN_MIGRATION; } diff --git a/kernel/sched/hmp.c b/kernel/sched/hmp.c index 3acf96d1f1d6..744c60dfb4fb 100644 --- a/kernel/sched/hmp.c +++ b/kernel/sched/hmp.c @@ -455,6 +455,12 @@ compare_clusters(void *priv, struct list_head *a, struct list_head *b) cluster1 = container_of(a, struct sched_cluster, list); cluster2 = container_of(b, struct sched_cluster, list); + /* + * Don't assume higher capacity means higher power. If the + * power cost is same, sort the higher capacity cluster before + * the lower capacity cluster to start placing the tasks + * on the higher capacity cluster. + */ ret = cluster1->max_power_cost > cluster2->max_power_cost || (cluster1->max_power_cost == cluster2->max_power_cost && cluster1->max_possible_capacity < |
